Генерация кода для Arduino (генерация ШИМ сигнала)¶
В этом примере мы покажем, как в Engee можно сгенерировать программный код для генерации ШИМ на платформе Arduino.
Введение¶
Создать широтно-импульсно модулированный сигнал (ШИМ) можно множеством способов. Мы реализуем модель из базовых компонентов, максимально похожую на демонстрационный пример arduino_blink
. То есть нам предстоит управлять временной задержкой между событиями включениям и выключения светодиода.
Другие способы, которым можно воспользоваться:
- Сравнивать плавно меняющийся аналоговый сигнал с пилообразным сигналом и переключать диод в момент пересечения
- Использовать одну из встроенных функций Arduino
Подготовка платформы¶
Для этого примера нам потребуется Arduino-совместимая платформа (Uno, Leonardo, Iskra и другие) и подходящий у ней шнур USB. Также нужно будет установить на ваш компьютер среду Arduino IDE, найти и поставить дополнительные драйверы (если необходимо) и подключить имеющуюся плату через порт USB.
Описание модели¶
В этом примере мы будем генерировать код из модели pwm.engee
.
Интерфейс модели совпадает с интерфейсом в примере arduino_blink
. На каждом шаге вычислений она:
- переключает состояние светодиода при помощи выхода
out_LED_BUILTIN
(изначально равно 1, с каждым циклом меняется на "противоположное"), - возвращает Arduino значение временной задержки при помощи выхода
param_WAIT_MS
(значение в миллисекундах).
Скважность будет изменяться по синусоиде с частотой, заданной в блоке Sine
. Максимальное значение задается в качестве амплитуды этого же блока, а задаваемое там же смещение позволяет значению задержки никогда не становиться меньше 0, что вызовет ошибку на платформе Arduino и остановит выполнение программы.
Именно при помощи значения param_WAIT_MS
мы будем управлять скважностью импульса, включая и выключая "бортовой" светодиод платформы Arduino импульсами разной длины.
ШИМ-сигнал характеризуется тем, что в течение фиксированного периода $T_d$ он сменяет состояние 1
и состояние 0
, длительность которых в сумме равна $T_d$, но отношение которых изменяется при помощи параметра $w$ (скважность).
Мы будем использовать этот сигнал для имитации плавного управления яркостью светодиода на платформе Arduino.
Приведение типов данных¶
В основном, блоки модели Engee находят прямое отражение в коде для Arduino. Не все платформы используют одинаковые стандарты языков С/С++. Естественно, при создании кода всегда приходится думать о низкоуровневых деталях реализации.
В этом примере проблемой, которую мы преодолеваем в Engee, является автоматическое приведение типов. На платформе Arduino:
- При сложении булевого типа данных с численным на выходе получается булевый
- При сложении типа данных double с беззнаковым целым числом на выходе получается беззнаковое целое число (работа синусоиды оказывается неверной, если не указать особый тип данных)
Где возникают проблемы с типами данных?
Поэтому нам нужно предусмотреть, как проявится работа типов данных в коде на Си, и соответствующим образом настроить блоки модели (отмеченные зелеными галочками):
- Блоки
Constant
должны возвращать тип данныхFloat32
- Блок
Compare To Zero
должен возвращать неbool
, аuint8
Все эти настройки выставляются в соответствующих блоках. Иногда эти проблемы можно решить при помощи блоков Data Type Conversion
, такие преобразования тоже переходят в сгенерироавнный код.
Тестирование модели¶
Процесс полунатурного моделирования
подразумевает запуск модели в симулированном окружении для отладки и оптимизации с последующим контролем работы того же алгоритма на аппаратной платформе.
В нашем случае стоит учитывать то, что Arduino работать на собственной тактовой частоте, и разные команды могут выполняться за разное количество тактов. Чтобы опираться на реальное физическое время, лучше создать шаблон для генерации кода на основе системы реального времени.
Тем не менее мы уже можем показать, как будет работать наше решение.
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);
Построим график времени задержки переключения светодиода (параметр, который передается в 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)
Что мы здесь видим? Временная задержка param_WAIT_MS
изменяется во времени таким образом, чтобы каждые два соседних значения в сумме всегда образовывали примерно 10 миллисекунд. Законом для модуляции этого процесса задается синусоидой.
Если правильно совместить оба выходных сигнала, то мы увидим именно тот ШИМ сигнал, который управляет включением и отключением тока, подаваемого на выход, к которому подключени светодиод.
plot( cumsum(data["param_WAIT_MS"].value),
data["out_LED_BUILTIN"].value,
st=:step, size=(900,200))
Эта модель не настроена таким образом, чтобы имитировать реальное время выполнения операций, поэтому вы можете заметить, что модельное время (2 секунды – длительность моделирования) не равно процессорному времени (по графику – примерно 1 секунда, и то с большими допушениями). Для обеспечения этого свойства нужно немного по-другому построить модель.
Генерация кода¶
В этой демонстрации мы предлагаем сгенерировать код при помощи следующей команды:
engee.generate_code( "$(@__DIR__)/pwm.engee",
"$(@__DIR__)/sketch_pwm_custom/pwm_code" )
В итоге, в каталоге sketch_pwm_custom
повяляется папка pwm_code
, в которой мы находим .c
и .h
файлы с кодом нашей модели.
Файлы проекта¶
В нашем проекте только одна модель. Из сгенерированных файлов нам нужны будут только файлы с кодом этой модели: исходный код pwm.c
и заголовочный файл pwm.h
.
Стоит обратить внимание на структуру файлов и каталогов проекта:
- файл
sketch_pwm_custom.ino
содержит описание интерфейса Модель-Arduino, - каталог уровня
sketch_pwm_custom
должен называться так же, как и файл*.ino
(требования Arduino IDE), - каталог
pwm_code
содержит файлы со сгенерированным кодом, которые мы будем включать в файл*.ino
.
Перенос модели на Arduino¶
Чтобы перенести проект на Arduino, стандартные шаги следующие:
- Скачать каталог
sketch_pwm_custom
из файлового браузера, распаковать архив - Открыть файл
sketch_pwm_custom.ino
и нажать на кнопкуUpload
Результат должен быть следующим (светодиод плавно мерцает):
Заключение¶
Мы создали чуть более сложную модель, чем мигающий светодиод. Такой способ создания ШИМ сигнала позволит управлять простой техникой, например светодиодами или сервомоторами.
Нам пришлось учесть, как работает приведение типов данных на целевой платформе. Тем не менее, мы убедились в том, что платформа Engee делает возможным моделирование компонентов, которые практически бесшовно можно запустить на аппаратной платформе путем генерации кода.