Владимир Бойчук

На распутье — Ардуино, Cи или Ассемблер?

Сначала короткая предыстория появления этой статьи. Относительно давно, помигав светодиодом, захотелось сделать что-то полезное. Так появился Беспроводной программируемый по Wi-Fi комнатный термостат с монитором качества воздуха и другими полезными функциями. Как назло, в это время перестал работать мой промышленный термостат. Меня выручила еще сырая поделка, наспех спрятанная в картонную коробочку. За время отопительного сезона напрягал лишь один недостаток прототипа – это необходимость таскать по квартире удлинитель 220В и кабель, который всегда путался под шваброй. Поэтому решил сделать нечто похожее, но автономное, притом, с питанием от батареек, как в серийном образце.

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

Приступая к задаче, для меня было очевидно одно – вряд ли программы промышленных автономных устройств составлены на платформе Arduino IDE. Где все спрятано в громоздкие тяжеловесные библиотеки, а простые коды (скетчи) занимают в редакторе несколько десятков строк, делая работу в этой среде комфортной и не требующей особых усилий. Уточню сразу – дальше речь о выборе языка программирования между Ардуино, Си или Ассемблером. "Язык Ардуино"- это сленг для краткости. Нет такого языка программирования. Если увидите тут и дальше "язык Ардуино", то - это "Arduino IDE — интегрированная среда разработки для Windows, MacOS и Linux, разработанная на Си и C ++" (Википедия)

В начале пути меня оптимистично настроила статья Почему многие не любят Arduino. Ниже, для наглядности, картинка оттуда с кодом «мигалки».

 

Пример слева написан в платформе Arduino IDE, а справа - работа непосредственно с регистрами. Скетч выглядит несколько компактней, чем та же «мигалка», но с использованием регистров.

На изображении ниже - компиляция кода «мигалки» на Ассемблере. Как видно, былая компактность испарилась – количество строк в 3 раза больше, чем в Ардуино.

И так, с 2 картинок выше видно – размер памяти, занимаемой в контроллере кодом «мигалки» одним светодиодом, написанным в платформе Ардуино, составляет 1030 байт, на Си – 176 байт, на Ассемблере – 42 байта.

Теперь взглянем на более сложный код. Поскольку в своих проектах использую модуль давления-температуры BMP280, составил код барометра-термометра на Си, чтобы заодно была какая-то польза.

В проект входят следующие компоненты: контроллер ATMEGA328P, модуль давления-температуры BMP180 и дисплей Nokia 3110. ATMEGA328P принимает инфу с датчика BMP180 и после преобразований отображает ее на дисплее Nokia 3110, затем спит. Сон задается сторожевым таймером Watchdog. Проект собирается в Atmel Studio 7 и эмулируется в Proteus 8 Pro. Этот проект Atmel Studio был создан для отладки кода в Proteus'e. В библиотеке Proteus 8 Pro модуля BMP280 нет, поэтому пришлось составить код с включением BMP180. Светодиод в коде - для наглядности, чтобы придать динамику статичной картинке.

Ниже - электрическая схема устройства. При монтаже схемы обращайте внимание на функциональное назначение выводов контроллера и модулей. Подключение кварца - XTAL1, XTAL2 (ATMEGA328P). Уточню, схему барометра-термометра на BMP180 я "в железе" не собирал, поэтому тут могут проявиться проблемы, которые не видны при эмуляции в Proteus'e.

Для скачивания zip-файла проекта в Atmel Studio 7 перейдите по ссылке – тут все виртуальные проекты и коды программ из этой публикации.

Файлы прошивок *.hex находятся в папках Debug соответствующего проекта Atmel Studio 7. В архиве есть проект барометра-термометра на BMP280. Его электрическая схема такая же, как и у барометра-термометра на BMP180. Проект успешно собирается в Atmel Studio 7 и работает "в железе". Для работы "в железе" пришлось внести изменения в строке #define BMP280_ADDR 0x77 файла библиотеки bmp280.c, а именно: заменить начальный адрес 0x77 на 0x76. Не забудьте сделать эту корректировку, если будете использовать в своих проектах код барометра-термометра на BMP280, с подключенной библиотекой bmp280.c.

Ниже - код этого же барометра-термометра в платформе Arduino IDE. Естественно, с другими библиотеками.

Ресурсы, потребляемые программой барометра-термометра на Си и в Arduino IDE наглядно показаны на картинке:

Как видно, эти примеры потребляют 5954 байт (С) и 12956 байт (Arduino IDE) в Flash. Соотношение изменилось с 6-ти раз для «мигалки» до 2-х с небольшим. К сожалению, линейной зависимости нет – чем объемней код, тем меньше соотношение размеров памяти Ардуино к Си. В идеале на этой картинке должен присутствовать 3 столбец с кодом на Ассемблере, но такого кода в Интернете я не нашел, а составить код самому мне пока не под силу.

Попутно замечу, что использование компилируемых в Arduino IDE библиотек и функций на С/С++ особо имеет смысл в тех случаях, когда размер занимаемой памяти превышает или близок к размеру памяти контроллера. Мне, например, часто удается уходить от предупреждения: Недостаточно памяти, программа может работать нестабильно.

Теперь посмотрим еще один вариант – это цифровой термометр-гигрометр на AM2302 (DHT22), ATtiny13 и MAX7219, код которого составлен на Ассемблере.

Автор статьи задался целью разработать простой термометр-гигрометр выполненном на одном из самых «маленьких» микроконтроллеров — ATtiny13 с весьма скромными характеристиками – 1Кб программной памяти, 64 байтами ОЗУ и 5 интерфейсными выводами. Он решил эту непростую задачку, выбрав Ассемблер, заодно вспомнив те далекие времена, когда код можно было составлять на низкоуровневых языках, используя машины типа ZX-Spectrum.

Ниже скриншот со сборкой данного кода в Atmel Studio 7.

Код устройства на Ассемблере занимает 738 байт памяти в контроллере. Безусловно, программа барометра-термометра, о котором шла речь выше, будь ее код составлен на Ассемблере, заняла бы больше места. По нескольким причинам - в схеме реализовано управление дисплеем Nokia3110 по интерфейсу SPI (это 5 линий связи, тут – 3), связь с датчиком BMP280 осуществляется по протоколу I2C (2 линии, тут – 1) и дополнительные символы, которые позволяют не гадать – температура это или другой параметр.

Из того, что я нашел в Интернете, можно утверждать, Ассемблер даст выигрыш в размере кода для относительно больших проектов процентов 10-20 по сравнению с Си. Но надо учитывать, что в больших проектах Си может уменьшить размер кода за счёт лучшей оптимизации.

Код Ассемблера выполняется практически на машинном уровне: один цикл – одна команда. В качестве аргумента приведу пример из справочника по командам ассемблера AVR. Установка бита в регистре ввода/вывода - SBI A, b. Эта команда устанавливает заданный бит в регистре ввода-вывода. На выполнение этой операции контроллерами megaAVR потребуется 2 цикла и на tinyAVR, XMEGA - 1 цикл. Для схемы с контроллером ATtiny13 и резонатором 9,6 МГц выполнение команды займет один цикл, то есть 1/9600000 Гц = 0,104 мксек.

Выполнение похожей операции на языке Си, например, задать состояние порта - PORTB = 32; займет в этой же схеме не меньше времени. А о Ардуино и говорить нечего – там придется выполнить объемную функцию void digitalWrite(uint8_t pin, uint8_t val);. Подробно о размерах кода в Си и Ардуино читайте тут.

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

Теперь о энергосбережении немножко издали. Вспомним, что код Ассемблера выполняется на машинном уровне: один цикл – одна или несколько команд. в зависимости от типа контроллера. Это - десятые доли микросекунды. То есть, на выполнение программы с размером несколько десятков байт уйдут единицы-десятки микросекунд. Дальше контроллер бесконечно будет крутить этот набор «0» и «1», затрачивая энергию на перезаряд емкости затворов сотен полевых транзисторов, на которых построен кристалл контроллера, а также чтение и записи данных в его память. Длительность периода повтора будет зависеть только от размера кода в памяти контроллера, неважно на каком языке он составлен. Просто на Assembler'е он будет наименьшим, а в Arduino IDE – наибольшим. Соответственно, период цикла для кода на Assembler'е – наименьший, в Arduino IDE – наибольший.

Уменьшить эти затраты можно остановив процессор или программно уменьшив частоту его работы. В Ассемблере переход в "спящий" режим сна выполняет функция управления контроллером SLEEP. В других можно использовать функцию WDT (WatchDog Timer), а в Ардуино еще и функцию LowPower.powerDown (SLEEP_1S, ADC_OFF, BOD_OFF), заодно отключив все лишнее, что не используется в конкретной задаче. В эффективности этой функции сможет убедиться каждый, заменив в скетче «мигалки» (скетч - на картинке вначале статьи) функцию отсчета времени delay(1000); этой функцией и включив в разрыв питания контроллера амперметр. Да, не забудьте подключить библиотеку LowPower.h. На Си это сделал автор этой статьи. Ток в цепи питания attiny13a с паузой - 1,5мА, со сном - 240мкА. Потребление в 6(!) раз меньше.

Допустим, вы намерены собрать барометр-термометр и задумываетесь о энергосбережении. Понятно, что давление/температура в заданной разрядности не изменятся за несколько минут, которые для контроллера целая вечность. Ему можно выделить это время для сна. После сна он снова выполнит свою работу: примет информацию с датчика, преобразует в понятные для человека циферки и выведет все это на дисплей. И в таком режиме «работа-сон» он будет крутиться, пока не сядут батарейки. Объем «работы» контроллера, вернее время, которое контроллер будет занят выполнением работы, зависит от того, на каком языке составлена программа барометра-термометра. Если есть возможность загрузить в контроллер код на выбор – ArduinoIDE, C, Assembler, с одинаковым временем «сна», то в каком из трех предложенных вариантов батарейки сядут раньше (позже)? Мой ответ – ArduinoIDE (Assembler).

Так куда же идти? На мой взгляд, для любителей, как я, – это платформа Arduino IDE с низкоуровневыми вставками. Тем же, кому тесно в Arduino IDE, — в С. Хотя коды на С можно оптимизировать иногда до размеров не намного больше, чем в Assembler’е, все-таки для понимания работы контроллера стоит напрячься и освоить азы Assembler’а. Ведь полезность знаний – это аксиома.

Ссылки по теме