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

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

Уровень сложностиПростой
Время на прочтение9 мин
Количество просмотров7.2K
Всего голосов 13: ↑11 и ↓2+14
Комментарии33

Комментарии 33

Вместо

    MCAL_INIT         \
    HW_INIT           \
    INTERFACES_INIT   \

я предпочту

mcal_init();
hw_init();
interfaces_init();

Ваш подход исключает возможность кодо генерации препроцессором срр.exe из конфигов.

Что касается конфигов - понравилось как в esp-idf прикрутили kconfig.

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

Существует ли stand alone kconfig.exe для любых проектов без esp32?

На счет exe не уверен, но можно посмотреть раз, два

Kconfig считается устаревшей вещью.

не удивительно.

Ей на замену приходит DeviceTree

Посмотрю, спасибо, пока не сталкивался.

Вообще Kconfig считается устаревшей вещью. Ей на замену приходит DeviceTree.
Zephyr Project c Nrf5340 уже так и делают.

это говорит о том что массивы маленько тоже устарели. Им на смену уже вроде как пришли деревья (Tree) во многих применениях.

Вы видели stand alone DevTree компилятор для произвольных Win проектов?

Да и потом, язык DevTree очень-очень сложен для понимания. Из DevTree даже нет инструментов чтобы сгенерить graphviz код.

А язык Си простой, притом на нем еще и сам код пишут. Почему бы тогда на Си и сами конфиги не писать?

А язык Си простой, притом на нем еще и сам код пишут. Почему бы тогда на Си и сами конфиги не писать?

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

Внезапно Kconfig из зефира никуда не делся. Используется и то и то, только для разных целей

Существует ли stand alone kconfig.exe для любых Win проектов без Zephyr RTOS?

В доках Zephyr упоминается, что Kconfig это пережиток прошлого, который сообщество собирается со временем полностью заменить на DevTree 

В попытках прочитать примеры сильно пугает микс из snake_case и CamelCase

Про оформление кода у меня есть отдельный текст. Называется
51 Атрибут Хорошего С-кода

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

Тута у вас то ли ошибка, то ли неточность:

4

адрес в NVRAM

натуральное число

адрес может быть равным 0, который не входит во множество натуральных чисел.

А по факту - интересно, но довольно громоздко. Ну и не всегда конфиг известен при прошивке, ой не всегда. Иногда прошивка на одном посту а запись конфига вообще за 3 ккм где-то....

Во французской школе (см. Бурбаки) 0 включают во множество натуральных чисел.

Я чего-то думаю, что большинство читателей ХАБРа учились не во французской школе....

Да по памяти - все-же немецкая школа дала основные положения по натуральным числам.

Ccылка не открыватеся

Прошу прощения. Что-то где-то взглючило, и последняя буква в ссылке заменилась кракозябрами. А я не проверил :(
Правильная ссылка http://lib.ru/ANEKDOTY/non_pas.txt

Что это !?

/*Order matters!*/
define INIT_FUNCTIONS \
    MCAL_INIT         \
    HW_INIT           \
    INTERFACES_INIT   \
    PROTOCOLS_INIT    \
    CONTROL_INIT      \
    STORAGE_SW_INIT   \
    SW_INIT           \
    UNIT_TEST_INIT    \
    ASICS_INIT        \
    BOARD_INIT

Спрашивается, как узнать из этого кода какой порядок вызовов правильный?

И где тут те макросы которые были тут

#ifdef HAS_AES
    { .facility = AES, .name = "AES", },
#endif /*HAS_AES*/

#ifdef HAS_AD9833
    { .facility = AD9833, .name = "AD9833", },
#endif /*HAS_AD9833*/

#ifdef HAS_NOR_FLASH
    { .facility = NOR_FLASH, .name = "NorFlash", },
#endif /*HAS_NOR_FLASH*/

Чтобы такое неприличное шаманство не делать, первым вызовом идет запуск RTOS.

А последним на старте идет ожидание флагов инициализации всех задач периферии.

Wait_app_event(EVENT_GUI_TASK_READY
                 | EVENT_LEDS_TASK_READY
                 | EVENT_CAN_TASK_READY
                 | EVENT_INPUTS_TASK_READY
                 | EVENT_OUTPUTS_TASK_READY
                 | EVENT_NET_TASK_READY,
                 TX_AND, TX_WAIT_FOREVER);

Вся инициализация идёт максимально параллельно в асинхронных функциях. И тогда мистический порядок вызовов не нужен и массив не нужен.
А иначе будет не embedded дивайс, а черепаха с временем загрузки сравнимым с Windows.

Массивы удобны именно с точки зрения оформления кода, когда выровнены столбцы. И можно редактировать код блочными операциями.
А в статье именно это удобство и не упомянуто, а в коде даже игнорируется.

Что это !?

Это перечень элементов массива структур: указатель на функцию инициализации + имя функции.

Спрашивается, как узнать из этого кода какой порядок вызовов правильный?

Правильный порядок определяет программист формируя константный массив инициализации в коде на основе зависимостей между программными компонентами.

Правильно, программист.
А значит он должен долго дебажить все эти функции, пока не добъется правильного их порядка. Следовательно массив ему окажет медвежью услугу, превратив прямые вызовы в косвенные.

Факт в том, что массивы тоже иногда неуместны.

Правильно, программист.А значит он должен долго дебажить все эти функции, пока не добъется правильного их порядка. Следовательно массив ему окажет медвежью услугу, превратив прямые вызовы в косвенные.

Да есть такое.
Не всегда очевидна оптимальная последовательность инициализации системы.

Но к этой проблеме можно подойти с точки зрения дискретной математики.

Постановка задачи
Есть неупорядоченное множество компонентов. Просто набор строк (токенов).

start_pause,Writer,Log,Int,Clk,SysTick,Flash,Timer,GPIO,UART,Swd,ADC,AdcChannels,I2C,SPI,CAN,I2S,WatchDog,Pwm,DMA,DmaChannels,Mcu,DACsw,RS232,RS485,StringReader,CLI,iso_tp,UDS,DID,Relay,LedMonoInit,NVS,Flash_FS,Param,ADT,Heap,TIME,SuperCycle,task,Button,HwInit,UnitTest,Nau8814,board




У каждого компонента есть зависимости. Как правило несколько. У каких-то тривиальных компонентов нет зависимостей.

start_pause,Clk
Writer,UART
Log UART
Clk,
Flash
SysTick,Clk
Timer, Clk
GPIO,
UART, GPIO
Swd,GPIO...
ADC,AdcChannels,..
I2C,GPIO UART
SPI,GPIO UART
CAN,GPIO
I2S,GPIO DMA
WatchDog,
RS232,GPIO UART
RS485,GPIO
StringReader,
CLI,RS232
Relay,GPIO Time
LedMonoInit,
NVS,Flash
Flash_FS, NVS Flash
Param, Flash_FS
SuperCycle,
task, SuperCycle
 
 



Получается граф зависимостей программных компонентов.



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

То есть обойти граф в порядке увеличения количества зависимостей.

--------------------
Вот только типичный российский программист микроконтроллеров решить такую сложную задачу дискретной оптимизации не сможет.

Вы изобразили скопление простейшей периферии. Задачу очередности включения периферии сейчас легко решают генераторы кода типа ModusToolbox, STM32CubeMX, e2studio и т.п. Тут как раз проблем нет.

Я говорил о задачах уровня middleware, которые надо поднимать после периферии. Такие как: стеки полевых шин, TCP стек и все прикладные протоколы поверх него, логеры, файловые системы и USB классы, BLE профили, GUI и проч.
Там не построить каких-то простых однозначных графов. Поскольку эти задачи могут давать отказы на старте и их приходится деинициализировать и снова инициализировать. Какие-то, как файловые системы, могут уходить в долгое восстановление. Тут графы не работают, а массивы вредны. Только дебагинг и постоянный рефакторинг. Да еще не забыть, что задачи могут отключать, реинициализировать и включать периферию. Такой вообще не место в массивах.

ModusToolbox, STM32CubeMX, e2studio - это всё платформа зависимые утилиты. Они полезны чтобы научится пользоваться вендоровским HALом. Только и всего.

А репозиторий с кодом надо организовать так, чтобы можно было работать с микроконтроллерами разных производителей и быстро мигрировать в случае надобности на микроконтроллеры других вендоров.

Сегодня вы работаете с STM32, следующий MDR32, параллельно на вас висит сборка под ESP32, далее вы работаете с SPC58, после NRF53, потом с CC2642, А теперь Artery. На очереди микроконтроллеры от Nuvoton Technology, GigaDev, Holtec и прочие.

И всё это в рамках одного продукта, который решает задачу какого-то промышленного блока в конвейере на заводе. Прибор один, а микроконтроллеры в нём меняются как перчатки каждый год, так как поставщики не могут поставлять в нужных количествах.

В таких условиях нет смысла привязываться к какому-то там STM32CubeMX. Понимаете?

Надо просто cделать, к примеру, одну

bool uart_mcal_send(uint8_t num, const uint8_t* const data, size_t len);
которая будет везде делать одно и тоже.

И так же с инициализацией.

Надо строить своё приложение на каком-то более высокоуровневом API и не привязываться к STшному SPL или HAL.

Посмотрите как сделан Zephyr Project, в качестве иллюстрации.

Ну вы дали пример.

Видимо от того что под Zephyr Project пишет тьма аутсорсеров со всего света там коментарии и документация для HAL уровня отсутствуют как класс. Классический кот в мешке.

Более запутанной и одновременно неюзабельной экосистемы я не видел.

Лучше посмотрите mbed. B как там органично применяют HAL-ы.
Хотя тоже не идеал.

А вот кодогенерация для корректного обхода графа - достойна отдельной статьи. И, видимо, уже из неё (кодогенерации) как следствие будут нужны массивы функций, описанные тут.

Массив инициализации - это фактически результат топологической сортировки графа зависимостей программных компонентов.



https://habr.com/ru/companies/otus/articles/499138/

https://ru.wikipedia.org/wiki/Топологическая_сортировка

Вот, пожалуйста,
Как Составить Функцию Инициализации Микроконтроллера
(Топологическая Сортировка Графов)
https://habr.com/ru/articles/818917/

первым вызовом идет запуск RTOS.

Не всегда в прошивках разрешена RTOS

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

Например, I2C требует наличия проинициализированного GPIO, GPIO требует проинциализированные шины. В класс I2C передаются объекты GPIO, которые при создании инициализируются объектом нужной шины, которая также инициализируется при создании.

Спасибо за статью. У Вас очень интересные публикации!

Немного не скромно, но мы тоже в Embox экспериментируем с DevTree (например https://github.com/embox/embox/blob/master/board_config/vostok_vg015_dev.conf.h), очень интересно получается.
Правда не согласен, что в любом случае есть основной цикл. У нас же его нет..
А с массивами, мы еще любим всякие штуки делать типа расширяемых массивов (используем секции, а не препроцессор) (https://habr.com/ru/companies/embox/articles/265461/)
Ну и да, генерация кода наше все

Спасибо за статью. У Вас очень интересные публикации!

Благодарю Вас, Антон, что читаете. Рад, что тексты оказались полезны.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории