История о моем опыте использования операционной системы реального времени (ОСРВ) Zephyr для устройства на базе микроконтроллера STM32F7-Discovery. В статье: Привет, Хабр, меня зовут Илья. Я студент выпускного курса университета и параллельно прохожу стажировку на позицию embedded-разработчика в компании Третий пин. Мой приход совпал с началом изучения операционной системы реального времени Zephyr. Чтобы не делать исследование на пустом месте, мне и другим стажерам предложили придумать небольшой проект, где можно использовать эту операционную систему. Мы остановились на идее устройства для отладки оборудования, когда отсутствует возможность подключения к компьютеру. Устройство позволяет считывать, хранить и отображать логи тестируемого устройства на дисплее или передавать их на компьютер по Ethernet. Проект получил внутренне название Logovoz. Прототип решили делать на STM32F7-Discovery. О том, что получилось планирую рассказать в следующих статьях. Сегодня – про сам Zephyr. Zephyr — это сравнительно новая операционная система реального времени с прицелом на embedded и устройства интернета вещей. Она была разработана в 2015 году компанией Wind River Systems, автора другой популярной в авиационной и космической отраслях ОС — VxWorks. Операционная система реального времени – это такая операционная система, ключевым критерием которой, наравне с корректной работой, является время выполнения операций. Так, если в Windows программа отработает на милисекунду позже, пользователь может даже не обратить внимания, а в ОСРВ эта ситуация является недопустимой. Например, представьте, что будет, если контроллер подушки безопасности автомобиля отработает на пару секунд позже, чем нужно? Чем хорош Zephyr: Система не лишена минусов. В основном, они связаны с её относительной молодостью. Например, проблемы с документацией в разных версиях системы. Несмотря на это, система имеет активное комьюнити и активно развивается. Система во многом схожа с Linux. Как и Linux, Zephyr содержит menuconfig или guiconfig (то же самое, но с отдельным GUI, а не в консоли), которые конфигурируют программные части системы на основе файлов Kconfig. Это могут быть различные драйверы, поддержка сетевых функций и т.д. Для описания же аппаратной части используется структура device tree. С помощью неё конфигурируются диапазоны адресов регистров в памяти, периферия, линии прерываний и др. В качестве системы сборки Zephyr иcпользует CMake. Поэтому каждое приложение должно иметь CMakeList.txt в качестве точки входа системы сборки. Сборка проекта осуществляется с помощью West. Команды west упрощают настройку приложения. Например, написав программу, собрать её под STM32F746G-Discovery надо командой: Не меняя исходный код, программа под NUCLEO-F207ZG собирается командой: Подробнее про доступные команды можно узнать, вызвав подсказку: Для использования определенной версии Zephyr и подключения сторонних модулей используется файл манифеста west.yml. Если у вас есть отладочная плата и она поддерживается Zephyr — открываем статью Getting Started Guide на официальном сайте и проделываем 8 нехитрых пунктов для вашей ОС. Для выполнения 7 пункта придётся найти файл Kconfig.defconfig и в нём посмотреть название отладки в параметре BOARD.  Расположение для STM32F746G-Discovery:
zephyr/boards/arm/stm32f746g_disco/Kconfig.defconfig  И, voila, вы гордый обладатель отладки с мигающим светодиодом. Если вашей платы нет в списке, то вам открывается дополнительный уровень сложности в игре «запусти зефир», где нужно описывать собственную борду, либо пользоваться возможностью эмуляции плат в QEMU.  Zephyr содержит большую базу примеров с описаниями, где можно посмотреть реализованные в системе возможности вашей отладки. Если ваша плата поддерживает периферию из примера — запускайте его. После чего, основываясь на нём, можно собрать свой проект, который будет делать очень важные и полезные вещи или просто радовать глаз, мигая светодиодами. Для отладки приложения я пользовался связкой VSCode + marus25.cortexdebug. В документации приведена инструкция для использования Eclipse в
качестве IDE. Для хранения логов в проекте планировалось реализовать файловую систему на SD-карте. Смотрю в щедро предлагаемые мне системой возможности, но не обнаруживаю там поддержки SD.
Zephyr на момент версии 2.1 умеет работать с SD-картами SDHC от 2 до 32 Гб ёмкости через SPI. Для работы с ними есть примеры, инструкция, всё замечательно. Хорошо, тогда почему же моя отладка не поддерживает работу с SD в Zephyr? Смотрю reference manual на stm32f7 и в разделе SDMMC нахожу строку. Одно из преимуществ Zephyr – это активное комьюнити. Гугл в помощь, и вот найдены братья по несчастью, один из которых даже написал свой драйвер работы с SD. Написанный драйвер — отлично, но есть нюанс. На рисунке представлена модель драйверов в Zephyr. Для использования конкретной реализации к ней надо обратиться через обобщенный API. Конечная цель работы с SD картой – взаимодействовать с ней, как с файловой системой. Такой интерфейс предоставляется через подсистему disk. Директория с этой подсистемой содержит как обобщенный API, так и API, предоставляющие доступ к файловой системе в ОЗУ, во флеше или на SD картах через SPI. Соответственно, надо добавить сюда свой интерфейс, который будет обращаться к реализации драйвера работы с SD. Берём готовые реализации интерфейсов, смотрим, как там всё сделано, и пишем что-то подобное. При написании драйверов и интерфейсов рекомендуется использовать язык Си, а также макросы, которые применяются в подобных файлах Zephyr. В конце создания API не забываем вписать о нём информацию в файлы CMakeList.txt и Kconfig, чтобы драйвер можно было собрать и включить в системе. В итоге, на карточку памяти гордо записан текстовый файл с приветствием миру. Работа с любыми новыми возможностями Zephyr заключается в том, что ты смотришь документацию на них и примеры. Потом пытаешься запустить сэмпл или повторить его. То же самое я пытался проделать и с файловой системой, но столкнулся с отсутствием файлов при сборке проекта. В результате поиска проблемы оказалось, что часть файлов есть в каталоге Zephyr с подсистемами, а часть файлов берётся из стороннего репозитория и подключается в качестве модуля в файле манифеста. Это касается случаев, когда вы сами записываете требуемые модули, а не пользуйтесь готовым файлом со всеми доступными сторонними репозиториями. В поисках определенной функциональности можно наткнуться на ссылки более старых версий Zephyr, а при переходе в документации на свежий релиз, оказывается, что этого описания уже нет или оно полностью поменяло расположение. При работе возникало ощущение, что авторы сами не до конца понимают, где должен быть тот или иной раздел документации, поэтому он сменяет местоположение от версии к версии.  Бывают и другие досадные моменты. В мажорном обновлении с Zephyr 1.14 до 2.0 сменилась такая незначительная деталь, как спецификация device tree. В итоге поменялся формат статуса в файлах .dts c «ok» на «okay». Вроде бы мелочь, но при создании собственной платы и переходе на другую версию, проект не будет собираться. Поэтому если вы работаете на определенном релизе Zephyr, внимательно следите и за версией документации. 
 В попытках запустить RTC (Real Time Clock) на плате, я находил, что драйвер часов реального времени был, но потом его не стало. Неприятная ситуация. Позже оказалось, что его функциональность осталась, но была переименована и получила интерфейс Counter. Воспользовавшись примерами, запустить RTC оказалось несложно. И он даже работал. До первого reset-а. А вот потом обнулился, хотя суть часов реального времени и заключается в том, чтобы не сбрасываться во время reset-а. Это могло произойти из-за того, что в отладку нельзя подключить батарейку и проверить работу часов с ней, а в Zephyr всё на самом деле прекрасно работает. Помогла возможность подсмотреть реализацию RTC в HAL. Те драйверы, которые удалось пощупать, были написаны с применением LL. Не найдя чего-то в нужном драйвере, можно узнать реализацию в HAL и дописать это. Выяснилось, что при инициализации часов в системе, сбрасываются регистры RCC. Не делая этого при reset-е можно оставить нетронутыми значения RTC и он будет работать, как и должен. Вторым найденным недостатком в реализации RTC оказалось отсутствие функций выставления времени и даты. Их можно считать, но каждый раз отсчитывать время от 2000 года оказалось как-то неудобно. Поэтому снова смотрим в HAL, вдохновляемся и добавляем реализацию сеттеров вместе с требуемым интерфейсом. Стоит ли пробовать Zephyr? Кратко – да. Zephyr действительно поддерживает много фишек «из коробки». Достаточно сделать пару кликов в guiconfig и вот в проекте появляется поддержка UART, SPI, Ethernet. Посмотрел пример, повторил, изменил, оно ещё и работать будет. Возможность не переписывать исходный код при переезде на другую плату тоже подкупает. Однако, я работал с отладкой, изначально поддерживаемой в системе, и рассказать про процесс самостоятельного описания платы, с какими сложностями можно столкнуться в этом случае, не могу. Что касается встреченных мною трудностей, большинство из них вытекают из того факта, что система новая и поддержка некоторых функций ещё просто не сделана.  Если у вас был опыт работы с Zephyr, поделитесь им в комментариях. Embedded-программист