Разработка прошивки для встроенных устройств: типовые трудности и их решения

Встроенное программное обеспечение, или, как его часто называют, прошивка, является неотъемлемой частью любой встроенной системы, ведь электронное устройство – это не просто набор аппаратных средств. Без инструкций микроконтроллеры не знают, как управлять периферийным оборудованием. Однако разработка встроенного программного обеспечения является непростой задачей. Она включает в себя не только написание кода, но и правильно продуманное аппаратное взаимодействие периферийных устройств, тестирование и отладку. Сегодня мы поговорим о распространенных трудностях разработки встроенного программного обеспечения и о том, как их преодолевают разработчики.
Любая встроенная система требует прошивки. Внутри такой электроники, как умные часы, микроволновая печь или пульт дистанционного управления от телевизора, находятся разные периферийные устройства. За их корректную работу отвечает управляющий элемент – обычно микроконтроллер. Чтобы устройство функционировало правильно, микроконтроллеру нужны “инструкции”. Прошивка – это специализированное программное обеспечение, которое управляет работой микроконтроллера и взаимодействием с периферией.

Встроенное ПО также может устанавливаться на микропроцессоры и FPGA, но в этой статье мы будем обсуждать только проблемы разработки прошивки для микроконтроллеров.

Под термином “встроенное программное обеспечение” понимается программа, разработанная для конкретного устройства – в отличие от прикладного ПО, которое может работать на любом типе компьютера или смартфона, если на них установлена подходящая операционная система.

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

Разработка прошивки является неотъемлемой частью проектирования встроенных систем. Мы предлагаем различные услуги по разработке кастомного встроенного ПО. Наша команда имеет опыт работы с 8-, 16-, 32- и 64-разрядными микроконтроллерами от самых известных производителей: Atmel, Nordic Semiconductor, Texas Instruments, STMicroelectronics, Renesas, Silicon Labs, GigaDevice, Geehy и др. Мы преимущественно работаем с архитектурами процессоров Arm Cortex, RISC V, но также разбираемся и в других, в том числе в микроконтроллерах AVR, PIC и MSP430.

Если вам требуется разработка встроенного программного обеспечения с нуля, мы можем взять на себя как проектирование аппаратной части, так и создание прошивки. Мы также можем написать прошивку для уже готовой аппаратной платформы. Если устройство требует работы в режиме реального времени, мы можем разработать ПО на базе ОС реального времени. Для устройств, которые должны выполнять несложную логику, больше подойдет так называемое bare-metal ПО. Оно не требует операционной системы. Мы также можем проинспектировать, протестировать, отладить и оптимизировать созданное вами ПО и помочь с миграцией на новые аппаратные платформы. Свяжитесь с нашей командой, чтобы обсудить ваш случай.

Типы встроенных систем

Классификация встраиваемых систем на основе производительности микроконтроллеров и на основе производительности и функциональности устройств.
Встроенные программы различаются по сложности и размеру в зависимости от того, для какого устройства они разработаны. Встроенные системы традиционно классифицируются либо по производительности используемого в них микроконтроллера, либо по производительности и функциональности устройства.
По производительности микроконтроллера встроенные решения делятся на следующие типы:
  • системы малого масштаба (используют 8- или 16-разрядные микроконтроллеры);
  • системы среднего масштаба (используют 16- или 32-разрядные микроконтроллеры);
  • сложные системы (используют 32- или 64-разрядные микроконтроллеры).

По производительности и функциональности встроенные системы делятся на четыре типа:

  • Встроенные системы реального времени
Спроектированы так, чтобы гарантированно генерировать выходные сигналы через определенные временные интервалы. Такие решения призваны реагировать на события в реальном времени или почти в реальном времени. Устройства этого типа используются в сферах, где нужна незамедлительная реакция на запрос: в здравоохранении, военном оборудовании, системах управления дорожным движением и т.д.

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

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

  • Мобильные системы
Эти решения представляют собой небольшие портативные устройства, такие как смартфоны, планшеты, цифровые камеры. Данная категория пересекается с автономными встроенными системами. По сути, это те же автономные решения, но портативные.

Для правильной работы все перечисленные типы решений требуют встроенного программного обеспечения. Команда КЕДР Solutions имеет опыт работы со всеми этими категориями и знает, как справиться с типовыми проблемами разработки программного обеспечения для встроенных систем.

Распространенные трудности при разработке встроенного программного обеспечения

Проблемы безопасности

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

Так, в 2011 году специалист по безопасности Чарли Миллер обнаружил уязвимость в аккумуляторах MacBook. Он смог получить контроль над прошивкой микроконтроллеров, которые управляют ими. В результате Миллер мог заражать компьютеры через прошивку. По его мнению, таким образом можно даже заставить аккумуляторы перегреваться и загораться.

Еще одна частая проблема, стоящая перед разработчиками встроенного ПО, – это защита интеллектуальной собственности. Один из наших проектов иллюстрирует стандартные меры защиты, которые программисты могут использовать в этом случае. Когда мы разрабатывали музыкальную педаль для перелистывания нот, нам потребовалось обезопасить обновление встроенного ПО для устройства. Распространенным средством защиты обновлений по воздуху является шифрование.
Музыкальная педаль для перелистывания нот в приложениях, смартфон с нотами на экране и головка гитары.
В устройстве стоит микроконтроллер nRF51822 с поддержкой BLE 4.0 и ядром Cortex-M0. Для шифрования команда использовала инструмент разработки nRF Util, способный генерировать криптографические ключи и пакеты обновления прошивки (DFU). Инструмент позволяет шифровать пакет перед отправкой его по воздуху на устройство.

Еще один проект в нашем портфолио требовал разработки встроенного программного обеспечения для системы управления аккумуляторами. Заказчика очень беспокоил вопрос безопасности, поэтому команда обеспечила два уровня защиты.

В качестве первого уровня мы использовали шифрование. Система состоит из нескольких печатных плат с микроконтроллерами, и каждая из них требует регулярных обновлений прошивки. Обычно новый образ обновления передается персоналу по электронной почте. Именно в этот момент файл может быть украден. Чтобы его содержимое нельзя было взломать, наша команда шифрует файлы образов с помощью алгоритма AES-256. Перед установкой обновления устройство расшифровывает файл.

В качестве второго уровня защиты используются цифровые подписи. Когда аппаратное обеспечение получает обновление, оно проверяет по подписи, пришел ли файл от КЕДР Solutions. Если подпись неверна или вообще отсутствует, обновление не будет установлено.

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

Загляните в наше портфолио, чтобы узнать больше о проекте по разработке системы управления аккумуляторами.

Нехватка памяти

Разработчик ПО для встроенных систем ограничен в ресурсах. Как упоминалось выше, некоторые встроенные устройства очень малы: пульты дистанционного управления, веб-камеры, MP3-плееры. Размер, в свою очередь, накладывает ограничения на емкость батарей, доступную вычислительную мощность, тепловыделение и объем памяти. Более того, в то время как устройства становятся все меньше, требования к их функционалу и производительности растут. Разрешить это противоречие можно, заказав у нас разработку встроенного ПО для вашего устройства.

Одна из типовых трудностей, возникающих из-за ресурсных ограничений, – это малый объем памяти.

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

Однако такая оценка может быть неточна, или требования могут измениться непосредственно во время разработки прототипа. Тогда, чтобы созданная прошивка умещалась в доступный объем памяти, ее нужно оптимизировать.

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

Поэтому команда по разработке встроенного ПО перенесла уникальные функции из интерпретатора во встроенный язык и удалила интерпретатор. Это позволило сэкономить около 100 КБ на флеш-памяти микроконтроллера.
Фрагмент кода встроенного ПО, использующий компилятор Zero Basic для вычисления математических выражений
Еще один способ оптимизировать прошивку под доступный объем памяти – хранить информацию в подходящем типе данных. Так, если нам нужно хранить числа от 0 до 100, подойдет 1-байтовый тип данных. Но если эти же числа мы будем хранить в 4-байтовом типе данных, то память будет использоваться неэффективно.

В упомянутом выше проекте мы изначально использовали тип данных double (8 байт). Он более точен, чем многие другие типы. Но калькулятору не требовался такой уровень точности, поэтому мы решили заменить его на float (4 байта). Хотя float не так точен, он требует вдвое меньше памяти, чем double.
Фрагмент кода встроенного ПО, использующий тип данных float в целях оптимизации
Использование более “легких” типов данных будет работать как с константами, так и с переменными. Если вы точно знаете, что значение переменной не превысит 100, нет смысла хранить значения в “тяжелых” типах данных.

Энергоэффективность

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

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

Очевидный способ снизить потребление энергии ядром процессора – уменьшить количество обрабатываемых им инструкций. В этом случае поможет оптимизация кода.

Другой способ – использовать спящий режим на микроконтроллере. Как правило, устройству не нужно, чтобы ядро работало все время. Поэтому, когда обработка данных не требуется, ядро переводится в спящий режим и остается в нем, пока не произойдет внешнее событие – например, прерывание по таймеру или поступление внешнего сигнала от датчика.

Прошивка созданного нами графического калькулятора состоит из единственного цикла. В течение каждой итерации цикла ядро опрашивает кнопки. Это происходит 30 раз в секунду и занимает всего несколько микросекунд. Если ядро не обнаруживает нажатия клавиш, оно остается в спящем режиме оставшееся время.

Таким образом, в режиме опроса микроконтроллер потребляет около 5 мА. Для сравнения, в режиме активного ожидания он потреблял бы около 80 мА.

Тот же подход применяется и к периферии: когда устройства не нужны, они отключаются. В другом проекте, над которым работала КЕДР Solutions, устройство также питалось от батареи. Оно активно рассылало пакеты данных и регулярно выводило логи через UART. Однако когда рассылка не осуществляется, периферия UART отключается, чтобы минимизировать энергопотребление.
Фрагмент кода встроенного ПО, который отключает UART для снижения энергопотребления
Естественно, этот метод работает только для выходов. Поскольку процессорное ядро выполняет инструкции на основе входных сигналов, выключать их не рекомендуется. В этом конкретном случае UART использовался только для вывода сигнала.

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

Аналогичный подход наша команда использовала при работе над устройством отслеживания персонала и активов. В решении применяется BLE 5.0 для отправки и приема пакетов данных каждые 7,5 миллисекунды. Во время соединения энергопотребление BLE достигает пика. Одна из наших задач состояла в том, чтобы максимально снизить его, не влияя на пропускную способность канала.

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

Трудности, связанные с отладкой

Отладка является неотъемлемой частью разработки встроенного программного обеспечения, особенно когда речь идет о проектировании сложных устройств. Для поиска ошибок в коде используют различные инструменты отладки. Они позволяют выполнять код шаг за шагом и обнаруживать места, где программа зависает. Однако некоторые китайские производители микроконтроллеров не имеют совместимых инструментов отладки.

Тогда приходится отслеживать поведение программы, вводя нужные значения, строки кода или текст в консоль, – например, выводя данные через UART. Эти строки вставляются в части кода, которые предположительно содержат ошибку.

Если значение, строка кода или текст отображаются, значит, код выполняется корректно. Если строка не выводится или значение изменилось, значит, что-то пошло не так на предыдущем шаге. Если консоли нет, можно также запрограммировать светодиод включаться и выключаться.

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

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

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

После обновления ПО устройство может работать неправильно. Это означает, что код содержит ошибки, но использовать отладчик уже не получится. В таких случаях приходится использовать метод, описанный выше.

Обновления и обновления по воздух

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

Обновить прошивку простого устройства не так уж трудно. Но в случае сложных систем и особенно устройств Интернета вещей требуется проявить находчивость. Здесь мы сталкиваемся с целым рядом проблем: недостаточным объемом памяти, недостаточной пропускной способностью устройств, трудностями с управлением версиями и, конечно же, с вопросом безопасности.

Распространенная проблема здесь заключается в прерываниях. Устройства с малым объемом памяти должны перезаписывать прошивку напрямую, как только они получают данные. Если что-то прервет процесс, код окажется поврежден: например, если питание отключится до завершения установки.

В худшем случае устройство “закирпичится”, и это уже не исправить. Если у устройства есть загрузчик, прошивку можно переустановить. Тем не менее при работе с обновлениями по воздуху прерывания могут происходить часто. Проблема обычно решается с помощью резервной памяти.

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

Именно туда предварительно загружается образ прошивки. В роли такой памяти может выступать либо файловая система, либо область флеш-памяти. После этого устройство выполняет проверку целостности данных, чтобы убедиться, что ни один байт не был потерян или изменен во время передачи. И только затем образ устанавливается. Если вдруг во время установки обновления, все же что-то пошло не так, то будет загружен предыдущий рабочий образ. Таким образом, прерывания не могут иметь серьезных последствий.

Точность генерации импульсов

Еще одной трудностью при разработке встроенных программ становится достижение необходимой точности таймингов. Так, в одном из проектов заказчик предъявил очень жесткие требования к быстродействию системы. При этом программа должна была выполнять довольное большое количество задач. Было решено писать прошивку под операционную систему реального времени, поскольку у ОС есть механизмы для диспетчеризации задач и планирования их выполнения, а значит мы упростили бы себе организацию кода, сэкономили бы трудозатраты.

Из-за того, что ОС требуется время на организацию процессов, мы предвидели, что сигналы будут генерироваться с задержкой. Само по себе это не стало бы проблемой, т.к. в используемой ОСРВ есть соответствующая настройка для процесса и задержка ожидалась стабильной. Однако на испытаниях выяснилось, что такое решение дает критичный для нас джиттер. Вот как выглядел сигнал на выходе:
Осциллограмма сигнала, генерируемого во встроенном устройстве с применением операционной системы.
Тогда команде пришлось переписать прошивку без применения операционной системы. Это сложнее, поскольку программисту надо самостоятельно реализовать диспетчеризацию, и требует большего времени на отладку и разрешение конфликтов подпрограмм. Но в результате удалось добиться нужной точности генерации сигналов. Вот как стала выглядеть осциллограмма:
Осциллограмма сигнала, генерируемого bare-metal прошивкой (без участия операционной системы) во встроенном устройстве.

Заключение

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

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

Еще одна распространенная проблема связана с обновлением прошивки. Обновления по воздуху нестабильны, что приводит к прерываниям в передаче файлов и, в конечном итоге, к повреждению данных. Если такие обновления устанавливать напрямую, не загружая их предварительно в резервную память, ошибка может привести к “закирпичиванию” устройства. Также необходимо обеспечить должный уровень безопасности при передаче данных, поскольку через прошивку можно взломать само устройство или подменить прошивку.

И наконец, много времени уходит на отладку. Чтобы ускорить этот процесс, разработчикам необходимы опыт и профессиональные знания. Вот почему многие готовы передать разработку прошивки на аутсорсинг компаниям по проектированию встроенных систем, таким как КЕДР Solutions. Мы хорошо знакомы с микроконтроллерами различных производителей, таких как Microchip, Nordic Semiconductor, Texas Instruments, STMicroelectronics, Renesas, Silicon Labs, GigaDevice, Geehy и др. Благодаря опыту работы с 8-, 16-, 32- и 64-битными микроконтроллерами мы можем взять на себя разработку встроенного программного обеспечения любой сложности. Напишите нам, чтобы обсудить, как мы сможем помочь вашему проекту.
Другие статьи