Добрый день! Не секрет, что стандартные примеры, работающие из коробки, — штука неплохая: загрузил на плату и наслаждайся. Это удобно для быстрого ознакомления. Но затем, если мы намерены сами что-то создать, нужно разбирать код примера, читать документацию, писать свой код, долго отлаживаться… Хочется этот этап как-то упростить. По этой причине, я хотел бы рассказать о том, как мы сделали интеграцию стандартных примеров из библиотеки STM32Cube в Embox.

Итак, 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-микрофоны, миниатюрные микрофоны встроенные в плату), после чего через несколько секунд запись воспроизведется.

Видео с некоторыми примерами из BSP.


Pеализацию можно посмотреть здесь.

На этом все. Будем рады вопросам.

Комментарии (11)


  1. golf2109
    12.02.2018 21:13

    а можно в двух словах — в чем преимущество embox например перед FreeRTOS?


    1. alexkalmuk Автор
      12.02.2018 21:30

      Embox является POSIX совместимым, имеет сетевой стек, файловую систему и т.д… Это позволило, например, запустить pjsip на stm32f4-discovery. А на более больших платформах на Embox работает даже Qt embedded.


      1. golf2109
        12.02.2018 21:38

        а сетевой стек и файловая система входят в состав embox?


        1. alexkalmuk Автор
          12.02.2018 21:45

          Да, входят. Они изначально разрабатывались в составе Embox так, чтобы можно было настраивать под маленькие платформы.


      1. Dima_Sharihin
        13.02.2018 14:50

        Но ведь уже есть NuttX, в чем преимущество перед ней?


        1. abondarev
          13.02.2018 15:38

          Это конкурент, когда начинали Embox этот проект не был таким популярным.
          Оба проекта развивались параллельно. У нас более сильный упор был сделан на статическую сборку, на основе нашей оригинальной системы сборки, У NuttX упор был сделан, на запуск кода без MMU, то есть динамические механизмы. Автор (Грегори Нат) общался с нами в нашей рассылке, когда мы задумались над похожим механизмом. Так что, у каждого проекта есть сильные стороны, например, я не помню чтобы приведенные выше проекты (Qt, pjsip), запускались на NuttX, поправьте меня если я ошибаюсь. А имея несколько вариантов пользователь (разработчик) может выбирать, что ему ближе.


  1. imperror
    13.02.2018 11:43

    Уважаемый автор, если не сложно, не могли бы вы проверить работу порта USB_HS на вашей плате. У меня почему-то не работает ни хост не девайс. Спасибо.


    1. 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


    1. alexkalmuk Автор
      13.02.2018 16:31
      +1

      P.S. Кстати, там у них в исходниках есть макрос USE_USB_HS, который должен где-то определяться (Projects/STM32746G-Discovery/Applications/USB_Device/MSC_Standalone/readme.txt). Без него тоже не будет работать. Может в вашем случае он как раз и не определен оказался, кто знает…


      1. imperror
        13.02.2018 19:44

        Спасибо. Похоже на моей плате железный косяк с USB_HS.


        1. abondarev
          14.02.2018 13:04

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