Как стать автором
Обновить

Proxy MCAL для Микроконтроллера

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров2.1K

"Кодовая база только в том случае модульная, если из неё одновременно собираться код для множества разных платформ."

"Переносимая кодовая база - это ваш плацдарм для будущих разработок."

Пролог

Каких только микроконтроллеров мне не приходилось программировать за 12 лет: AVR, STM8, MSP430x, STM32, NXP (LPC2148), Xilinx MicroBlaze, NIOS II, MDR32, ESP32, MIK32, SPC58NNx, сс26x2, NRF53, Artery AT32, YTM32, FlagChip FC7300x. В сумме 16 семейств микроконтроллеров. Все они разные, как снежинки.

Могу с уверенностью сказать, что любая прошивка для микроконтроллера зиждется на базовом системном коде. Это код настройки тактирования CLOCK, EEPROM, GPIO, I2S, NVS, PWM, SPI, TIMER, WATCHDOG, ADC, DAC, EXT_INT, I2C, INTERRUPT, PDM, RTC, SPIFI, UART, WDG, CAN, DMA, FLASH, I2S, PIN, SDIO, SWD, USB и прочего. Всё это называют разными словами: HAL, MCAL, EHAL, System Software, SPL и прочее. Чисто ради определенности выберем название MCAL (Microcontroller Abstraction Layer).

Любое приложение на микроконтроллере строится поверх MCAL.

При этом каждый производитель микроконтроллеров поставляет бесплатно свою открытую реализацию HAL, а иногда и сразу две (HAL или LL)

Vendor

Название HAL

язык реализации

Artety

AT32F435_437_Firmware_Library_EN_V2.1.5

C, ASM

Nordoc

nrfx-v2.1.0

С

YunTu

YTM32Bx_SDK

C

STm

STM32F4xx_HAL_Driver

С

STm

SPL

C

STm

libopencm3

C

TI

simplelink_cc13x2_26x2_sdk

С

Espressiv

ESP-IDF

C

Zephyr

Zephyr HAL

С

Миландр

Standard Peripherals Library

C

Mikron

MIK32_HAL

C

Элвис

ELIoT1_SDK

..

и прочие

...

...

Многие компании пишут реализацию HAL сами. Вплоть до регистров процессора. Каждый называет функции получившегося HAL как хочет.

В чем проблема?

Проблема в том, что при продолжительной разработке на одном семействе микроконтроллеров получается так, что приложение намертво привязано к конкретному семейству микроконтроллеров и его SDK, так как напрямую использует HAL от вендора.

Это особенно явно проявляется в таких случаях, когда надо переносить прошивку на другой микроконтроллер.

В таких случаях получается, что приходится заново писать всю прошивку, всё приложение, драйвера всех ASIC-ов. Переписывать каждый файл. Драйвер светодиодов, драйвер кнопок, драйвер ASICов c I2C SPI управлением. В общем всё переписывать. Поменяли MCU и пришлось переписать все файлы в репозитории. Нормально так да?

Определения

--Прототипом функции в языке Си называется объявление функции, не содержащее тела функции, но указывающее имя функции, типы аргументов и возвращаемый тип данных. В то время как определение функции описывает, что именно делает функция, прототип функции может восприниматься как описание её интерфейса (API).

--binding-и - это функции, которые просто вызывают другие функции. В переводе на кухонный язык - программный клей. Binding-и нужны только лишь для того, чтобы связать два совершенно разных API.

--proxy - уполномоченный, сделанный по доверенности, совершенный по доверенности, выданный по доверенности.

--лазанья-код – это идиоматическое выражение, используемое в IT-среде для обозначения кода, который имеет слишком много слоев абстракции, слои сильно взаимозависимы, и любое изменение в одном слое может негативно повлиять на весь проект.

--CLI - Интерфейс командной строки. Это способ взаимодействовать с программой обменом текстами по принципу запрос-ответ.

--ASIC- специализированная микросхема, которая выполняет только одну специфическую задачу аппаратно.

--SDK ( software development kit) — комплект для разработки программного обеспечения, набор инструментов для разработки программного обеспечения, объединённый в одной папке, обычно содержит комплект необходимых исходников, статических библиотек, компилятор, отладчик, иногда — интегрированную среду разработки.

По каким причинам обычно приходится переносить прошивку на совершенно другой микроконтроллер?

1--Прежний микроконтроллер морально устарел. Появились в продаже те, что производительнее, экономичнее и главное - дешевле.

2--Изначального микроконтроллера просто больше нет в продаже. Случился кризис полупроводников.

2--Прежний микроконтроллер невозможно больше купить в нужных количествах

3--Государство-производитель микроконтроллера наложило Санкции и Эмбарго на поставки своего микроконтроллера на Вашу территорию.

4--В новой версии продукта понадобился новый функционал (например Bluetooth 5.3), которого не было в предыдущем микроконтроллере на PCB. Всё. Меняем МК.

6--У вас в офисе может банально не хватить отладочных плат с вашим основным микроконтроллером и тут для отладки, условно, акселерометра подойдет какая-нибудь широко распространенная в продаже отладочная плата на основе другого уже микроконтроллера. И тут-то вы пожелаете переносимости прошивки на другое железо. Это чисто организационный момент.

7--Даже если Вы работаете только с одним семейством MCU, то рано или поздно Вы столкнётесь с багом, который захочется попробовать воспроизвести на другом микроконтроллере. И тут-то Вам понадобится собрать, то же приложение на , условно, STM32 или ESP32.

8--Менеджер проекта получил взятку (подарок) от производителя микроконтроллеров, чтобы в очередной версии серийного продукта компании был именно микроконтроллер от этого производителя (который дал взятку). Это не шутка, я такое видел.

Как видите, причин для миграции проекта на новое железо может быть сколько угодно. То есть существует электронный продукт (условно контроллер CNC станка или датчик дыма), а железо под ним меняется в зависимости от конъюнктуры. Товар пользуется спросом. Производство останавливать нельзя. Схемотехники ставят другой микроконтроллер, чтобы продолжать производство. Программистам надо приспосабливаться к программированию другой архитектуры.

Как решить проблему миграции FW на новое железо?

Очевидно, что нужен некоторый уровень абстракции для связи приложения и базового System Software. Нужен proxy MCAL, посредник для вызова API функций от того SDK, который поставляет данный конкретный вендор.

Переводя на кухонный язык, нужны одни и те же единообразные функции, которые будут по-своему реализованы для каждого отдельного микроконтроллера и семейства микроконтроллеров. Только и всего. Эти функции могут как сами писать в сырые регистры, так и вызывать готовый HAL от вендора (binding-и). Это зависит лишь от того, сколько у Вас свободного времени. Если много, то разбираемся с регистрами. Если мало, то пишем программный клей на функции вендора. Понимаете?

Требования к MCAL?

1--Прежде всего proxy MCAL должен обладать таким свойством, как полнота. Должно быть много функций. Неприлично много функций. Функции на все случаи жизни. Если функция не нужна, то её просто надо делать weak функцией и компоновщик её сам выкинет из бинаря.

2--Принятие. MCAL должен признаваться коллегами. Все должны его дорабатывать. Повлиять на его создание.

3--Простота. Такой MCAL должен быть простым и интуитивно понятным. Все функции помещаются на одном экране. Там не надо очевидных комментариев. По модульным тестам и так будет понятно как им пользоваться. А модульные тесты будут найдены утилитой grep.

4--Масштабируемость.

5--Универсальность

6--Переносимость, независимость от платформы. Чтобы можно было даже на PC собирать код и, чтобы просто вызывались пустые функции.

7--MCAL должен управляться через UART-CLI. Чтобы можно было верифицировать каждую отдельную функцию в ручном режиме во время исполнения прошивки.

8--Для MCAL должны быть модульные тесты. Тесты нужны не только для испытаний проекта. Тесты служат, как документация к коду.

Вот пожалуй и всё.

Пример реализации MCAL

Рассмотрим на примере той функции которая точно есть в каждой прошивке: GPIO_Set. Это gpio_logic_level_set на stm32

bool gpio_logic_level_set(Pad_t pad, GpioLogicLevel_t logic_level) {
    HAL_GPIO_WritePin(Port2PortPtr(pad.port), 1 << pad.pin, 
                      (GPIO_PinState)logic_level);
    return true;
}

Версия gpio_logic_level_set на nrf5340

bool gpio_logic_level_set(Pad_t Pad, GpioLogicLevel_t logic_level) {
    bool res = false;
    res = gpio_is_valid_pad(Pad);
    if(res) {
        nrf_gpio_pin_write((uint32_t)Pad.byte, (uint32_t)logic_level);
    } 
    return res;
}

Версия gpio_logic_level_set на Artery AT32

/*can be called from isr*/
bool gpio_logic_level_set(Pad_t Pad, GpioLogicLevel_t logic_level) {
    Pad_t pad = {0};
    (void)pad;
    pad.byte = pad_num;
    gpio_type* GPIOx = Port2PortPtr(Pad.port);
    if(GPIOx) {
        switch((uint8_t)logic_level) {
        case GPIO_LVL_HI: {
            GPIOx->scr = 1 << Pad.pin;
        } break;
        case GPIO_LVL_LOW: {
            GPIOx->clr = 1 << Pad.pin;
        } break;
        }
    }
    return true;
}

Как видите, название и аргументы у функции одни, а тело функции разное, в зависимости от микроконтроллера. Всё просто и лаконично. Это и есть binding-и.

Достоинства MCAL

Создание MCAL даст вам ряд очень важных достоинств:

1++Переносимость приложений. Вы пишите приложение под MCAL. Потом просто пишите binging-и под другой проц и получаете приложение на другом микроконтроллере. Красота!

2++Можно вызывать модульные тесты базового ПО через MCAL. Вы пишите модульные тесты только один раз для MCAL. Далее Вы тестируете HAL от вендора через готовые тесты для MCAL.

3++Управлять базовым ПО через CLI. Вы пишите CLI только один раз. Для MCAL. Вся остальная периферия уже запускается из CLI через binging-и на MCAL.

4++Вы можете разрабатывать и отлаживать драйвер SPI-Flash памяти вообще на любом микроконтроллере. Благодаря MCAL драйвер любого ASICа будет одинаковый для всех микроконтроллеров.

Недостатки Proxy MCAL

1--В каждом действии появляется ещё один вызов функции. Ваш код будет исполняться медленнее на 10-20 тактов процессора. Только и всего. Хотя непременно найдется кто-то кто назовет это "лазанья-код".

2--Надо очень осторожно подбирать имена для функций Proxy MCAL. Иной раз создашь функцию system_init(), а потом появится вендор, который в своем SDK тоже создал функцию system_init. Код SDK ты, естественно, менять не имеешь права. Это же чужой код. Поэтому приходится выпускать новый релиз proxy_mcal с переименованной функцией mcal_system_init. Та же история с названиями для перечислений.

Аналогии

На самом деле концепция proxy MCAL стара, как мир.

1-- Именно эту задачу решают Portable Operating System Interface (POSIX). Таким образом разные OS имеют один и тот же интерфейс.

2--Есть такой репозиторий Zephyr Project. У Zyphyr свой API для HAL. Поддержка огромного количества MCU. Zephyr весьма универсальный. Однако для пуска Zephyr вам придется переходить на CMake, Kconfig, DeviceTree, что не всегда возможно.

3--Существует AUTOSAR методология для создания переносимых прошивок. Там уже есть готовые названия эталонных функций для MCAL. Однако AUTOSAR подразумевает существенную оплату за присоединения к консорциуму и получение исходников. Коллега как-то отметил, что покупка универсального драйвера сторожевого таймера из AUTOSAR стоит 30k EUR. Это то, что любой русский программист МК пишет за три дня.

Вам не обязательно покупать исходники AUTOSAR, чтобы добиться переносимости. Вы можете сами внутри своей организации договориться о том как будет выглядеть ваш внутренний proxy MCAL. Хотя конечно было бы лучше, если был бы некоторый индустриальный стандарт для того, чтобы единообразно называть функции для HAL для доступа к GPIO UART SPI и прочее между всеми производителями микроконтроллеров в стране: Миландр, Микрон, ЭЛВИС, НИИЭТ, Байкал, ДЦ Союз, Цифровые решения, Ангстрем, НИИСИ РАН и прочие.

Если Вы получили сорцы от другой организации и там условно уже есть proxy_MCAL1, а у вас давно работает proxy_MCAL2, то это тоже не беда. Вы просто напишете binding-и c proxy_MCAL1 на proxy_MCAL2 или наоборот. И приложение заведется само собой.

Итоги

Задача переносимости прошивок с микроконтроллера на микроконтроллер полностью решается через создание уровня абстракции под названием proxy MCAL. Благодяря MCAL миграция на новый MCU станет простой задачей.

При переходе на новые микроконтроллер вам надо всего-навсего написать binding с MCAL на HAL от вендора. Это простая задача, которую можно получить даже студенту. И, таким образом, ваш прибор заработает, как и прежде на новом SoC-е, как ни в чем не бывало.

Вот моя реализация proxy_mcal https://github.com/aabzel/proxy_mcal . Специально выкладываю ее для опровержения.

Словарь

Акроним

Расшифровка

HAL

Hardware Abstraction Layer

MCAL

Microcontroller Abstraction Layer

POSIX

Portable Operating System Interface

SPL

Standard Peripherals Library

CLI

command-line interface

FW

Firmware

API

application programming interface

SoC

System-on-a-Chip

PCB

Printed circuit board

ASIC

Application Specific Integrated Circuit

ПО

Программное обеспечение

ASM

Язык ассемблера

Ссылки

название

URL

1

Моя версия proxy MCAL

 https://github.com/aabzel/proxy_mcal

2

Что Должно Быть в Каждом FirmWare Pепозитории

https://habr.com/ru/articles/689542/

3

Архитектура Хорошо Поддерживаемого Программного Компонента

https://habr.com/ru/articles/683762/

4

Атрибуты Хорошего С-кода (Хартия Си Программистов)

https://habr.com/ru/articles/679256/

5

Примеры абстракций в технике и повседневности

https://habr.com/ru/articles/715146/

6

Архитектура Xорошего Кода Прошивки (Массив-Наше Всё)

https://habr.com/ru/articles/816589/

7

Диспетчер Задач для Микроконтроллера

https://habr.com/ru/articles/757000/

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
У Вас в организации есть свой proxy MCAL?
13.33% да2
86.67% нет13
Проголосовали 15 пользователей. Воздержались 6 пользователей.
Теги:
Хабы:
Всего голосов 13: ↑11 и ↓2+14
Комментарии42

Публикации

Работа

Программист С
33 вакансии
DevOps инженер
32 вакансии

Ближайшие события