Генерация кода для 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" )
"Created directory - /user/start/examples/codegen/arduino_pwm_chart/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
на каждом шагу расчета.
Как видно из графика, представленного на плоттере, период изменения длительности импульса ШИМ составляет 200 миллисекунд, а значения длительностей имеют ранее указанные значения. Кроме этого, на плате Arduino можно наблюдать ступенчатое, с периодом в 1 секунду постепенное увеличение яркости встроенного светодиода, так как и светодиод, и контакт 13 платы подключены к одному каналу контроллера.
Снятие сигналов с платы Arduino
В завершение тестирования работы модели на целевом устройстве проведем осциллографирование необходимого сигнала с контакта 13 платы Arduino.
На представленной осциллограмме наблюдается постепенное увеличение длительности импульса ШИМ с периодом изменения длительности ~200 миллисекунд и общей длительностью цикла увеличения длительности 1 секунда.
Вывод
В рассмотренном демонстрационном примере была разработана модель алгоритма управления ШИМ для Arduino-совместимых платформ на основе библиотеки конечных автоматов. Также отдельное внимание уделено рассмотрению работы с периферией целевого устройства посредством блока C Function
. Как было установлено по полученным из микроконтроллера данным и снятым с отладочной платы осциллограммам, сгенерированный из модели Engee Си-код в точности воспроизводит заложенный алгоритм и соответствует результатам моделирования.