Итак, Embox, как и любая ОС, предоставляет много приятных вещей, таких как потоки, управление памятью и т.д. STM32Cube, в свою очередь, обладает множеством примеров по использованию аппаратуры. Каждый пример представляет собой самодостаточный набор исходников с проектными файлами для MDK ARM, EWARM и SW4STM32 (что-то поверх Eclipse, вроде бы), т.е. нажал кнопку — все само собралось, на плату загрузилось и запустилось. Теперь вопрос: как же интегрировать такой пример в инфраструктуру операционной системы? Посмотрим более детально как устроены примеры.
Для желающих сразу посмотреть на то, что вышло, и пропустить технические подробности, прыгать сюда :)
Я буду опираться на пример для LCD-экрана и BSP для отладочной платы STM32F746G-Discovery. Рассмотрим пример для экрана. Помимо проектных файлов в дереве исходников находятся readme.txt, Inc/ и Src/. Они-то нам и нужны, переходим в папку с исходниками и смотрим, что там лежит:
# ls Projects/STM32746G-Discovery/Examples/LTDC/LTDC_Display_1Layer/Src/
main.c stm32f7xx_hal_msp.c stm32f7xx_it.c system_stm32f7xx.c
Файл system_stm32f7xx.c имеется во всех примерах, в нем содержится инициализация встроенной флэш памяти, PLL, а также устанавливается SystemFrequency. Эти функции вызываются еще до main(). В Embox эти же настройки производятся при старте системы, поэтому этот файл мы просто не будем использовать. Идем далее, в файле stm32f7xx_hal_msp.c содержатся функции инициализации аппаратуры, специфичные для конкретной задачи. Это функции вида *_MspInit, которые определены изначально как WEAK, что позволяет их переопределить. Этот файл берем с собой в Embox. Далее, stm32f7xx_it.c — тут переопределяются обработчики аппаратных прерываний и исключений. И наконец, main.c — здесь все понятно — основная логика + некая инициализация, которую мы потом выбросим.
Перенос примера в Embox происходит с помощью скрипта import_stm32_cube_example.py.
После его запуска первым делом копируется в дерево исходников readme.txt, Inc/ и Src/. Теперь нужно сгенерить файл системы сборки Embox. Для этой цели используется шаблон, в который подставляется название примера, исходники, платформа (f7 или f4) и необходимые зависимости.
/* GENERATED FILE */
package stm32_PLATFORM_.cmd
@AutoCmd
@Cmd(name="_EXAMPLE_", help="")
@BuildDepends(third_party.bsp.stm_PLATFORM_cube.core)
module _EXAMPLE_ {
source "Src/embox_main.c"
depends _EXAMPLE__Lib
}
@BuildDepends(third_party.bsp.stm_PLATFORM_cube.core)
module _EXAMPLE__Lib {
@Cflags("-Wno-unused")
@IncludePath("$(ROOT_DIR)/platform/stm32_PLATFORM_/cmds/_EXAMPLE_/Inc")
_SOURCES_
depends third_party.bsp.stm_PLATFORM_cube.stm32_PLATFORM__discovery_bsp
depends third_party.bsp.stm_PLATFORM_cube.stm32_PLATFORM__discovery_components
depends third_party.bsp.stm_PLATFORM_cube.stm32_PLATFORM__discovery_utilities
}
Модули third_party.bsp.stmf7cube.stm32f7_discovery_bsp и third_party.bsp.stmf7cube.stm32f7_discovery_components содержат библиотеку BSP из Cube и разные вспомогательные библиотеки. Обычно в проектных файлах Cube эти исходники явно перечисляются, но у нас в системе они общие и подключаются одинаковым образом для любого примера все сразу. Эти библиотеки и все исходники примера будут скомпилированы в некоторую статическую библиотеку внутри Embox (в противном случае, нарушится линковка команд Embox с weak функциями Cube), поэтому для запуска примера будет сгенерирован файлик с одной лишь функцией main().
С системой сборки разобрались, теперь нужно как-то переделать обработчики прерываний из Cube, чтобы ОС их приняла. Формат обработчика прерываний в Cube void (*handler)(void), в то время как в Embox сигнатура другая. На примере обработчика DMA2_Stream7_IRQHandler в Embox это можно реализовать так:
static
irq_return_t embox_DMA2_Stream7_IRQHandler(unsigned int irq_nr, void *data) {
DMA2_Stream7_IRQHandler();
return IRQ_HANDLED;
}
А затем зарегистрируем эту обертку:
irq_attach(DMA2_Stream7_IRQn + 16, embox_DMA2_Stream7_IRQHandler, 0, NULL,
"DMA2_Stream7_IRQHandler");
Можно видеть, что названия обработчиков прерываний в Cube имеют вполне определенную структуру — *_IRQHandler (как и названия номеров прерываний — *_IRQn). Поэтому чтобы автоматически найти все обработчики прерываний для данного примера, мы сначала проходимся по файлу препроцессором cpp, и в полученном файле находим все *_IRQHandler. А далее, import_stm32_cube_example.py сгенерирует файл Src/embox_stm32f7xx_it_lib.c, в котором будут все необходимые обработчики прерываний и глобальная функция embox_stm32_setup_irq_handlers, вызов которой регистрирует обработчики прерываний.
Помимо прерываний от периферии нужно еще не забыть про аппаратный таймер, так как в обработчике таймера Cube (называется он SysTick_Handler) могут происходить действия типа заполнения аудио буферов. Поэтому обработчик первого уровня — это обработчик в Embox, который в конце уже вызовет SysTick_Handler.
Наконец, нужно исправить Src/main.c. Во-первых, функция main в примерах Cube всегда содержит 3 стандартных вызова — CPU_CACHE_Enable(), HAL_Init(), SystemClock_Config(). Эти функции и так вызываются при старте Embox, поэтому в примере они крайне вредны, поэтому автоматически оказываются закомментированы. И последнее, добавляется регистрация обработчиков прерываний, т.е. вызов вида
if (0 != embox_stm32_setup_irq_handlers()) {
printf("embox_stm32_setup_irq_handlers error!\n");
}
В итоге получается полноценный модуль ОС, который можно сразу же включить в конфиг, залить Embox на плату, и запускать как обычную команду из командной строки.
Для демонстрации всего вышеперечисленного я выбрал, как уже говорил, два примера — 1) Экран LCD 2) Пример с записью звука из BSP.
Для желающих поиграться с STMкой ниже даю ссылки и рецепты. Вероятно, тут пригодится статья о том, как собрать и запустить Embox.
Пример LCD на STM32F746G-Discovery
Переходим в папку с Embox, и добавляем пример:
# ./scripts/stm32/import_stm32_cube_example.py f7 ./build/extbld/third_party/bsp/stmf7cube/core/STM32Cube_FW_F7_V1.5.0/Projects/STM32746G-Discovery/Examples/LTDC/LTDC_Display_1Layer
Эта директория с длинным названием появится после того как вы первый раз соберете Embox, при сборке скачается архив с STM32Cube под F7 и cкопируется куда нужно.
Теперь в папке platform/stm32f7/cmds/ появится пример LTDC_Display_1Layer. Можете сами зайти в папку и убедиться если не верите :)
Добавляем его в конфиг conf/mods.config как stm32f7.cmd.LTDC_Display_1Layer. Пересобираем Embox и загружаем на стмку. В консоли minicom после загрузки Еmbox запускаем пример командой LTDC_Display_1Layer. На экране нарисуется картинка с девушками.
Пример со звуком на STM32F746G-Discovery (+ Touchscreen)
Переходим в папку с Embox, и добавляем пример:
# ./scripts/stm32/import_stm32_cube_example.py f7 ./build/extbld/third_party/bsp/stmf7cube/core/STM32Cube_FW_F7_V1.5.0/Projects/STM32746G-Discovery/Examples/BSP
Теперь в папке platform/stm32f7/cmds/ появится пример BSP. В этом примере продемонстрированы многие возможности STM32F7-Discovery.
Добавляем его в конфиг как stm32f7.cmd.BSP. Пересобираем Embox и загружаем на стмку. В консоли minicom после загрузки Еmbox запускаем пример командой BSP. После запуска вы можете выбрать нужный пример нажатиями на User Button (синий джойстик) и начать запись звука с микрофонов (MEMS-микрофоны, миниатюрные микрофоны встроенные в плату), после чего через несколько секунд запись воспроизведется.
Pеализацию можно посмотреть здесь.
На этом все. Будем рады вопросам.
Комментарии (11)
imperror
13.02.2018 11:43Уважаемый автор, если не сложно, не могли бы вы проверить работу порта USB_HS на вашей плате. У меня почему-то не работает ни хост не девайс. Спасибо.
alexkalmuk Автор
13.02.2018 16:25+1Я проверил USB_HS в режиме девайса. Для этого импортировал в Embox вот этот пример — Projects/STM32746G-Discovery/Applications/USB_Device/MSC_Standalone. Там алгоритм такой:
1) Вставляем SD карту.
2) Вставляем micro USB кабель в USB_HS (CN12) и включаем плату.
3) Подключаем кабель к PC. Должен появиться девайс.
Вот бранч в Embox, в котором этот пример работает — github.com/embox/embox/tree/stm32f7-usb-device-example. Темлейт: make confload-platform/stm32f7/usb-device
alexkalmuk Автор
13.02.2018 16:31+1P.S. Кстати, там у них в исходниках есть макрос USE_USB_HS, который должен где-то определяться (Projects/STM32746G-Discovery/Applications/USB_Device/MSC_Standalone/readme.txt). Без него тоже не будет работать. Может в вашем случае он как раз и не определен оказался, кто знает…
imperror
13.02.2018 19:44Спасибо. Похоже на моей плате железный косяк с USB_HS.
abondarev
14.02.2018 13:04По идее, чтобы однозначно ответить железный баг или нет, нужно запустить тот же бинарник. Поскольку могут быть разные компиляторы.
Вы проверили то что предложили выше? То есть, собрали и запустили на вашей плате?
golf2109
а можно в двух словах — в чем преимущество embox например перед FreeRTOS?
alexkalmuk Автор
Embox является POSIX совместимым, имеет сетевой стек, файловую систему и т.д… Это позволило, например, запустить pjsip на stm32f4-discovery. А на более больших платформах на Embox работает даже Qt embedded.
golf2109
а сетевой стек и файловая система входят в состав embox?
alexkalmuk Автор
Да, входят. Они изначально разрабатывались в составе Embox так, чтобы можно было настраивать под маленькие платформы.
Dima_Sharihin
Но ведь уже есть NuttX, в чем преимущество перед ней?
abondarev
Это конкурент, когда начинали Embox этот проект не был таким популярным.
Оба проекта развивались параллельно. У нас более сильный упор был сделан на статическую сборку, на основе нашей оригинальной системы сборки, У NuttX упор был сделан, на запуск кода без MMU, то есть динамические механизмы. Автор (Грегори Нат) общался с нами в нашей рассылке, когда мы задумались над похожим механизмом. Так что, у каждого проекта есть сильные стороны, например, я не помню чтобы приведенные выше проекты (Qt, pjsip), запускались на NuttX, поправьте меня если я ошибаюсь. А имея несколько вариантов пользователь (разработчик) может выбирать, что ему ближе.