Генерация кода для Arduino (ШИМ на конечных автоматах)¶
В этом примере мы разработаем простейшую модель в Engee для широтно-импульсной модуляции на выходе Arduino-совместимых плат с использованием библиотеки Конечных автоматов для построения алгоритма и блока C Function
для взаимодействия с периферией целевого устройства.
Введение¶
Цель этого примера - показать процесс разработки алгоритма управления контактами ШИМ для Arduino-совместимых платформ с использованием библиотеки конечных автоматов. В этом примере, в отличие от демонстрационного примера arduino_blink_chart
, все возможные состояния алгоритма сведены к единственному, а изменение выходных переменных происходит в условных переходах. Это дает возможность пусть незначительно, но уменьшить объем загружаемого в целевое устройство скомпилированного файла. Кроме того, пример показывает, как при помощи блока C Function
обращаться к периферии контроллера и функциям среды разработки Arduino.
Аппаратное обеспечение в этом примере используется то же, что и в примере arduino_blink_chart
.
Описание модели¶
В модели управления контактом ШИМ платы Arduino блок Сhart
непосредственно воспроизводит управляющий алгоритм, его выходные контакты cnt
и out
используются для вывода и сохранения соответствующих переменных. Блок C Function
используется для взаимодействия с периферией контроллера через функции, используемые в Arduino IDE. Блок Outport
("Cnt") в модели необходим для успешной кодогенерации.
На вход "in" блока C Function
поступает величина длительности импульса ШИМ, где далее она передается на контакт ШИМ (~13) платы Arduino.
Диаграмма состояний¶
Диаграмма состояний блока Chart
, как и упоминалось ранее, в данном примере представлена одним состоянием - Counter
. Как видно из диаграммы ниже, это состояние не воспроизводит никаких действий, кроме проверок условий переходов. На каждом шаге расчета модели происходит проверка условия перехода по указанному порядку, пока не выполнится одно из них. При выполнении условия исполняется тело условия и происходит переход в начальное состояние, после чего цикл повторяется.
В теле условия выполняется присваивание одного из 6 возможных значений переменной out
длительности импульса ШИМ и инкрементируется переменная счетчика cnt
. Условие для выполнения одного из переходов - нахождение переменной счетчика в одном из указанных диапазонов. При достижении переменной cnt
максимального значения "1000", счетчик сбрасывается (cnt = 0;
), длительность импульса ШИМ устанавливается равной "0".
Таким образом, мы получаем на выходе Out
блока Chart
ступенчатое изменение длительности импульса ШИМ.
Начальные значения выходных переменных равны "0", как это видно из меню настройки сигналов в диаграмме состояний.
Блок C Function
¶
Блок C Function
используется в рассматриваемой модели исключительно для кодогенерации. Поэтому для успешной компиляции и проверки работы модели Engee пользовательский код в секциях C Function
заключен в директивы условной компиляции #ifdef ... #endif
. Прописанное в коде C Function
условие компиляции выполняется только при подключении заголовочного файла Arduino.h
, что происходит автоматически при компиляции из Arduino IDE, и не происходит при компиляции в среде моделирования Engee.
Секция StartCode
блока используется для инициализации периферии контроллера и будет вызвана в Arduino IDE один раз в функции setup()
.
Секция OutputCode
блока используется для изменения длительности импульса ШИМ и вывода этого значения в последовательный порт. Она будет вызываться каждую 1 миллисекунду в функции loop()
.
Подробное описание каждой процедуры дано в комментариях блока C Function
.
Результаты моделирования¶
Загрузим описанную модель:
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);
Из полученных данных модели построим графики изменения значения счетчика и длительности импульса ШИМ:
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)
Как видно из результатов моделирования, полученный сигнал счетчика Cnt
увеличивается с шагом "1" каждую 1 миллисекунду от "0" до "1000" в течение 1 секунды. Сигнал длительности импульса ШИМ Out
в зависимости от величины значения счетчика ступенчато возрастает со значениями: "51", "102", "153", "204", "255".
Теперь, убедившись в корректной работе алгоритма и модели, можно перейти к кодогенерации и воспроизведению работы модели на целевом устройстве.
Загрузка кода в Arduino¶
Для переноса разработанной модели на целевое устройство сгенерируем Си-код:
engee.generate_code( "$(@__DIR__)/pwm_chart.engee",
"$(@__DIR__)/pwm_chart_code" )
В указанной директории pwm_chart_code
сгенерировались подключаемые файлы. Также в директории демонстрационного примера arduino_pwm_chart
выложен заранее написанный скетч Arduino с именем этой директории arduino_pwm_chart.ino
. В нем подключается заголовочный файл, полученный при кодогенерации, инициализируются и вызываются переменные времени расчета модели, а также вызываются функции расчета модели. При этом, функции расчета модели теперь используются не только для расчета управляющего алгоритма, но и для управления периферией контроллера. Подробное описание скетча дано в комментариях его кода.
Для выполнения кода на Arduino необходимо скачать директорию arduino_pwm_chart
и загрузить скетч arduino_pwm_chart.ino
из Arduino IDE в целевое устройство. В нашем случае, как говорилось ранее, это - Iskra Neo от Amperka.
После успешной компиляции и загрузки исполняемого кода на целевое устройство в окно диагностики Arduino IDE выводится сообщение об успешности операции и размере выходного файла:
Выполнение кода на Arduino¶
Так как в блоке C Function
мы использовали значение длительности периода ШИМ не только для управления каналом ШИМ, но и для вывода рассчитанного значения в последовательный порт компьютера, перейдем в инструменты Arduino IDE и запустим плоттер по последовательному соединению. На плоттер соединения будет выводиться изменение переменной out
на каждом шагу расчета.