Engee documentation
Notebook

Code generation for Arduino (PWM on finite automata)

In this example, we will develop a simple model in Engee for pulse width modulation on the output of Arduino-compatible boards using the Finite Automata library to build the algorithm and the C Function block to communicate with the peripherals of the target device.

Introduction

The purpose of this example is to show the process of developing a PWM contact control algorithm for Arduino-compatible platforms using the terminal automata library. In this example, unlike the demo example arduino_blink_chart, all possible states of the algorithm are reduced to a single state, and the output variables are changed in conditional transitions. This makes it possible to reduce the size of the compiled file loaded into the target device, even slightly. In addition, the example shows how to use the C Function block to access controller peripherals and functions of the Arduino development environment.

The hardware used in this example is the same as in the example arduino_blink_chart.

Model Description

In the Arduino board PWM pin control model, the Сhart block directly reproduces the control algorithm, its output pins cnt and out are used to output and store the corresponding variables. The C Function block is used to communicate with the controller periphery via functions used in the Arduino IDE. Block Outport ("Cnt") in the model is necessary for successful code generation.

image_2.png

The "in" input of the C Function block receives the value of the PWM pulse duration, where it is further transferred to the PWM pin (~13) of the Arduino board.

State diagram

The state diagram of the block Chart, as mentioned earlier, in this example is represented by a single state - Counter. As can be seen from the diagram below, this state does not reproduce any actions except for the checks of transition conditions. At each step of the model computation, the transition condition is checked in the specified order until one of them is fulfilled. When the condition is fulfilled, the condition body is executed and the transition to the initial state occurs, after which the cycle repeats.

image.png

The condition body assigns one of 6 possible values to the PWM pulse width variable out and increments the counter variable cnt. The condition for one of the transitions is that the counter variable is within one of the specified ranges. When the counter variable cnt reaches the maximum value "1000", the counter is reset (cnt = 0;) and the PWM pulse duration is set to "0".

. Thus, we get a step change of PWM pulse duration at the output of Out block Chart.

image.png

The initial values of the output variables are "0", as can be seen in the signal setting menu in the status diagram.

Block C Function

The C Function block is used in the considered model exclusively for code generation. Therefore, for successful compilation and verification of the Engee model, the user code in the C Function sections is enclosed in the conditional compilation directives #ifdef ... #endif. The compilation condition written in the code C Function is executed only when the header file Arduino.h is connected, which happens automatically when compiling from the Arduino IDE, and does not happen when compiling in the Engee modelling environment.

The StartCode section of the block is used to initialise the controller peripherals and will be called once in the Arduino IDE in the function setup().

image.png

The OutputCode section of the block is used to change the duration of the PWM pulse and output this value to the serial port. It will be called every 1 millisecond in the function loop().

image_3.png

A detailed description of each procedure is given in the block comments C Function.

Modelling results

Let's load the described model:

In [ ]:
if "pwm_chart" in [m.name for m in engee.get_all_models()]
    m = engee.open( "pwm_chart" );
else
    m = engee.load( "$(@__DIR__)/pwm_chart.engee" );
end

data = engee.run(m);

From the obtained model data, plot the change in counter value and PWM pulse width:

In [ ]:
using Plots
plotlyjs();
plot(data["Chart.cnt"].time, data["Chart.cnt"].value,
    label="Cnt", size=(900,300), lw=2)
plot!(data["Chart.out"].time, data["Chart.out"].value,
    label="Out", size=(900,300), lw=2)
xlims!(0.0,3.0)
Out[0]:

As can be seen from the simulation results, the received counter signal Cnt increases in increments of "1" every 1 millisecond from "0" to "1000" for 1 second. The PWM pulse width signal Out increases stepwise with values depending on the value of the counter value: "51", "102", "153", "204", "255".

Now, having made sure that the algorithm and the model work correctly, we can proceed to code generation and reproduce the work of the model on the target device.

Uploading the code to Arduino

To transfer the developed model to the target device, let's generate C code:

In [ ]:
engee.generate_code( "$(@__DIR__)/pwm_chart.engee",
                     "$(@__DIR__)/pwm_chart_code" )
Out[0]:
"Created directory - /user/start/examples/codegen/arduino_pwm_chart/pwm_chart_code"

Plug-in files were generated in the specified directory pwm_chart_code. Also in the directory of the demo example arduino_pwm_chart there is a pre-written Arduino sketch with the name of this directory arduino_pwm_chart.ino. In it, the header file obtained during code generation is connected, the model calculation time variables are initialised and called, and the model calculation functions are called. At the same time, the model calculation functions are now used not only to calculate the control algorithm, but also to control the controller periphery. A detailed description of the sketch is given in the comments of its code.

To execute the code on Arduino, you need to download the arduino_pwm_chart directory and upload the arduino_pwm_chart.ino sketch from Arduino IDE to the target device. In our case, as stated earlier, it is Amperka's Iskra Neo.

After successful compilation and downloading the executable code to the target device, the Arduino IDE diagnostic window displays a message about the success of the operation and the size of the output file:

image.png

Code execution on Arduino

Since in the C Function block we used the PWM period duration value not only to control the PWM channel but also to output the calculated value to the serial port of the computer, let's go to the Arduino IDE tools and run the connection plotter over the serial connection. The connection plotter will output the change in the variable out at each calculation step.

.gif

image.png

As can be seen from the plotter graph, the period of change of the PWM pulse duration is 200 milliseconds, and the values of the durations have the previously indicated values. In addition, on the Arduino board it is possible to observe a stepwise, with a period of 1 second gradual increase in the brightness of the built-in LED, since both the LED and pin 13 of the board are connected to the same channel of the controller.

Capturing signals from the Arduino board

To complete the testing of the model on the target device, we will oscillate the required signal from pin 13 of the Arduino board.

imag098.png

The presented oscillogram shows a gradual increase in the PWM pulse duration with a duration change period of ~200 milliseconds and a total duration of the duration increase cycle of 1 second.

Conclusion

In this demo, a model of a PWM control algorithm for Arduino-compatible platforms based on a library of finite automata has been developed. Also a separate attention was paid to the consideration of working with the periphery of the target device by means of the block C Function. As it was established by the data received from the microcontroller and oscillograms taken from the debug board, the C-code generated from the Engee model exactly reproduces the algorithm and corresponds to the simulation results.

Blocks used in example