Использование mbed кода в собственном проекте на STM32 — опыт разгона китайского LCD
Иногда чужой код очень помогает в деле подключения к микроконтроллеру периферийного железа. К сожалению, адаптировать чужой код к своему проекту бывает сложнее, чем переписать его самому, особенно если речь идет о мега фреймворках вроде arduino или mbed. Желая подключить китайский LCD на базе ILI9341 к плате STM32L476G DISCOVERY, автор задался целью воспользоваться в демо-проекте от ST драйвером, написанным для mbed, не изменив ни строчки в его коде. В результате удалось заодно разогнать экран до невиданных скоростей обновления в 27 fps.
Введение в проблему
ST Microelectronics производит весьма интересные, как по возможностям, так и по цене, микроконтроллеры, а также выпускает платы для быстрой разработки. Про одну из них и пойдет речь — STM32L476G DISCOVERY. Вычислительные возможности этой платы весьма радуют — 32 битный ARM с максимальной тактовой частотой 80MHz может выполнять операции с плавающей точкой. При этом он способен снижать энергопотребление до минимума и работать от батарейки, ожидая возможности сделать чего то полезное. К этому устройству я решил подключить дешевый китайский цветной LCD с разрешением 320 на 240, работающий по SPI интерфейсу. Про то, как использовать его с mbed, подробно написано здесь. Mbed — это онлайн среда программирования, где вы можете скомпилировать себе прошивку вообще не имея компилятора на своем компьютере, после чего скачать ее и прошить, просто скопировав на свою mbed-совместимую плату, которая при подключении к USB выглядит как сменный диск. Все это замечательно, но есть несколько проблем. Во-первых, не все платы mbed-совместимы. Во-вторых, есть много уже существующих проектов, которые с mbed не совместимы вообще никак, в том числе софт, поставляемый ST. Ну и наконец, не все разработчики совместимы с mbed, некоторые (например автор этих строк) находят в этом чудесном инструменте больше недостатков, чем преимуществ. Каковы эти недостатки, мы обсудим ниже, пока достаточно упомянуть, что после подключения драйвера дисплея к демо-проекту от ST и нескольких простых оптимизаций он стал работать быстрее примерно в 10 раз.
Изучаем код драйвера
Настало время скачать и изучить исходный код драйвера дисплея. Работа с портами в mbed организована через вызовы методов классов, представляющих порты ввода-вывода. Например, класс DigitalOut реализует доступ к порту вывода. Присвоение экземпляру этого объекта нуля или единицы инициирует запись соответствующего бита в выходной порт. Инициализация класса DigitalOut производится перечислимым типом PinName, единственное назначение которого — идентифицировать ножку процессора. Один из главных недостатков реализации DigitalOut и других классов, реализующих ввод-вывод — то, что инициализация порта происходит в конструкторе экземпляра класса. Это отлично подходит для моргания светодиодом, если экземпляр класса DigitalOut создан на стеке в функции main. Но представим себе, что у нас много разнообразного железа, инициализация которого разбросана по нескольким модулям. Если мы сделаем экземпляры наших классов ввода-вывода статическими переменными, то потеряем всякий контроль над инициализацией, поскольку она будет происходить до функции main и в произвольном порядке. Библиотеки ST (они называются HAL — hardware abstraction level) используют другую, более гибкую, парадигму. Каждый порт ввода-вывода имеет свой контекст и набор функций, которые с ним работают, но их можно вызывать именно тогда, когда это необходимо. Контексты портов обычно создаются как статические переменные, но никакой автоматической неконтролируемой инициализации при этом не происходит (библиотеки ST написаны на C). Стоит также упомянуть чрезвычайно удобную утилиту CubeMX, которая может для нужного вам набора портов сгенерировать весь необходимый код инициализации и даже позволяет вам впоследствии вносить изменения в этот набор портов не затрагивая вашего собственного кода. Ее единственный недостаток — невозможность использования с уже существующими проектами, вы должны начать проект с использования этой утилиты.
Библиотека mbed для инициализации ресурсов микроконтроллера использует те же функции HAL из библиотеки ST, но делает это местами поразительно бездумным образом. Чтобы убедится в этом, достаточно посмотреть код инициализации SPI порта (который нам понадобится для работы с дисплеем) в файле spi_api.c. Функция spi_init сначала ищет подходящий SPI порт по ножкам, которые он будет использовать, а затем вызывает функцию init_spi, которая собственно и инициализирует порт. При этом на все 3 возможных SPI порта используется одна статическая структура контекста
По сути это классический пример использования глобальных переменных вместо локальных. Даже с учетом того, что у нас одно вычислительное ядро, глобальный контекст не защищен от одновременного использования в разных местах кода, есть ведь еще прерывания, а также вытесняющая многозадачность.
Подключаем библиотеку к своему проекту
Итак писать весь код на mbed я не хочу. Мне гораздо больше нравятся примеры от ST, которые поставляются в составе CubeMX. Готового драйвера моего LCD для библиотек ST я не нашел, писать его самому желания не возникло. Остался альтернативный способ как то развлечься — подключить драйвер, написанный для mbed, причем так, чтобы в нем не потребовалось ничего менять. Для этого нужно всего то — реализовать библиотеки mbed альтернативным способом. На самом деле задача проще, чем кажется, поскольку из всех библиотек mbed драйвер LCD использует только порт вывода и SPI. Кроме этого, ему нужны функции формирования задержек и классы файлов и потоков. С последними все просто — они нам не нужны и заменяются заглушками, которые не делают ничего. Функции формирования задержек написать легко — они в файле wait_api.h. Реализация классов ввода-вывода требует чуть более творческого подхода. Мы собираемся исправить недостаток библиотек mbed и не делать инициализацию железа в конструкторе. Конструктор будет получать ссылку на контекст порта, расположенный где то еще, код его инициализации будет совершенно независим от наших интерфейсных классов. Есть единственный способ передать эту информацию в конструктор, не меняя код драйвера, — через PinName, который вместо простого перечисления ножек будет отныне хранить указатель на порт, номер ножки, а также опционально указатель на ресурс (вроде SPI), к которому подключена эта ножка.
Реализация порта вывода достаточно тривиальна. Для улучшения производительности мы постараемся поменьше использовать функции HAL, а работать по возможности напрямую с регистрами порта, а также писать код inline, что позволит компилятору избежать вызовов функций.
Код реализации SPI порта ненамного сложнее, его можно посмотреть тут. Поскольку мы отделили инициализацию порта от интерфейсного кода, запросы на изменение конфигурации мы игнорируем. Разрядность слова просто запоминаем. Если пользователь желает передать 16 битное слово, а порт сконфигурирован как 8 битный, то нам достаточно просто переставить местами байты и передать их один за другим — в буфер порта все равно помещается до 4 байт. Все файлы, необходимые для компиляции драйвера, собраны в директории compat. Теперь можно подключить оригинальные файлы драйвера к проекту и скомпилировать их. Нам понадобится также код, который проинициализирует порты, создаст экземпляр драйвера и нарисует на экране что то осмысленное.
Разгон
Если LCD используется для вывода чего то динамического, то возникает естественное желание сделать общение с ним более быстрым. Первое, что приходит в голову — повысить тактовую частоту SPI, которую драйвер выставляет в 10MHz, но мы его пожелания игнорируем и можем выставить какую угодно. Оказалось, что экран прекрасно работает и на частоте 40MHz — это максимальная частота, на которую способен наш процессор с тактовой частотой 80MHz. Для оценки производительности был написан простой код, который в цикле выводит битмапку 100×100 пикселей. Результат потом экстраполировался на весь экран (битмапка, занимающая весь экран, в память просто не помещается). Результат — 11fps весьма далек от теоретического предела в 32fps, который получается, если передавать 16 бит на каждый пиксел без остановок. Причина становится понятна, если заглянуть в исходный код драйвера. Если ему нужно передать последовательность байт, он просто передает их один за другим, в лучшем случае упаковывая в 16 битные слова. Причина такого неэффективного дизайна кроется в mbed API. SPI класс имеет метод для передачи массива данных, но он может использоваться только асинхронно, вызывая функцию нотификации по завершению, причем в контексте обработчика прерывания. Неудивительно, что этим методом мало кто пользуется. Я дополнил свою реализацию класса SPI функцией, которая передает буфер и ожидает окончания передачи. После того, как я добавил вызов этой функции в код передачи битмапки, производительность возросла до 27fps, что уже весьма близко к теоретическому пределу.
mcu.by
Услуги разработки встраиваемого ПО и контрактная разработка электроники на ShuraCore
Старт ARM. mbed os. часть 1-ая.
Доброе время суток!
Продолжаем говорить об операционных системах реального времени и на повестке дня у нас mbed os.
mbed – это платформа и операционная система для подключенных к Интернету, устройств на базе 32-разрядных ARM Cortex-M микроконтроллеров. Такие устройства также известны как, устройства “Интернет вещей”. Проект совместно разработанный с компанией ARM и с ее технологическими партнерами.
Разработка программного обеспечения.
Приложения могут быть разработаны только для mbed платформы (http://mbed.org/). Для разработки используется онлайн IDE среда для написания и компиляции кода. Код пишется и составляется в браузере, и храниться на облаке, с помощью С++ компилятора ARMCC C/C++ компилируется код. Mbed IDE предоставляет рабочее пространство с возможностью импорта и обмена кодом с распределенным управлением версией программы, и генерации документации на программу.
Для разработчика предоставленный огромный инструментарий для создания и написания прошивок для микроконтроллера. С mbed можно работать на смарт-устройствах. Он состоит из основных библиотек, предоставляющих для микроконтроллера драйвера периферийных устройств, ethernet, RTOS и среды выполнения, инструменты для сборки и сценарии тестирования, и отладки.
БД содержит библиотеки драйверов для устройств, которые могут быть подключены к микроконтроллерам для создания конечного продукта.
Mbed HDK предназначен для OEM-производителей, а также предоставляет информацию для формирования специализированного оборудования для поддержки mbed SDK. Он состоит из Firmware Interface и схем, которые могут быть легко использованы для создания макетных(печатных) плат, модулей OEM и перепрограммируемых продуктов, применимых для производства.
Проект разработан ARM в сочетании с другими крупными технологическими компаниями и mbed сообщества разработчиков.
Ядро платформы, разработанные в рамках лицензии Apache License 2.0. Mbed работает с HAL от производителей микроконтроллеров.
В этой статье создадим три проекта для поднятия mbed os и запустим их. Проекты я буду создавать для stm32f429i-disco.
Пример 1. Мигание светодиодов.
Пример 2. Эхо на UART.
Пример 3. AT на BT(BT_BOARD V1.05).
Проект для Keil собран для stm32f429i-disco.
Вопросы и пожелания по mbed os оставляйте в комментариях.
Как перестать бояться и полюбить mbed [Часть 1]
Мы занимаемся поставками электронных компонентов. Чтобы делать нашу работу хорошо, недостаточно просто уметь привозить и продавать электронные компоненты — ещё важно уметь демонстрировать их преимущества. Именно поэтому мы не только пишем обзорные статьи, но и создаем руководства по применению разных «железок» и разрабатываем небольшие демонстрационные проекты.
Об истории создания одного из таких демонстрационных проектов я и расскажу — буду последовательно описывать процесс создания прототипа устройства, оснащенного ёмкостным сенсорным экраном, и предназначенного для измерения относительной влажности и температуры.
Особенный интерес представляет подход к написанию встроенного ПО — софт полностью написан в онлайн IDE от mbed. То есть программа для микроконтроллера была создана на единственной вкладке гугл-хрома и одинаково работает на отладочных платах от разных производителей.
В качестве базовой платформы выбрана плата SLSTK3400A от Silicon Labs с контроллером серии EFM32 Gecko на борту. На первом фото она в центре. Однако, моя цель — это создание в mbed такой программы, которая бы работала и на других mbed-овских отладочных платах. Поэтому в качестве управляющего модуля на каждом этапе разработки будут попеременно включаться разные отладки от SiLabs, Atmel и Wiznet.
Теперь рассмотрим компоненты более подробно.
Цифровой датчик относительной влажности HYT-271 от IST
Датчикам серии HYT уже была посвящена статья на Хабре, сегодня я приведу менее детальное описание.

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

Порядок управления графическим контроллером и особенности программных средств разработки от FTDI мы также рассмотрим в следующих статьях.
С ассортиментом TFT-модулей можно ознакомиться на сайте производителя и на нашем сайте. На дисплеи с диагональю 2.83″, 5″ и 7″ как раз специальные цены действуют.
Для демонстрационного проекта используется TFT-модуль RVT43ULFNWC00 серии uxTouch. Он имеет диагональ 4.3», ёмкостный сенсорный экран и встроенный графический контроллер FT801.
Дисплеи uxTouch — это самые симпатичные из существующих модулей Riverdi. Они выполнены в декоративной рамке, покрыты защитным стеклом и не имеют внешних монтажных креплений и отверстий. Для установки дисплея в корпус предусмотрена клейкая поверхность, которая расположена на задней стороне декоративной рамке по всему периметру дисплея. Выглядит всё это действительно здорово — когда крутишь модуль в руках, привыкшие к ipad руки и глаза не чувствуют никакого подвоха.
Отладочная плата и mbed
Для опроса датчика и отображения данных на TFT-дисплее, при наличии микросхемы FT801, от управляющего микроконтроллера не требуется высокой производительности. Чаще всего хватает даже 8-разрядного кристалла, в этом собственно и фишка использования графического контроллера от FTDI.
Однако, в угоду своему желанию познакомиться с mbed, я всё-таки использую платформу ARM Cortex-M — в качестве основной рабочей платы беру SLSTK3400A, а в качестве альтернатив ATSAMD21-XPRO от Atmel и WIZwiki-W7500P от Wiznet. Помимо необходимых интерфейсов I2C и SPI, целевые контроллеры имеют довольно приличный объем памяти — не менее 64 Кб Flash. Это бонус позволит реализовать не только красивое отображение полученной с датчика информации, но и, например, хранение изображений.
На отладочных платах нам не понадобится большинство доступных периферийных устройств. Цепи измерения энергопотребления от SiLabs, аппаратный Ethernet от Wiznet, а также бесконечные контроллеры, сенсорные и механические кнопки, светодиоды и ЖКИ — всё идет лесом. Обращаем внимание только на наличие встроенного USB-отладчика и выводов необходимых цифровых сигналов.
Теперь об mbed.
Mbed — это программная платформа для разработки IoT-устройств на базе микроконтроллеров с ядром ARM Cortex-M. Платформа разрабатывается самой компанией ARM и производителями микроконтроллеров. Я не буду касаться многих входящих в состав mbed инструментов (облачных сервисов, инструментов шифрования, mbed OS и т.д.). Рассматриваться будут только онлайн IDE, «базовая» библиотека mbed и драйверы для сопряжения отладочной платы с другими устройствами.
Библиотеки mbed предоставляют уровень абстракции, на котором программа пишется не под конкретный контроллер STM32 / Atmel / NXP, а сразу под любую платформу, которая поддерживается в mbed. Поддержка платформы обозначает, что mbed-вызовы корректно обращаются ко всему HAL целевого микроконтроллера, что mbed понимает топологию отладочной платы, и что отладочная плата опознается на ПК как внешний диск и прошивается простым копированием бинарного файла на этот диск.
О том, как зарегистрироваться на developer.mbed.org, скомпилирвать и запустить демо-программу, уже написаны подробные инструкции. Вот, например, руководство по миганию светодиодом моего авторства.
Посему сразу перейдем к заготовке будущего проекта — простой программе для вывода на последовательный порт счетчика секунд.
Создаем проект, состоящий из файла main.cpp и пакета библиотек mbed, компилируем программу, получаем автоматически скачанный бинарный файл и копируем его на плату SLSTK3400A, которая определяется компьютером как внешний диск.
Теперь, запустив Terminal, мы получаем с контроллера искомые данные.
Для того чтобы сгенерировать прошивку для плат от Atmel и Wiznet, достаточно сменить целевую платформу,
После этого вновь скомпилировать проект и скопировать полученный файл на соответствующий диск — XPLAINED или MBED (да, корейцы отличились в придумывании имен).
На маке программа сразу заработала на всех трех платах, а на Windows понадобилась установка mbed-овского драйвера COM-порта. Без него по последовательному интерфейсу хотела общаться только плата от SiLabs. Впрочем, это штатная ситуация, хорошо описанная самой компанией ARM.
Здесь пора заметить один из главных минусов mbed: printf — это по факту единственное доступное средство отладки. Кроме того что вам недоступна отладка с breakpoint-ами и доступом к регистрам микроконтроллера, у mbed существуют и другие очевидные ограничения. Например, универсальные драйверы периферии по определению не могут поддерживать все особенности периферии каждого контроллера, то есть в mbed могут отсутствовать библиотеки для необычных модулей или поддержка каких-то нестандартных режимов работы периферийных устройств вашего микроконтроллера. А это могут быть именно те режимы и модули, из-за которых вы и выбрали свой клисталл.
Ещё, конечно, mbed — не лучший инструмент для создания сложных приложений, связанных с планированием задач и многопоточностью (хотя я не в курсе что умеет новая mbed OS 5). Ну и в конце концов, список поддерживаемых аппаратных платформ пока далеко не полон.
Чтобы полюбить mbed, нужно хорошо понимать круг задач, для которых он предназначен (спасибо, капитан). На мой взгляд, платформа mbed наилучшим образом подходит:
а) для прототипирования и быстрого освоения новых компонентов,
б) для знакомства с программированием микроконтроллеров.
Ключевыми свойствами mbed являются универсальность, доступность и простота использования программных библиотек, а также возможность экспорта написанного в mbed софта во «взрослые» IDE.
Ещё один плюс mbed — интеграция в компилятор системы контроля версий, а также сама идея простого обмена программными модулями. Считается, что разработчики встраиваемых систем пользуются системами контроля версий довольно неохотно. Mbed — отличный повод наконец-таки прочувствовать преимущества использования контроля версий, если раньше руки не доходили. Что же касается публикации собственных наработок и использования чужих библиотек и проектов, то с учетом железонезависимости mbed-программ идея opensource для embedded выглядит гораздо более привлекательно, чем обычно.
Возвращаясь к моему проекту, сразу скажу что итоговая программа, изначально написанная под микроконтроллер SiLabs, абсолютно корректно работает на платах от Atmel и Wiznet. По идее всё должно получиться и на других доступных микроконтроллерных платформах, полный список которых доступен на developer.mbed.org/platforms/.
Также имеет смысл заглянуть в список поддерживаемых компонентов developer.mbed.org/components/. Для всех перечисленных дисплеев, сервоприводов, датчиков, беспроводных контроллеров и других модулей доступны библиотека и пример её использования. Для моего TFT-модуля Riverdi (точнее для контроллеров серии FT800) такая библиотека уже была создана одним из пользователей, а вот драйвер для датчика HYT был написан в ходе разработки этого проекта.
Процесс разработки приложения будет последовательно описан в следующих статьях, а видео с работающей сборкой я могу показать уже сейчас.
ARM MBED OS. Работа с произвольным МК STM32 под PlatformIO
Когда в январе сего года я писал материал о файловой системе LittleFS (интегрированной в состав arm mbed os), то обещал в скорейшем времени описать создание проекта с arm mbed os для произвольного микроконтроллера STM32. Как известно, онлайн IDE от ARM (а точнее, выделенного подразделения Arm mbed) поддерживает, во-первых, строго определенное число отладочных плат, и число их невелико; во-вторых, экспортирует онлайн-примеры, на базе которых можно строить какие-то свои проекты, только для наиболее известных IDE: ARM, uVision KEIL и IAR. Более того, некоторые примеры не экспортируются вовсе. То есть, доступны для экспорта или только варианты для IAR, или только для KEIL, и так далее. Так что, как в то время показалось, научиться “прикручивать” arm mbed os к любому МК было бы не лишним вовсе.
Однако, жизнь вносит свои коррективы в любые планы, и работать в этом направлении длительное время не получалось. Но вопрос оставался открытым, и теперь, по прошествии значительного времени, я возвращаюсь к тематике.
Так или иначе, оставался неразрешенным один немаловажный вопрос. Мы все используем различные IDE и различные тулчейны. Процесс портирования довольно непрост, и требует определенных танцев с бубном. К примеру, ассемблер для GCC не поддерживает синтаксис x86 (там AT&T), поэтому самая первая и элементарная проблема, с которой тут столкнется программист – это ругань того же GCC-шного компилятора на ассемблерные вставки в исходных кодаx операционной системы Arm mbed.
Кто-то пользуется IAR, кто-то uVision, кто-то пишет в Sublime Text, а кто-то (как и я) – в Code::Blocks. Кто-то использует Windows, а кто-то – Linux. Объять необъятное и охватить неохватываемое мы не в силах, и при этом оставить один из вариантов без рассмотрения – значит оставить за бортом какую-то часть аудитории.
Решение пришло внезапно и оказалось весьма простым и универсальным.
PlatformIO IDE.
PlatformIO – кроссплатформенный тулчейн, написанный на python, наличие которого на машине пользователя является, пожалуй, единственным обязательным условием (не ниже версии 2.7).
По своему исполнению и использованному инструментарию PlatformIO мне напомнил несколько лет назад вышедшую IDE MicroEJ Studio, в которой можно было писать код для микроконтроллеров на Java. В дальнейшем в МК заливалась MicroJVM (написанная на С), и код исполнялся в ней. Широкого распространения, впрочем, среда не получила, и в массы не пошла.
PlatformIO может использоваться в составе ряда широко распространенных IDE и редакторов кода:
Основными элементами являются PlatformIO IDE и PlatformIO Core.
В довольно-таки уже далеком 2016 году PlatformIO была кандидатом на награждение в номинации “Best IoT Software&Tools” в конкурсе 2016 IoT Awards.
Это в общих чертах. Подробную документацию можно изучить на сайте проекта platformio.org и в разделе Документация.
Наша же задача – установить требуемые средства разработки, создать проект, и что-то в нем сделать.
Atom vs. VS Code
На домашней странице к загрузке предлагаются два редактора: Atom и VS Code. Я попробовал оба, и сразу скажу: VS Code удобнее. Хотя бы потому, что в нем элементарно присутствует переход по коду. Забегая вперед, скажу: в проекте библиотек и исходников arm mbed os вы не увидите, они все сидят в локальном репозитории, поэтому в дереве проекта будут только ваши main.cpp и всё прочее, что создадите вы сами. Поэтому смотреть какие-то объявления, классы и их объекты, интерфейсы классов, придется сто процентов. А Atom такой возможности… не представляет! И при использовании Atom довольствоваться нужно будет только документацией mbed os. Согласитесь, это неудобно.
Итак, дальнейшее рассмотрение процесса я провожу в применении к VS Code. Нам необходимо проделать следующие шаги:
После установки запускаем редактор, открываем панель расширений (Extensions), и вводим в поиске “platformio”. Первым же вариантом выскочит “PlatformIO IDE”. Нажимаем “Install”, дожидаемся окончания установки, перезагружаем редактор.
Пользователям Linux можно сразу установить udev rules для адекватной работы отладчика. В принципе, этот шаг можно опустить, и вернуться к нему в том случае, если при старте отладчика терминал выдаст сообщение вроде “Remote communication error. Target disconnected.: Connection reset by peer.”
Открываем терминал и пишем в нем:
Если терминал выдаст “Permission denied”, то скачиваем файл “99-platformio-udev.rules” по ссылке, и принудительно копируем файл в etc/udev:
где $USER – это имя вашего пользователя. К примеру, у меня это subdia.
После этого все проблемы с отладчиком, если они могли возникнуть, должны решиться.
Окружение и локальный репозиторий arm mbed os
После установки окружения не будет лишним понять, где находится локальный репозиторий arm mbed os (как я уже говорил, в дереве проекта вы его не увидите), где находятся все исходники mbed os, и куда сохраняется скомпилированный проект.
Файлы прошивки и прекомпилированные исходники находятся непосредственно в папке проекта.
Это всё, что нам нужно знать о том, что где хранится. Перейдем непосредственно к созданию проекта.
Создание проекта.
Вкратце о создаваемом проекте. По очевидным причинам, я принял решение создать проект для платы, которая не включена в состав поддерживаемых ARM online IDE, а именно STM32F4DISCOVERY.
В мире встраиваемых систем принято создавать демонстрационные проекты с миганием светодиодов. Мы этого делать не будем – это уже просто и неинтересно. PlatformIO подразумевает несколько типов проектов: cmsis, hal, rtos, и так далее. Так как речь сейчас идет об arm mbed os, то есть об операционной системе, создадим проект именно для rtos.
В проекте мы создадим и запустим три задачи (Task): первая будет выполнять перемножение массивов типа float (у нас же процессор Cortex-M4F, так воспользуемся FPU), вторая задача… ну ладно — мигать светодиодами (=)), а третья – определять степень загруженности процессора.
Открываем VS Code. Первым делом откроется окошко PIO Home. Выбираем “New Project”.
В окне “Project Wizard” указываем название проекта (у нас пусть это будет “armmbed_F407_CPU_usage”), и выбираем плату в выпадающем списке “Board”. Для читателей, планирующих использовать материал при написании софта для своих авторских плат: да, привязка к конкретной плате, но все ноги и периферию можно перенастраивать. Далее я пару слов об этом скажу, не спешите расстраиваться. Итак, Board.
Выбираем STM32F4DISCOVERY, и переходим в окно “Framework”. Тут у нас несколько вариантов.
Так как мы условились использовать arm mbed os, то очевидно, что здесь выбираем вариант “mbed”. Жмем “finish” — готово. Мастер маленько подумает, и откроет свежесозданную болванку проекта. Взглянем на это.
Как я уже упоминал выше, в проекте всего две папки по умолчанию: lib (пустая) и src, содержащая единственный файл main.cpp. Всего исходного кода, напомню, здесь мы не увидим. Но тем не менее, мы имеем возможность использовать весь функционал arm mbed os. Чтобы использовать rtos, мы должны добавить флаг сборки в файл “platformio.ini”:
Вообще, конфигурационный файл заслуживает отдельного рассмотрения. Мне этот подход напомнил TIRTOS/SYSBIOS от Texas Instruments с их конфиг-файлом .cfg, хоть в arm mbed все и проще намного. В конфигурационном файле можно декларировать многое — от аппаратных ресурсов до флагов сборки и отладки. К примеру, вот состав простейшего конфигурационного файла:
А это — конфиг-файл нашего примера в окончательном его виде:
Так что здесь есть что осваивать на досуге.
Итак, начинаем приводить проект в тот вид, который нам необходим.
Я буду приводить код блоками, и пояснять, что в нем происходит. Для начала, мы должны включить в исходник заголовочные файлы “mbed.h” и “rtos.h”. Думаю, понятно, зачем.
Функция “main” примет следующий вид:
Сначала мы создаем объекты класса “Thread”, то есть по сути, наши задачи (Task, Thread), которые будут нам обеспечивать определенный функционал.
Если кто-то обратил внимание, то следующей строкой значится
Это задача “idle” — то есть задача с пониженным приоритетом, которой отводится только то время, которое остается у процессора после выполнения задач с нормальным и повышенным приоритетами. Ну, в нашем случае эта задача останется голодной, так как у процессора не останется на нее времени. Я привел это здесь просто для примера.
Далее мы запускаем задачи по очереди методом “start”, передавая ему ссылки на функции задач, а именно на то, что будет выполняться в процессе. Это “ledblink” — шморгалка, “cpu_usage” — подсчет загрузки CPU, и самая тяжелая — “math_thread”, выполняющая перемножение массивов.
Посмотрим по очереди на каждую из задач. С “ledblink” все просто.
Мы поочередно меняем состояние вывода со светодиодом на противоположное, и вызываем задержку в 500 мс. Кстати, декларация “myled1” выглядит так:
Обратим теперь свое внимание на задачу “cpu_usage”.
Здесь уже все несколько сложнее. Вообще, дабы не выдумывать велосипед, я использовал готовую библиотеку, написанную одним веселым парнем еще в 2014 году для arm mbed, которая так и называется: CPU_Usage. Взять ее можно по ссылке, там же приводится краткое ее описание. Библиотека использует таймер (мы видим объект класса Timer tim). Сначала вызывается конструктор класса “cpu”, затем поочередно методы “working” (начало работы), и “update” — вычисление загрузки процессора в процентах.
Пожалуй, сейчас самый подходящий момент для демонстрации. Покажу скрин из режима отладки.
Слева вверху видим значение “value” = 95. Значит, процессор в тот момент был загружен на 95%. Вообще, по результатам эксперимента это значение при выполнении одних и тех же задач варьировалось от 87 до 98%.
Кстати, почему я демонстрирую скриншоты из дебаггера, а не из терминала? Все просто, у меня под рукой нет переходника UART-USB, поэтому я не могу использовать UART терминал (вот эта функция “pc.printf()” — это как раз вывод по UART, pc — объект класса Serial).
И последняя, и самая прожорливая для процессора – задача “math_thread”. Посмотрим на нее – сначала в “голом” виде, затем немножко дополним плюшками arm mbed.
Когда я придумывал, чем поднагрузить процессор, перемножение массивов сразу пришло мне на ум. И я вспомнил ситуацию, как заказчик, для которого я кое-что делал удаленно (и отлаживал тоже удаленно, через полмира), кричал мне в скайп: “Вы же программист, так загрузите процессор! Он же совсем холодный, сделайте так, чтобы он нагрелся!”. Давайте теперь уже таки сделаем так, чтобы наш MCU нагрелся. =)
И я решил перемножать массивы не последовательно, а генерировать их индексы с помощью генератора случайных чисел. И тут мне снова на помощь пришла одна замечательная математическая библиотека: alglib. Она охватывает огромный пласт математического функционала, и взять ее можно здесь. Весь огромный пласт функционала мы использовать, конечно же, не будем, а воспользуемся небольшим кусочком.
Если посмотреть на задачу-вычислитель произведения, то мы увидим там два вызова “RandomMassIndex()”. Это как раз функция, возвращающая значение в диапазоне (у нас диапазон ограничен числом элементов массивов).
Итак, что мы здесь делаем. Сначала инициализируем структуру “ae_state” (она используется для внутренних нужд), а затем – просто вызываем метод “ae_randominteger”, которому передаем ссылку на нашу структуру, и диапазон, в котором мы хотим получить сгенерированное случайное число (у нас это 0..18). Эта цифра должна быть меньше максимально генерируемого значения. Число элементов массива у нас – 20 (0..19), и максимальное число равно 19. Так что нам в качестве граничного аргумента 18 подойдет как нельзя лучше.
Кстати, можно посмотреть на результаты вызова этой функции.
Вверху слева – сгенерированные случайные индексы массивов, “rand_num_dmassi1” и “rand_num_dmassi2”. 13 и 12.
Прогоним еще один цикл, посмотрим – изменятся ли.
11 и 17. Изменились. Значит, все работает.
Раз уж мы заговорили об анализе ресурсов (в частности, об использовании процессорного времени), то немного уделим времени памяти и приоритетам задач. В arm mbed os реализован целый класс rtos::Thread для этих нужд.
Прямо в задачу “math_thread” добавим следующие строки:
Здесь (и выше) я использовал ключевое слово volatile – чтобы переменную можно было отслеживать.
Итак, сначала мы получаем ID задачи для дальнейшего использования. Затем вызываем методы для определения стека задачи и ее приоритета. Приоритет можно менять на ходу – в некоторых применениях это востребовано.
Смотрим.
Видим, что размер стека задачи равен 4096 байт, а приоритет – osPriorityNormal. Нормальный, в общем, приоритет.
Кроме этого, мы можем оценить степень используемости, размер неиспользованного и использованного стека. Прямо в main добавляем:
И после запуска задач:
Здесь вызываются четыре метода. “Stack_size()” возвращает размер стека задачи (аналогично тому, что мы оценивали чуть ранее), “max_stack()” возвращает размер максимально использованного в процессе выполнения, “free_stack()” возвращает размер свободного места, а “used_stack()” — размер использованного. Возвращаемые значения – в байтах. Для всех трех наших задач эти величины будут одинаковыми.
Посмотрим, что нам покажут по телевизору в дебаггере.
Как видим, от 4096 байт мы откушали совсем немного – всего 64 байта, и имеем в запасе еще аж 4032 байта.
Пожалуй, на этом с экспериментами и анализом мы закончим – я и так заигрался.
Да, что еще хотел сказать по поводу авторских плат. Сейчас кто-то может сказать, мол, вот – взял F4Discovery, на ней поигрался в свое удовольствие, а у меня вообще плата самодельная, и светодиоды вообще висят на других ногах, и в целом, я хочу SPI на ней поднять. Так вот, в репозитории armbed, в папке “targets” (выбираем дальше уже свои вполне конкретные MCU – их там тьма), в директориях каждого микроконтроллера, есть чудесные хидеры с названиями “PinNames.h”, “PeripheralPins.h” и “PeripheralNames.h”. Редактируя эти файлы, можно добавлять/редактировать/удалять периферию.
На этом, пожалуй, я остановлюсь. Больше примеров для различных применений arm mbed (в том числе и не-rtos, а просто bare metal) можно клонировать или скачать архивом здесь.
Ссылку на архив (Google диск) с нашим созданным примером прикрепляю к материалу, а полный исходный код размещаю ниже под спойлером — для полноты охвата всей картины. Если что – добро пожаловать на почту subdia.subdia@gmail.com.
Спасибо за внимание, всем удачного дня и хорошего настроения.























