Как мы запускали стандартные примеры из библиотеки STM32Cube

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

    На этом все. Будем рады вопросам.
    Embox 190,24
    Открытая и свободная ОС для встроенных систем.
    Поделиться публикацией
    Похожие публикации
    Комментарии 11
    • +1
      а можно в двух словах — в чем преимущество embox например перед FreeRTOS?
      • +2
        Embox является POSIX совместимым, имеет сетевой стек, файловую систему и т.д… Это позволило, например, запустить pjsip на stm32f4-discovery. А на более больших платформах на Embox работает даже Qt embedded.
        • +1
          а сетевой стек и файловая система входят в состав embox?
          • +1
            Да, входят. Они изначально разрабатывались в составе Embox так, чтобы можно было настраивать под маленькие платформы.
          • +1

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

            • +1
              Это конкурент, когда начинали Embox этот проект не был таким популярным.
              Оба проекта развивались параллельно. У нас более сильный упор был сделан на статическую сборку, на основе нашей оригинальной системы сборки, У NuttX упор был сделан, на запуск кода без MMU, то есть динамические механизмы. Автор (Грегори Нат) общался с нами в нашей рассылке, когда мы задумались над похожим механизмом. Так что, у каждого проекта есть сильные стороны, например, я не помню чтобы приведенные выше проекты (Qt, pjsip), запускались на NuttX, поправьте меня если я ошибаюсь. А имея несколько вариантов пользователь (разработчик) может выбирать, что ему ближе.
        • 0
          Уважаемый автор, если не сложно, не могли бы вы проверить работу порта USB_HS на вашей плате. У меня почему-то не работает ни хост не девайс. Спасибо.
          • +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
              P.S. Кстати, там у них в исходниках есть макрос USE_USB_HS, который должен где-то определяться (Projects/STM32746G-Discovery/Applications/USB_Device/MSC_Standalone/readme.txt). Без него тоже не будет работать. Может в вашем случае он как раз и не определен оказался, кто знает…
              • 0
                Спасибо. Похоже на моей плате железный косяк с USB_HS.
                • 0
                  По идее, чтобы однозначно ответить железный баг или нет, нужно запустить тот же бинарник. Поскольку могут быть разные компиляторы.
                  Вы проверили то что предложили выше? То есть, собрали и запустили на вашей плате?

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

            Самое читаемое