Основные моменты из части 1.
– Arduino - это прежде всего, кроссплатформенный фреймворк для программирования микроконтроллеров. Он позволяет писать программы, которые смогут выполняться на основных современных распространенных архитектурах микроконтроллеров, включая AVR, STM32, ESP32, Raspberry Pi Pico.
– Платформа Arduino предоставляет универсальный API, абстрагированный от железа и одинаковый на разных типах микроконтроллеров. Если ваша программа использует только этот API, она будет работать на любом микроконтроллере, для которого есть реализация платформы Arduino. (Это основной плюс, другой такой платформы пока нет).
– Платформа Arduino работает поверх нативной программной платформы, специфичной для каждого микроконтроллера. Поэтому, все возможности нативной платформы (регистры, функции HAL, API ESP-IDF) будут в полной мере доступны прямо в скетче Arduino. Если не хватает возможностей API Arduino, всегда можно использовать любые возможности нативной платформы.
Как мы раскроем основные преимущества платформы Arduino, исходя из сказанного выше? Мы можем писать портируемый между разными архитектурами микроконтроллеров код. Благодаря портируемости Arduino, производители дисплеев, датчиков, всяких дополнительных модулей, микросхем управляемых по I2C и прочего, просто выкладывают драйвер для Arduino, и он может работать на любом микроконтроллере, будь то STM32, AVR, ESP32 и прочие. Write once, deploy everywhere!
Но! Здесь надо сразу понять очень важную вещь. Любое портируемое, абстрагированное, универсальное решение - неизбежно проигрывает в эффективности непортируемому, неуниверсальному, прибитому к особенностям архитектуры, специализированному под конкретный микроконтроллер решению. Поэтому, программы с использованием платформы Arduino будут есть больше ресурсов, будут работать медленнее, использовать железо менее эффективно. Но это не обязательно плохо. На самом деле, нет разницы, вы используете 5% или 55% ресурсов своего микроконтроллера. Пока у вас есть свободные ресурсы. То есть, напрашивается такой традиционный для любого программирования подход:
Трехуровневая система (подход)
-
Пока проект простой, все делаем через API Arduino, получаем максимально портируемый код.
-
Проект развивается. Эффективности начинает не хватать - в нужных местах, бутылочных голышках, начинаем использовать API нативной платформы.
-
Если проект еще дальше развивается и усложняется, приходится выжимать из железа все возможности какие есть - отказ от платформы Arduino, перенос проекта на нативную платформу полностью.
Все эти три этапа мы рассмотрим в этом цикле статей, в последующих частях.
А сейчас начнем с первой части, но сразу разберем как это делать эффективно.
ЧТО ТАКОЕ IDE и почему так не любят Arduino IDE
Для новичка IDE это бог и царь. Это такая волшебная программа, которая «делает хорошо». Без IDE вообще нельзя запрограммировать ничего. Или можно? Но как?
На самом деле, любая IDE устроена довольно просто. В основном, это всего лишь текстовый редактор, специализированный для написания кода. Но когда вы написали код - его надо откомпилировать, собрать прошивку. А потом записать прошивку в микроконтроллер. И вот тут сюрприз! Это делает не IDE! Ваша IDE только запускает в фоне утилиты командной строки, которые и компилируют ваш код. Потом вызывает утилиту командной строки, которая записывает код в микроконтроллер.
Получается, что мы можем взять любой текстовый редактор кода (да хоть Блокнот, но лучше специализированный редактор), которых имеется на любой вкус, и написать код скетча. Потом вызвать те самые утилиты командной строки, которые его компилируют и прошивают в микроконтроллер. И получить то же самое. Arduino IDE просто объединяет все это в одной программе, чтобы было удобно.
Но на самом деле Arduino IDE это не лучший представитель мира IDE. Она черезчур тяжеловесна, предоставляя при этом минимум возможностей, и скрывая многую полезную информацию по умолчанию. И основная функция - текстовый редактор - реализована в ней совсем примитивно. Не идет ни в какое сравнение с удобством Vim или Emacs. Отказавшись от Arduino IDE, можно делать все то же самое, на гораздо более слабой машине, заметно быстрее, заметно лучше понимая что происходит, и тратя заметно меньше нервов.
РАБОТА С ARDUINO В КОНСОЛИ
Итак, выбор текстового редактора оставим за читателем. Можно использовать любой, какой больше нравится. Главный вопрос - где взять Arduino для командной строки теперь? Нам нужно установить Arduino-CLI (https://arduino.github.io/arduino-cli/dev/). Набор утилит доступен для всех ОС, для Linux - еще и для ARM. Но, поскольку этот проект является полным СПО, он присутствует просто в репозиториях дистриутивов Linux. То есть - устанавливаем пакет arduino-cli из репозитория.
Теперь создадим минимальный скетч, на котором мы можем проверить, как идет сборка прошивки в arduino-cli. Для простоты, напишем скетч блинкалки светодиодом на нашей плате. Практически на всех отладочных платах есть хотя бы один светодиод, помигаем им. Наберем вот такую программу и сохраним в файл blink.ino. Имя файла должно совпадать с именем папки, в которой сохранен этот файл! Поэтому создадим папку blink и поместим файл blink.ino в нее.
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Функция setup
вызывается один раз, при включении платы. У нас она настраивает ножку LED_BUILTIN, к которой подключен светодиод распаянный на плате, как выходную. Далее в функции loop, которая вызывается раз за разом при работе, ножка включается и выключается функцией digitalWrite.
Обратите внимание, что вся работа в этом скетче делается только через функции Arduino! А это значит, что на любой совместимой плате, с любым микроконтроллером, будет работать вот этот скетч, без каких-либо изменений. Если написать два таких примера без платформы Arduino, для Atmega358 и для STM32F411, то увидите что они отличаются чуть менее чем полностью. Ножки настраиваются по-разному, ножки включаются и выключаются по-разному (похож принцип, но конкретные названия регистров совсем разные). STM32F411 еще потребует инициализации тактового генератора, простыней кода на пол-экрана. А с Arduino у нас получается один и тот же скетч, который работает и там и там. И еще на многих платформах.
Далее, вся работа сводится к вызовам утилиты arduino-cli с нужными ключами. Это позволяет сделать все то же, что делается в Arduino IDE. Рассмотрим простейший пример. Пусть у нас есть скетч, написанный в текстовом редакторе, надо его собрать и прошить в микроконтроллер. Я предпочитаю для этих задач, использовать GNU Make. Можно применить и скрипт на bash, но Make имеет ряд удобств. Правда, сейчас они не сильно проявятся из-за дубовости arduino-cli. Поместим в папку blink
файл Makefile
Выглядеть наш Makefile будет примерно так:
all:
arduino-cli -v compile -e --fqbn STMicroelectronics:stm32:Nucleo_64 --board-options "pnum=NUCLEO_F411RE"
upload:
arduino-cli -v upload -e --fqbn STMicroelectronics:stm32:Nucleo_64 --board-options "pnum=NUCLEO_F411RE"
flash:
st-flash write build/STMicroelectronics.stm32.Nucleo_64/Nucleo_64.ino.bin 0x8000000
monitor:
arduino-cli monitor -p /dev/ttyACM0 --config "baudrate=115200"
erase:
st-flash erase
Теперь разберем его построчно. Если вы не знакомы с синтаксисом Make, прочитайте про него сначала. Кратко - Makefile состоит из целей, для каждой из них прописаны команды, как эту цель собрать. Цели здесь - это all, upload, flash и т. д. После цели ставим двоеточие, на следующей строке с отступом пишем команды, нужные для реализации цели. По умолчанию, если выполнить make
в каталоге с нашим Makefile, собирается цель all. Разберем опции ее команды.
-v
- это флаг, который включает подробную информацию по ходу сборки.
compile
- команда «компилировать».
-e
- файлы с бинарной прошивкой, помещать в папку проекта, а не в temp. Нам это надо включать обязательно, чтобы потом работали команды для прошивки в микроконтроллер без зубодродительных путей в temp.
--fqbn
- полное название борды (отладочной платы, под которую все это делается). Вот с этим несколько сложнее. В моем примере это плата Nucleo-64. Это имя платы, надо уметь найти для своей платы. Для этого вызываем команду arduino-cli board list
при подключенной плате. Но работает это далеко не всегда! Для моей Nucleo-64, просто выводится Unknown вместо имени платы. Поэтому ищем FQBN из списка вручную, список получаем командой arduino-cli board listall
. В левой колонке - названия плат, в правой FQBN, просто копируем его как есть и подставляем в опцию --fqbn
.
--board-options "pnum=NUCLEO_F411RE"
- мало одного FQBN. Борда может иметь несколько вариантов, например моя Nucleo-64 имеет микроконтроллер STM32F411RE, и это не есдинственный и не дефолтный вариант Nucleo-64. Поэтому надо указывать еще и pnum. И надо тоже знать, какие варианты pnum есть для вашей борды. Для этого выполняем команду arduino-cli board details --fqbn STMicroelectronics:stm32:Nucleo_64
. И видим для этой борды, с одним и тем же названием Nucleo-64, список из штук 20 разных вариантов pnum (то есть на эти борды может ставится штук 20 разных микроконтроллеров, какой-то из них - ваш. На чипе написано название).
Вот и все! Эта команда соберет скетч. Обратите внимание, что мы не указываем тут файл скетча. И другие файлы, если у вас проект состоит из нескольких модулей. Система сборки Arduino находит их сама, она просто собирает все что лежит в папке проекта. После сборки, в папке build
внутри проекта мы получим бинарники прошивки в разных форматах.
Теперь это надо записать в микроконтроллер. Для этого цель upload. Что в ней:
upload
- команда «загрузить».
Все остальные опции те же самые, что при сборке. Эта команда находит программатор, или способ прошивки, прописанный в глубине Arduino для вашей платы, и заливает прошивку. Для Nucleo-64 - используется программатор ST-Link, распаянный на самой плате, и работающий в режиме Mass Storage. То есть, ОС видит подключенную плату как флешку, arduino-cli
просто кидает файл прошивки на эту «флешку». Это просто, но имеет свои минусы. Поэтому рассмотрим еще второй способ прошивки - через не связанную с arduino, родную утилиту st-flash
от STM32. Для этого цель flash
. Внутри нее - стандартная команда заливки прошивки через st-flash. Теперь используется тот же программатор STLink, распаянный на плате, но уже в нормальном режиме - непосредственно, STLink`a. В этой команде нам надо указать путь в bin файлу прошивки.
А что, если надо полностью стереть flash память микроконтроллера? Я вообще не нашел как это сделать нормально средствами Arduino. Поэтому используем родной st-flash
, цель erase
в Makefile.
Ну и конечно же, самая фишка Arduino IDE - отладочный терминал! Как мы его заменим? Очень просто - для этого выполняем make monitor
. Цель monitor
запускает режим терминала прямо в… терминале Linux! Здесь:
-p
- указываем виртуальный COM порт, который создается при подключении платы. Найти его не трудно командой dmesg
сразу после втыкания платы. Читаем ее вывод, и там все видно.
--config "baudrate=115200"
- настраиваем скорость порта. В вашем скетче задана скорость для Serial интерфейса, задаем здесь точно такую же.
После этого, все что в скетче печатается через Serial, мы начинаем видеть в терминале Linux, из которого вызвана команда.
Если у вас другая отладочная плата, надо заменить в Makefile
значения FQBN и pname. Если платформа не STM32, то цели flash
и erase
надо убрать. Для сборки прошивки, заливки ее на плату, и подключения монитора сообщений выполняем:
$ make
$ make upload
$ make monitor
Вот - базовая работа с Arduino из командной строки. Помимо этого, конечно же, понадобится установить модули среды Arduino для вашей борды, и библиотеки Arduino, если вы их используете. Это может быть сделано либо через Arduino IDE, и все утановленное в ней будет доступно в arduino-cli. Либо через сам arduino-cli
, как это сделать я подробно расписывать не буду, все понятно по документации на arduino-cli
.
Теперь мы можем писать скетчи на Arduino API, компилировать их и прошивать. То есть уровень 1 по моей трехуровневой системе. В следующей части рассмотрим практический пример такого скетча, с переходом на уровень 2 (нативные возможности).