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

Генерация кода для Arduino (генерация ШИМ сигнала)

В этом примере мы покажем, как в Engee можно сгенерировать программный код для генерации ШИМ на платформе Arduino.

Введение

Создать широтно-импульсно модулированный сигнал (ШИМ) можно множеством способов. Мы реализуем модель из базовых компонентов, максимально похожую на демонстрационный пример arduino_blink. То есть нам предстоит управлять временной задержкой между событиями включениям и выключения светодиода.

Другие способы, которым можно воспользоваться:

  • Сравнивать плавно меняющийся аналоговый сигнал с пилообразным сигналом и переключать диод в момент пересечения
  • Использовать одну из встроенных функций Arduino

Подготовка платформы

Для этого примера нам потребуется Arduino-совместимая платформа (Uno, Leonardo, Iskra и другие) и подходящий у ней шнур USB. Также нужно будет установить на ваш компьютер среду Arduino IDE, найти и поставить дополнительные драйверы (если необходимо) и подключить имеющуюся плату через порт USB.

image_2.png

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

В этом примере мы будем генерировать код из модели pwm.engee.

Интерфейс модели совпадает с интерфейсом в примере arduino_blink. На каждом шаге вычислений она:

  1. переключает состояние светодиода при помощи выхода out_LED_BUILTIN (изначально равно 1, с каждым циклом меняется на "противоположное"),
  2. возвращает Arduino значение временной задержки при помощи выхода param_WAIT_MS (значение в миллисекундах).

image_2.png

Скважность будет изменяться по синусоиде с частотой, заданной в блоке Sine. Максимальное значение задается в качестве амплитуды этого же блока, а задаваемое там же смещение позволяет значению задержки никогда не становиться меньше 0, что вызовет ошибку на платформе Arduino и остановит выполнение программы.

Именно при помощи значения param_WAIT_MS мы будем управлять скважностью импульса, включая и выключая "бортовой" светодиод платформы Arduino импульсами разной длины.

ШИМ-сигнал характеризуется тем, что в течение фиксированного периода $T_d$ он сменяет состояние 1 и состояние 0, длительность которых в сумме равна $T_d$, но отношение которых изменяется при помощи параметра $w$ (скважность).

image.png

Мы будем использовать этот сигнал для имитации плавного управления яркостью светодиода на платформе Arduino.

Приведение типов данных

В основном, блоки модели Engee находят прямое отражение в коде для Arduino. Не все платформы используют одинаковые стандарты языков С/С++. Естественно, при создании кода всегда приходится думать о низкоуровневых деталях реализации.

В этом примере проблемой, которую мы преодолеваем в Engee, является автоматическое приведение типов. На платформе Arduino:

  • При сложении булевого типа данных с численным на выходе получается булевый
  • При сложении типа данных double с беззнаковым целым числом на выходе получается беззнаковое целое число (работа синусоиды оказывается неверной, если не указать особый тип данных)

Где возникают проблемы с типами данных?

image_3.png

Поэтому нам нужно предусмотреть, как проявится работа типов данных в коде на Си, и соответствующим образом настроить блоки модели (отмеченные зелеными галочками):

  • Блоки Constant должны возвращать тип данных Float32
  • Блок Compare To Zero должен возвращать не bool, а uint8

Все эти настройки выставляются в соответствующих блоках. Иногда эти проблемы можно решить при помощи блоков Data Type Conversion, такие преобразования тоже переходят в сгенерироавнный код.

Тестирование модели

Процесс полунатурного моделирования подразумевает запуск модели в симулированном окружении для отладки и оптимизации с последующим контролем работы того же алгоритма на аппаратной платформе.

В нашем случае стоит учитывать то, что Arduino работать на собственной тактовой частоте, и разные команды могут выполняться за разное количество тактов. Чтобы опираться на реальное физическое время, лучше создать шаблон для генерации кода на основе системы реального времени.

Тем не менее мы уже можем показать, как будет работать наше решение.

In [ ]:
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()).

In [ ]:
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)
Out[0]:

Что мы здесь видим? Временная задержка param_WAIT_MS изменяется во времени таким образом, чтобы каждые два соседних значения в сумме всегда образовывали примерно 10 миллисекунд. Законом для модуляции этого процесса задается синусоидой.

Если правильно совместить оба выходных сигнала, то мы увидим именно тот ШИМ сигнал, который управляет включением и отключением тока, подаваемого на выход, к которому подключени светодиод.

In [ ]:
plot( cumsum(data["param_WAIT_MS"].value),
             data["out_LED_BUILTIN"].value,
      st=:step, size=(900,200))
Out[0]:

Эта модель не настроена таким образом, чтобы имитировать реальное время выполнения операций, поэтому вы можете заметить, что модельное время (2 секунды – длительность моделирования) не равно процессорному времени (по графику – примерно 1 секунда, и то с большими допушениями). Для обеспечения этого свойства нужно немного по-другому построить модель.

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

В этой демонстрации мы предлагаем сгенерировать код при помощи следующей команды:

In [ ]:
engee.generate_code( "$(@__DIR__)/pwm.engee",
                     "$(@__DIR__)/sketch_pwm_custom/pwm_code" )
Out[0]:
"Created directory - /user/start/examples/codegen/arduino_pwm/sketch_pwm_custom/pwm_code"

В итоге, в каталоге sketch_pwm_custom повяляется папка pwm_code, в которой мы находим .c и .h файлы с кодом нашей модели.

Файлы проекта

В нашем проекте только одна модель. Из сгенерированных файлов нам нужны будут только файлы с кодом этой модели: исходный код pwm.c и заголовочный файл pwm.h.

Стоит обратить внимание на структуру файлов и каталогов проекта:

image_2.png

  • файл sketch_pwm_custom.ino содержит описание интерфейса Модель-Arduino,
  • каталог уровня sketch_pwm_custom должен называться так же, как и файл *.ino (требования Arduino IDE),
  • каталог pwm_code содержит файлы со сгенерированным кодом, которые мы будем включать в файл *.ino.

Перенос модели на Arduino

Чтобы перенести проект на Arduino, стандартные шаги следующие:

  1. Скачать каталог sketch_pwm_custom из файлового браузера, распаковать архив
  2. Открыть файл sketch_pwm_custom.ino и нажать на кнопку Upload

Результат должен быть следующим (светодиод плавно мерцает):

20240127_042956_2.GIF

Заключение

Мы создали чуть более сложную модель, чем мигающий светодиод. Такой способ создания ШИМ сигнала позволит управлять простой техникой, например светодиодами или сервомоторами.

Нам пришлось учесть, как работает приведение типов данных на целевой платформе. Тем не менее, мы убедились в том, что платформа Engee делает возможным моделирование компонентов, которые практически бесшовно можно запустить на аппаратной платформе путем генерации кода.

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