Документация Engee
Notebook

Генерация кода для Миландр 1986ВЕ91Т (Бегущие огни)

Введение

В этом примере рассмотрена генерация кода из модели Engee для микроконтроллера 1986ВЕ91Т от АО "ПКК Миландр". Модель воспроизводит работу "бегущих огней" на отладочной плате микроконтроллера, сборка проекта осуществляется в среде Keil μVision, а загрузка исполняемого кода - через отладчик J-Link.

Аппаратная часть

Целевое устройство - микроконтроллер от АО "ПКК Миландр" 1986ВЕ91Т (MDR32F9Q1). В примере используется отладочный модуль для микросхем 1986ВЕ91Т, 1986ВЕ94Т (версия 5):

plata.jpg

В примере тестируется работа цифровых выходов PORT_Pin_10 - PORT_Pin_14 порта PORTD микроконтроллера, которые на отладочной плате соединены со светодиодами VD5 - VD9. Для тестирования работы воспроизведём "бегущий огонь" - последовательное включение и выключение светодиодов. Время включения каждого из светодиодов зададим равным 100 мс.

Описание модели

Модель примера - mdr32f9q1_running_lights.engee. Длительность выдачи высокого уровня сигнала на цифровые выходы устанавливается в модели блоком period_msec.

model_2.jpg

Блок Chart воcпроизводит алгоритм изменения с заданным периодом номера выходного светодиода по порядку. Блок Demultiplexer по номеру выходного светодиода выдаёт на соответствующий выходной порт единицу. Конечный автомат, реализуемый блоком Chart, включает в себя пять состояний, циклически поочередно активируемых.

chart.jpg

Для перехода между состояниями конечного автомата используется темпоральная логика.

Блоки периферии

Блоки PORTD_CONFIG и PORT1 - PORT5 добавляют в модель код Си для работы с периферией контроллера. Для того чтобы добавить в сгенерированный код строки с подключением необходимых заголовочных файлов, во вкладке Build options блока PORTD_CONFIG прописаны имена и путь к подключаемых файлов заголовков. Сами файлы не содержат код и не используются при моделировании. При сборке же проекта подключаемые файлы будут добавлены из пакета поддержки для микроконтроллера.

cfunc_options.jpg

Блок PORTD_CONFIG добавляет в сгенерированный из модели код с функциями для инициализации используемых в проекте портов, а блоки PORT1 - PORT5 - код с функциями для установки и сброса активного состояния соответствующих цифровых пинов.
Содержимое кодовых ячеек периферийных блоков "обёрнуто" в условные директивы препроцессора:

#if defined ( USE_MDR1986VE9x )
    // пользовательский код
#endif

Это позволяет выполнять код из блоков только на определенных целевых устройствах, игнорируя его, например, при моделировании в Engee.

Результаты моделирования

Загрузим и выполним модель примера:

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(модель);

Построим графики выходных переменных. Сигналы единичных импульсов по каналам отмасштабируем для наглядности:

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]:

Как видно из графиков, конечный автомат и демультиплексор отрабатывают заданный алгоритм, формируя последовательно импульсы на пять каналов с заданной длительностью.

Генерация кода

Сгенерируем код из разработанной модели.

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

Так как в проекте будет использоваться готовый main.c, для устранения путаницы кодовая ячейка выше удаляет сгенерированный из модели шаблон main.c. Готовый main.c и сгенерированные из модели файлы теперь необходимо добавить в проект среды разработки Keil μVision.

Состав проекта

Для работы с микроконтроллером в среде разработке необходимо установить пакет поддержки. Для этого примера мы использовали неофициальный пакет поддержки.

Далее следуют стандартные шаги при работе в Keil μVision. - создание проекта, добавление файлов источников для необходимой периферии и файлов заголовков для конфигурации устройства, а также настройка сборщика и дебаггера. После этого скачаем файлы примера из папки \code, а также main.c и добавим их в проект Keil.

project.jpg

Далее можно перейти к сборке проекта и загрузке/отладке кода.

Сборка проекта и загрузка кода

Соберём проект Keil, после чего в терминале среды должно быть выведено аналогичное сообщение:

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 uninitialized when used here [-Wuninitialized]
   49 |         cfunc_symbols->seq = seq;
      |                              ^~~
mdr32f9q1_running_lights.c(44): note: initialize 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') 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...
Program 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

Сборка прошла без ошибок, перейдём к загрузке кода в микроконтроллер. Подключим питание платы и JTAG-отладчик. В данном примере - это SEGGER J-link.

devboard_4.png

В случае успешной загрузки исполняемого кода в микроконтроллер в командной строке среды будет выведено аналогичное сообщение:

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

Выполнение кода на микроконтроллере

После загрузки код автоматически запускается на контроллере, в чём можно убедиться по бегущим огням на плате:

running_light_4.gif

Загруженный код выполняет заданный моделью Engee алгоритм.

Вывод

В этом примере мы разработали модель Engee, воспроизводящую алгоритм "бегущих огней" с использованием библиотеки конечных автоматов Engee и темпоральных операторов, сгенерировали код из модели и проверили его работу на микроконтроллере Миландр 1986ВЕ91Т, собрав проект и загрузив его в устройство из среды Keil μVision.

Блоки, использованные в примере