Engee documentation
Notebook

Code generation for Milander 1986BE91T (Running lights)

Introduction

In this example, code generation from the Engee model for the 1986BE91T microcontroller from PKK Milandr a.s. is considered. The model reproduces the operation of "running lights" on the microcontroller debug board, the project is built in the Keil μVision environment, and the executable code is loaded via the J-Link debugger.

Hardware part

The target device is a microcontroller from PKK Milandr, a.s. 1986VE91T (MDR32F9Q1). The example uses debug module for microchips 1986VE91T, 1986VE94T (version 5):

plata.jpg

The example tests the operation of the digital outputs PORT_Pin_10 - PORT_Pin_14 of the PORTD port of the microcontroller, which are connected to the LEDs VD5 - VD9 on the debug board. To test the operation we will reproduce "running light" - sequential switching on and off of LEDs. The ON time of each LED is set to 100 ms.

Model description

The example model is mdr32f9q1_running_lights.engee. The duration of the high level signal output to the digital outputs is set in the model by the block period_msec.

model_2.jpg

The Chart block reproduces the algorithm of changing the output LED number in order with the set period. Block Demultiplexer outputs a one to the corresponding output port by the number of the output LED. The finite automaton realised by the Chart block includes five states that are cyclically activated in turn.

chart.jpg

To transition between states of a finite automaton, temporal logic is used.

Peripheral blocks

Blocks PORTD_CONFIG and PORT1 - PORT5 add C code to the model to work with the controller periphery. In order to add lines to the generated code with connection of necessary header files, the names and path to the header files to be connected are specified in the Build options tab of the PORTD_CONFIG block. The files themselves do not contain code and are not used during modelling. When building the project, the plug-in files will be added from the support package for the microcontroller.

cfunc_options.jpg

Block PORTD_CONFIG adds to the code generated from the model with functions to initialise the ports used in the project, and blocks PORT1 - PORT5 add code with functions to set and reset the active state of the corresponding digital pins.
The contents of code cells of peripheral blocks are "wrapped" in conditional preprocessor directives:

#if defined ( USE_MDR1986VE9x )
    // user code
#endif

This allows code from blocks to be executed only on specific target devices, ignoring it when modelling in Engee, for example.

Modelling results

Let's load and run the example model:

In [ ]:
# @markdown **Программное управление моделированием:**  
# @markdown Требуется ввести только имя модели
имя_модели = "mdr32f9q1_running_lights" # @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 output variables. Scale the single pulse signals of the channels for clarity:

In [ ]:
gr(size = (900,400))
plot(данные["channel"].time, [данные["channel"].value, 1.05.*данные["vd5"].value,
     1.1.*данные["vd6"].value, 1.15.*данные["vd7"].value, 1.2.*данные["vd8"].value, 1.25.*данные["vd9"].value,];
     label=[:none "vd5" "vd6" "vd7" "vd8" "vd9"], title="Бегущие огни", st = :step)
Out[0]:

As can be seen from the graphs, the finite automaton and the demultiplexer work the given algorithm, forming sequentially pulses to five channels with a given duration.

Code generation

Let's generate the code from the developed model.

In [ ]:
# @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

# @markdown Генерировать `main.c`?
использовать = false # @param {type:"boolean"}

if (!использовать)
    cd("$(@__DIR__)/"*папка)
    rm("main.c")
end
[ Info: Generated code and artifacts: /user/start/examples/codegen/mdr32f9q1_running_lights/code

Since the ready main.c will be used in the project, the code cell above removes the template main.c generated from the model to eliminate confusion. The ready main.c and the files generated from the model must now be added to the Keil μVision development environment project.

Project composition

To work with the microcontroller in the development environment, a support package must be installed. For this example we used the unofficial support package.

The following are the standard steps for working in Keil μVision. - Create a project, add source files for the required peripherals and header files for the device configuration, and configure the builder and debugger. After that we download the example files from the folder \code, as well as main.c and add them to the Keil project.

project.jpg

Then you can move on to building the project and loading/debugging the code.

Building the project and loading the code

Let's build the Keil project, after which a similar message should be displayed in the environment terminal:

Build started: Project: new_project_1
*** Using Compiler 'V6.22', folder: 'C:\Users\engeeuser\AppData\Local\Keil_v5\ARM\ARMCLANG\Bin'.
Build target 'Target_1'
compiling main.c...
mdr32f9q1_running_lights.c(49): warning: variable 'seq' is uninitialised when used here [-Wuninitialized]
   49 | cfunc_symbols->seq = seq;
      | ^~~
mdr32f9q1_running_lights.c(44): note: initialise the variable 'seq' to silence this warning
   44 | int8_t seq;
      | ^
      | = '\0'
mdr32f9q1_running_lights.c(291): warning: comparison of integers of different signs: 'int64_t' (aka 'long long long') and 'uint64_t' (aka 'unsigned long long') [-Wsign-compare]
  291 | return counter * numerator >= (uint64_t)ceil(condition * denominator);
      | ~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mdr32f9q1_running_lights.c(338): warning: unused variable 'seq' [-Wunused-variable]
  338 | double seq = PORTD_CONFIG_cfunc_symbols.seq;
      | ^~~
mdr32f9q1_running_lights.c(351): warning: variable 'Chart_selector' set but not used [-Wunused-but-set-variable]
  351 | int Chart_selector;
      | ^
4 warnings generated.
compiling mdr32f9q1_running_lights.c...
linking...
Programme Size: Code=5264 RO-data=224 RW-data=16 ZI-data=1672  
".\Objects\new_project_1.axf" - 0 Error(s), 4 Warning(s).
Build Time Elapsed: 00:00:00

The assembly went without errors, let's move on to loading the code into the microcontroller. Let's connect the board power supply and JTAG debugger. In this example it is SEGGER J-link.

devboard_4.png

If the executable code is successfully loaded into the microcontroller, a similar message will be displayed on the command line of the environment:

Load "D:\\HARDWARE\Milandr\\new_project_1\Objects\\\new_project_1.axf" 
* JLink Info: Device "CORTEX-M3" selected.
Set JLink Project File to "D:\HARDWARE\Milandr\new_project_1\JLinkSettings.ini"
* JLink Info: Device "CORTEX-M3" selected.
 
JLink info:
------------
DLL: V8.12a, compiled Jan 9 2025 14:34:24
Firmware: J-Link ARM V8 compiled Nov 28 2014 13:44:46
Hardware: V8.00
Feature(s) : RDI,FlashDL,FlashBP,JFlash,GDB 
 
* JLink Info: Found SW-DP with ID 0x2BA01477
* JLink Info: DPv0 detected
* JLink Info: CoreSight SoC-400 or earlier
* JLink Info: Scanning AP map to find all available APs
* JLink Info: AP[1]: Stopped AP scan as end of AP map has been reached
* JLink Info: AP[0]: AHB-AP (IDR: 0x24770011, ADDR: 0x00000000)
* JLink Info: Iterating through AP map to find AHB-AP to use
* JLink Info: AP[0]: Core found
* JLink Info: AP[0]: AHB-AP ROM base: 0xE00FF000
* JLink Info: CPUID register: 0x412FC230. Implementer code: 0x41 (ARM)
* JLink Info: Found Cortex-M3 r2p0, Little endian.
* JLink Info: FPUnit: 6 code (BP) slots and 2 literal slots
* JLink Info: CoreSight components:
* JLink Info: ROMTbl[0] @ E00FF000
* JLink Info: [0][0]: E000E000 CID B105E00D PID 002BB000 SCS
* JLink Info: [0][1]: E0001000 CID B105E00D PID 002BB002 DWT
* JLink Info: [0][2]: E0002000 CID B105E00D PID 002BB003 FPB
* JLink Info: [0][3]: E0000000 CID B105E00D PID 002BB001 ITM
* JLink Info: [0][4]: E0040000 CID B105900D PID 002BB923 TPIU-Lite
ROMTableAddr = 0xE00FF000
* JLink Info: Reset type: NORMAL (https://wiki.segger.com/J-Link_Reset_Strategies)
* JLink Info: Reset: Halt core after reset via DEMCR.VC_CORERESET.
* JLink Info: Reset: Reset device via AIRCR.SYSRESETREQ.
 
Target info:
------------
Device: MDR1986BE91
VTarget = 3.319V
State of Pins: 
TCK: 1, TDI: 1, TDO: 0, TMS: 1, TRES: 1, TRST: 1
Hardware-Breakpoints: 6
Software-Breakpoints: 8192
Watchpoints: 4
JTAG speed: 2000 kHz
 
* JLink Info: Memory map 'after startup completion point' is active
Full Chip Erase Done.
Programming Done.
Verify OK.
* JLink Info: Memory map 'before startup completion point' is active
* JLink Info: Reset type: NORMAL (https://wiki.segger.com/J-Link_Reset_Strategies)
* JLink Info: Reset: Halt core after reset via DEMCR.VC_CORERESET.
* JLink Info: Reset: Reset device via AIRCR.SYSRESETREQ.
* JLink Info: Memory map 'after startup completion point' is active
Application running ...
Flash Load finished at 10:40:49

Executing code on a microcontroller

After downloading, the code is automatically run on the controller, which can be verified by the running lights on the board:

running_light_4.gif

The loaded code executes the algorithm given by the Engee model.

Conclusion

In this example, we developed an Engee model that reproduces the "running lights" algorithm using Engee finite automata library and temporal operators, generated code from the model, and verified its operation on a Milandr 1986BE91T microcontroller by assembling the project and loading it into the device from the Keil μVision environment.

Blocks used in example