Code generation for MIC32 (Fuzzy controller testing)
This demo presents the development of the Engee model for testing a fuzzy controller over the full range of input signals with output to the DAC of a MIK32V2 microcontroller.
Introduction
The purpose of the example is to test the operation of a fuzzy regulator with two inputs and one output. The fuzzy regulator model is built from the individual blocks of the Engee basic library for easy familiarisation with the functionality of the regulator. The target device used in this demo is a MIK32 NUKE V0.3 debug board based on K1948VK018 MIK32 Amur microcontroller. Compilation and loading of the code into the microcontroller is done from VS Code with PlatformIO extension.
Model description
The general view of the model is shown in the figure below.

It consists of two input assignment blocks, test signals, a fuzzy controller subsystem with two inputs and one output, an output scaling subsystem, and MIC32 peripheral blocks of the HAL library.
Controller inputs and outputs
Repeating variable binary signals from the [Repeating Sequence Stair] units (https://engee.com/helpcenter/stable/ru/base-lib-sources/repeating-sequence-stair.html) are input to the controller inputs:
# Входные сигналы:
x1_data = Vector(range(-2,2,105))
x2_data = Vector(range(-2,2,21))
# Шаг дискретизации:
Ts = 0.01
using Plots
gr()
plot([x1_data,x2_data]; label=["x1_data" "x2_data"],
st=:step, title = "Входные сигналы", aspect_ratio=10)
This setting of the input signals makes it possible to check the results of the controller output calculations over the entire value range of one and the other signal.
The work of the subsystem scaling
consists in changing the range of values and data type of the regulator output signal: :

The signal is limited from 0 to 4095 because the MIC32 DACs are 12-bit, the data format is integer, unsigned, 2 bytes.
Structure of the fuzzy controller
The fuzzy controller is based on Mamdani's algorithm and consists of identical phasification and aggregation blocks, activation block (min function), accumulation block (max function), defuzzification block (centre of gravity method for one-point sets, COGS).

The number of linguistic terms of the input signals is 3 and the output signal is 3.
Phasification and aggregation
The input variables have three linguistic terms taking non-zero truth values in the ranges - "LOW": , "MEDIUM": , "HIGH": . At the aggregation stage, triangular membership functions (FPs) symmetric with respect to zero are applied to the input signals. In the fuzzyfication
subsystems, the FPs are reproduced by 1-D Lookup Table blocks. The calculation of TPs for the input variables terms is given below.
# ФП для x1
μ_S_x1 = -x1_data;
μ_M_x1 = 1.0.-abs.(x1_data);
μ_L_x1 = copy(x1_data);
μ_x1 = [μ_S_x1, μ_M_x1, μ_L_x1];
# ФП для x2
μ_S_x2 = -x2_data;
μ_M_x2 = 1.0.-abs.(x2_data);
μ_L_x2 = copy(x2_data);
μ_x2 = [μ_S_x2, μ_M_x2, μ_L_x2];
термы = ["низкое" "среднее" "высокое"];
Saturation function of input signals:
function насыщение(μ)
for i in 1:length(μ)
if μ[i] > 1
μ[i] = 1.0
end
if μ[i] < 0
μ[i] = 0.0
end
end
end;
Application of saturation to terms, construction of membership functions:
for i in 1:3
насыщение(μ_x1[i])
end
for i in 1:3
насыщение(μ_x2[i])
end
график1 = plot(x1_data, μ_x1; title = "ФП для x1", label = термы, xlabel = "x1")
график2 = plot(x2_data, μ_x2; title = "ФП для x2", label = :none, xlabel = "x2")
plot(график1, график2;
ylabel = "Степень принадлежености, μ", legend = :right, aspect_ratio=3)
Activation
In the activation step, subconclusions for all combinations of terms are defined in the subsystem activation
. Activation is performed by the minimum function:
Accumulation
The following rule base is reproduced in the accumulation
subsystem:
- IF ( "LOW" AND ( "LOW" OR "MEDIUM")) THEN "LOW".
- IF ( "LOW" AND "HIGH") OR
( "MEDIUM" AND ( "LOW" OR "MEDIUM") )) OR
( "HIGH" AND "LOW") TO "MEDIUM". - ** IF** ( "MEDIUM" AND "HIGH") OR
( "HIGH" AND ( "MEDIUM" OR "HIGH")) TO "HIGH"
For clarity, let's present it in the form of a table:
колонки = ["низкое", "среднее", "высокое"];
строки = copy(колонки);
база_правил = [
"низкое" "среднее" "среднее";
"низкое" "среднее" "высокое";
"среднее" "высокое" "высокое"
];
(n,m) = size(база_правил)
heatmap(база_правил, fc = cgrad([:blue; :red; :green]), leg=false, xticks=(1:3,колонки), yticks=(1:3,строки))
title!("База правил")
xlabel!("Термы x1")
ylabel!("Термы x2")
annotate!( [(j, i, база_правил[i,j]) for i in 1:n for j in 1:m])
vline!(0.5:(n+0.5), c=:black)
hline!(0.5:(m+0.5), c=:black)
The degrees of truth of the output terms are defined in the maximum function of the subconclusions according to the expressions:
Defuzzification
The subsystem defuzzyfication
implements the centre of gravity method for one-point sets (COGS). The midpoints for the membership functions of the output terms are: . The calculation is realised without and with ( ) and with ( ). For modelling and testing we use the first method.

Modelling results
Let's load and run the example model:
# @markdown **Программное управление моделированием:**
# @markdown Требуется ввести только имя модели
имя_модели = "mik32_fuzzy_reg" # @param {type:"string"}
if имя_модели in [m.name for m in engee.get_all_models()]
модель = engee.open( имя_модели );
else
модель = engee.load( "$(@__DIR__)/"*имя_модели*".engee" );
end
данные = engee.run(модель);
Plot the graphs of the output variable
plot(данные["dac_val"].time, данные["dac_val"].value;
label="y", title="Выходной сигнал", xlims=(0,1.1))
This signal in the program loaded into the controller and will be sent to the digital to analogue converter.
The periphery of the controller in the model is realised using C Function blocks, as it was realised in the early DAC examples for MIC32: sawtooth signal, Engee logo.
Code generation and project assembly
# @markdown **Генерация кода:**
# @markdown Папка для результатов генерации кода будет создана в папке скрипта:
папка = "code" # @param {type:"string"}
# @markdown Генерация кода для подсистемы:
включить = false # @param {type:"boolean"}
if(включить)
подсистема = "" # @param {type:"string"}
engee.generate_code( "$(@__DIR__)/"*имя_модели*".engee", "$(@__DIR__)/"*папка;
subsystem_name = подсистема)
else
engee.generate_code( "$(@__DIR__)/"*имя_модели*".engee", "$(@__DIR__)/"*папка)
end
The project with the model is built in IDE VS Code+PlatformIO, the process of building is similar to the process from the example MIC32: Sawtooth Signal Generator.
The only exception is that the compiled programme is loaded not into the RAM of the controller, but into flash-memory via SPIFI interface. To configure PlatformIO in the example, the configuration file platformio.ini
is attached. Let's move on to the execution of the code on the microcontroller.
Code execution on MIK 32
After successful assembly and compilation of the project, connect an oscilloscope to channel P1.12 of the microcontroller and display the oscillogram.

As can be seen from the oscillogram, the DAC channel reproduces the shape of the modelled output signal of the fuzzy controller under test.
Conclusion
In this example we have considered the structure of the fuzzy controller in the Engee model, simulated its testing and tested the controller on the MIC32 Amur microcontroller after code generation from the model. The results of the modelling and testing on the controller are identical and consistent with the specified algorithm.