Code generation for Arduino (PWM signal generation)¶
In this example we will show you how to generate programme code in Engee to generate a PWM signal on the Arduino platform.
Introduction¶
There are many ways to create a pulse width modulated (PWM) signal. We will implement a model from basic components, as similar as possible to the demo example arduino_blink
. That is, we are going to control the time delay between the on and off events of the LED.
Other ways that can be used:
- Compare a smoothly varying analogue signal with a sawtooth signal and switch the diode at the moment of intersection
- Use one of the inbuilt functions of Arduino
Platform preparation¶
For this example we need an Arduino-compatible platform (Uno, Leonardo, Iskra, etc.) and a suitable USB cable. You will also need to install the Arduino IDE on your computer, find and install additional drivers (if necessary) and connect the existing board via USB.
Model Description¶
In this example, we will generate code from the model pwm.engee
.
The interface of the model is the same as in the example arduino_blink
. At each step of the computation it:
- switches the state of the LED using the output of
out_LED_BUILTIN
(initially equal to 1, changes to "opposite" with each cycle), - returns the time delay value to Arduino using the output
param_WAIT_MS
(value in milliseconds).
The RMS will change in a sine wave with the frequency set in the block Sine
. The maximum value is set as amplitude in the same block, and the offset set there allows the delay value to never become less than 0, which would cause an error on the Arduino platform and stop the programme execution.
It is with the help of the value param_WAIT_MS
that we will control the pulse width, switching on and off the "on-board" LED of the Arduino platform with pulses of different lengths.
PWM-signal is characterised by the fact that during a fixed period $T_d$ it changes state 1
and state 0
, the duration of which in total is equal to $T_d$, but the ratio of which is changed using the parameter $w$ (pulse width).
.
We will use this signal to simulate the smooth control of LED brightness on the Arduino platform.
Data type conversion¶
For the most part, the Engee model blocks are directly reflected in the code for Arduino. Not all platforms use the same C/C++ language standards. Naturally, you always have to think about low-level implementation details when creating code.
In this example, the problem we overcome in Engee is automatic type conversion. On the Arduino platform:
- When adding a boolean data type to a numeric data type, the output is boolean
- When adding a double data type to an unsigned integer, the output is an unsigned integer (the sine wave does not work correctly unless a special data type is specified).
Where do problems with data types occur?
Therefore, we need to envisage how the operation of data types will manifest itself in C code, and configure the model blocks (marked with green ticks) accordingly:
- The
Constant
block should return a data typeFloat32
- The
Compare To Zero
block should return notbool
, but ratheruint8
All these settings are set in the corresponding blocks. Sometimes these problems can be solved by using Data Type Conversion
blocks, such conversions are also passed into the generated code.
Model testing¶
The process of semi-natural modelling
involves running the model in a simulated environment for debugging and optimisation with subsequent control of the same algorithm on a hardware platform.
In our case it is worth considering that Arduino runs on its own clock frequency, and different commands can be executed for different number of clock cycles. In order to rely on real physical time, it is better to create a template for code generation based on a real-time system.
Nevertheless, we can already show how our solution will work.
if "pwm" in [m.name for m in engee.get_all_models()]
m = engee.open( "pwm" );
else
m = engee.load( "$(@__DIR__)/pwm.engee" );
end
data = engee.run(m);
Let's build a graph of LED switching delay time (parameter that is passed to delay()
).
using Plots
plot( data["param_WAIT_MS"].time, data["param_WAIT_MS"].value,
label="param_WAIT_MS", st=:step, size=(900,300))
# Подкрасим переменное время задержки (LED вкл/выкл)
plot!( data["param_WAIT_MS"].time, [iseven(i) ? d : NaN for (i,d) in enumerate(data["param_WAIT_MS"].value)],
label="задержка при выключенном LED", st=:step, size=(900,300), c=:black, lw=2)
plot!( data["param_WAIT_MS"].time, [isodd(i) ? d : NaN for (i,d) in enumerate(data["param_WAIT_MS"].value)],
label="задержка при включенном LED", st=:step, size=(900,300), c=:red, lw=2)
What do we see here? The time delay param_WAIT_MS
varies in time in such a way that every two neighbouring values always add up to about 10 milliseconds. The law to modulate this process is given by a sinusoid.
If we combine the two output signals correctly, we will see exactly the PWM signal that controls the switching on and off of the current supplied to the output to which the LED is connected.
plot( cumsum(data["param_WAIT_MS"].value),
data["out_LED_BUILTIN"].value,
st=:step, size=(900,200))
This model is not set up to simulate real operation time, so you may notice that the model time (2 seconds - simulation duration) is not equal to the CPU time (according to the graph it is about 1 second, and that with large tolerances). To provide this property, you need to build the model a little differently.
Code generation¶
In this demonstration, we propose to generate code using the following command:
engee.generate_code( "$(@__DIR__)/pwm.engee",
"$(@__DIR__)/sketch_pwm_custom/pwm_code" )
As a result, in the directory sketch_pwm_custom
we will find the folder pwm_code
, in which we will find .c
and .h
files with the code of our model.
Project files¶
There is only one model in our project. From the generated files we need only files with the code of this model: source code pwm.c
and header file pwm.h
.
It is worth paying attention to the structure of files and directories of the project:
- file
sketch_pwm_custom.ino
contains description of the Model-Arduino interface, - the
sketch_pwm_custom
level directory must be named the same as the*.ino
file (Arduino IDE requirements), - the directory
pwm_code
contains files with generated code, which we will include in the file*.ino
.
Transferring the model to Arduino¶
To transfer a project to Arduino, the standard steps are as follows:
- Download the catalogue
sketch_pwm_custom
from a file browser, unzip the archive - open the file
sketch_pwm_custom.ino
and click on the buttonUpload
The result should be as follows (LED flickers smoothly):
Conclusion¶
We have created a slightly more complex model than a blinking LED. This way of creating a PWM signal will allow you to control simple machines, such as LEDs or servo motors.
We had to consider how data type casting works on the target platform. Nevertheless, we have seen that the Engee platform makes it possible to model components that can be almost seamlessly run on a hardware platform through code generation.