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

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

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

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

Вы изобрели "толстую" RTOS вроде Zephyr.

Zephyr пере усложнен наличием device tree и механизмом kconfig .
Я же предлагаю взять от zephyr только методологию proxy Hal. И больше ничего.

device tree там не от сырости завёлся, но от того факта, что заметное количество блоков (IP) в контроллерах существует в двух-трёх вариантах на всех -- приходят на ум контроллеры DMA, EMAC, тот же USB в вариантах fs/hs/otg.

Собственно, на идее драйвера, привязанного к DT'шному compatible, весь arm'овый, да и не только, линукс построен.

В C++ для решения этой проблемы есть концепция zero-cost abdtractions. В 2025 писать на МК без современных плюсов как-то странно.

ну и mbed, как неплохой пример реализации этой идеи, пусть и не использующий всей новизны.

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

однако, всякая тонкая оптимизация, свойственная мк проектам, быстро разрушала эту стену: тонкости dma, usb, lan-mac и can настолько отличались от мк к мк, что если мы экономили каждую мс без сна (а это важно при питании от батареи), то код всё равно был сильно привязан к архитектуре.

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

Согласен, когда в примерах указывают функцию переключения типа gpio_pin_write(), то всё прекрасно выглядит, но модули покрупнее и посложнее, внутреннее поведение которых разнится от МК к МК только усложнят жизнь по крайней мере разработчику такого MCAL.

Читал-читал и думал - ну вот сейчас автор покажет реализацию своего MCAL на гитхабе, ну или хотя бы примеры, как может быть красиво и абстрактно реализован тот же доступ к GPIO при таком зоопарке подходов. Не говоря уже о каком-нибудь CAN - там даже философия аппаратных модулей внутри микроконтроллеров разная, у кого-то есть куча ящиков, у кого-то fifo сложные и фильтры, у кого-то свой CAN-подпроцессор со своим набором команд.

Не понимаю, как можно тут придумать что-то универсальное и гибкое не получив спагетти-код или не получив ограничение функционала, где через MCAL пролезет 5% от того что умеет аппаратная часть микроконтроллера. Думал, может хоть этот момент автор рассмотрит на каком-то сложном примере, но нет... О чём тогда статья, непонятно. Я как считал что сферический в вакууме MCAL не реализуем, так и считаю.

Вот что сказал об этом ChatGPT o3

Короткий вывод

В репозитории фактически лежит лишь набор «заготовок» для системы MCAL-драйверов: несколько Makefile/CMake-скриптов и пустой read_me.txt. Исходников, которые можно прямо собрать и запустить, там нет — большинство файлов, на которые ссылаются скрипты, отсутствуют. То есть «из коробки» использовать ничего не получится: придётся либо дописывать недостающие модули, либо подключать внешний MCAL-код.

Это что-то новое - выкладывать шлак и по его мотивам писать статьи по теме микрокнтроллеров.

Откуда тогда столько файлов?

MCAL-слой часто собирают скриптом-генератором, читающим YAML/Kconfig с конфигурацией проекта. Такой скрипт:

  1. делает копии общих template-файлов в каталог проекта;

  2. проставляет нужные #define и CMake/Make-флаги;

  3. генерирует _commands.c для CLI-оболочки, _diag.c для диагностики и т. д.

В коммите как раз видны одинаково оформленные пары файлов («general», «diag», «commands») для каждого переферийного блока — это типичный вывод такого инструмента.

Да, нынче трудно стало водить за нос.

Ну мой o3 - я ему дал весь репозиторий в zip - говорит что всё настоящее, но только верхний слой абстракции, без слоёв микроконтроллеров. И частично рукописное. Цитирую o3:

  • В папке mcal_common/ лежит ≈ 30 модулей (GPIO, UART, DMA, …).
    Для каждого «живого» модуля присутствуют:

    • _general.c — основная логика (state-машины, буферы, таймауты и т.п.);

    • *_commands.c — набор CLI-команд для отладочной консоли;

    • *_diag.c — обход регистров, self-test, счётчики ошибок.
      Код этой части компилируемый и читабельный — он написан вручную (комментарии, осмыслённые имена, нет признаков автосгенерированного мусора).

  • Общие макросы COMPONENT_GET_NODE/CONFIG и ссылки на code_generator.h показывают, что к каждой сборке должен добавляться автоматически сгенерированный файл с описанием таблиц конфигурации (маску выводов, частоты, номера каналов…). Сам генератор (скрипт/утилита) в архив не положен.

  • MCU-специфичного слоя нет вообще. Нет каталогов вида mcal_stm32f4/, mcal_esp32/ и даже простых «заглушек» с прямым доступом к периферии.
    Поэтому все вызовы вида gpio_pin_set_ll(), adc_start_ll() и т.п. остаются «висящими».

  • Отдельные модули действительно пока пустые: например, mcal_common/wdg/ содержит только wdg_const.h и .mk — чистый каркас без строчки кода.

Чем уже можно пользоваться

  • Алгоритмическая «верхушка». Логика обмена по CAN (очереди, сортировка по приоритету, ожидание TX Ack), I²C-FSM, FSM для I²S full-duplex, универсальные таймерные обёртки, фоновые state-машины PWM/RTC — их можно подключить к своему проекту и «подшить» к собственным низкоуровневым вызовам.

  • CLI-команды и диагностика. Если заведёте свою систему логов (log.h) и консоль, уже готов базовый набор команд: adc_init, gpio_toggle, dma_stat, …

  • Make/CMake-инфраструктура. Файлы mcal.mk и mcal.cmake умеют по флагам подключать нужные модули, размазывать #define HAS_<MODULE> и добавлять include-пути.

Что придётся дописать

  1. Нижний уровень для каждого семейства МК.
    Создаёте папку mcal_<chip> и реализуете функции *_ll() (low level), структуру ComponentDriver_t, ISR, карту прерываний. Это 70-80 % работы.

  2. Генератор конфигурации.
    Скрипт (часто на Python) читает YAML/Excel со схемой платы и штампует code_generator.h + *_config.c. Без него даже общая часть не соберётся: массивы AdcNode[], GpioPad[], ClockDomain[] просто не объявлены.

  3. Сторонние заголовки.
    Упоминаются, но отсутствуют: convert.h, time_mcal.h, none_blocking_pause.h и др. Их нужно либо написать, либо убрать зависимости.

Сгенерировано или вручную?

  • Файлы .c / .h внутри mcal_common — рукописные. Есть авторские комментарии, LOG_INFO() расставлены выборочно, стилистика единообразна.

  • Автоген требуются только для структур данных (константные таблицы и массивы), на что прямо указывают закомментированные строки #include "c_defines_generated.h".

Итого

  • Репозиторий даёт полноценный «бизнес-слой» периферии (state-машины, очереди, CLI, диагностика).

  • Без собственных драйверов и конфигуратора он нерабочий — это половина конструкции.

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

Вы что купили подписку на chatGpt?

Да а как нынче жить-то без этого... это же как вторая голова. Вот пока это пишу, оно мне рассказывает как с помощью Вашего MCAL выглядел бы мой драйвер J1939. Но вообще надо клод брать, вчера вот 4й вышел.

И сколько стоит тариф?

2000-2500р в месяц примерно в зависимости через какие сервисы платить.

Ну вот, говорит, вместо использования кучи аппаратных меилбоксов, как в моём оригинальном драйвере (каждый меилбокс со своим ID и приоритетом) мне придется самому программно засовывать все сообщения в единую свою FIFO очередь и вызывать ваш can_api_transmit_data - но который может ещё и не записать данные, и надо проверять что сообщение ушло через can_wait_tx_done_ll. А оно может не уйти, потому что, скажем, линия занята низкоприоритетными сообщениями, а мне надо отправить что-то срочное. И мне придется отменять отправку текущего сообщения (которое застряло и не уходит) и проталкивать более срочное. Притом что у вас CAN_abort пока и не предусмотрено.
Итого, придется работать с CAN "через одну дырку" делая программно свою приоритизированную очередь, вместо того, чтобы натолкать новых данных в десяток аппаратных мейлбоксов и забыть - сами отправятся по своим триггерам и приоритету (сейчас у меня так). В итоге накладные расходы использования вашего MCAL это не "10-20 тактов процессора" на вызов обертки MCAL, а существенное изменение архитектуры драйвера, который вместо полагания на аппаратные возможности микроконтроллера будет вынужден пользоваться кастрированным узким API MCAL, тратить кучу ресурсов на организацию своих приоритетов, FIFO и менеджера отправки данных в сеть CAN.

Хм. У вас тот же chatgpt,но совершенно другой вывод. Как так?

Ну почему другой - и тот и этот написали что больше части файлов нет, а именно привязки к микроконтроллерам. Разнится оценка вклада имеющегося кода: один раз он написал что это только скелет с обертками, другой раз решил что это уже половина дела уже. Ну а так-то да, gpt что дышло, как спроси так и вышло

Это довольно странно качать в Zip, а потом за свои же деньги заставлять GPT это распаковывать при этом потерять историю коммитов. Лучше смотреть прямо на GitHub-е. Там GPT дополнительно посмотрит как юзер активничал.

Но мой o3 пошел дальше и все же думает, что автор втихую использовал один из корпоративных генераторов сорсов:

- Vector DaVinci Configurator Classic / Pro
- EB tresos Studio
- ETAS ISOLAR-A, Mentor VSTAR, Artop

Но только потом он исходники еще сильно кастрировал.

У меня давече с другим проектом gpt o3 имел проблемы с ориентацией по гитхабу, а zip файл он разархивировал внутри и сразу все прочитал. Да и в plus подписке ограничивается число вызовов в неделю, и там и там один - что ссылку дать, что архив. Ну да лирика все это, и так понятно.

" Vector DaVinci Configurator Classic / Pro, EB tresos Studio, ETAS ISOLAR-A, Mentor VSTAR, Artop"
Впервые узнаю, что есть такие утилиты.

Как говорил мой начальник- схемотехник
"Не нужны нам программисты микроконтроллеров, ChatGPT сам напишет нам hex файл прошивки"

ChatGPT сам напишет нам hex файл прошивки

И схему тоже нарисует. Хочешь в стиле Сальвадора Дали, хочешь под стимпанк. Схемотехники, готовьтесь :)

Нечто подобное есть Кристофера Корманиоса, автора кникги Real-Time C++: Efficient Object-Oriented and Template Microcontroller Programming: GitHub - ckormanyos/real-time-cpp: Source code for the book Real-Time C++, by Christopher Kormanyos

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

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

Каждый начинает со своего MCAL. А получается просто CAL

-Сегодня первое, что делают люди, когда атакуют твою Темную Башню Зла - это заваливают тоннель для побега, - сказал Злобный Гарри.
-Ублюдки! - сказал Коэн. - Ты должен позволить Темному Повелителю скрыться. Все это знают.
-Это точно, - сказал Калеб. - Нужно оставлять себе немного работы на завтра.* ("Последний Герой")

Так вот надо всегда оставлять немного работы на завтра. А переписать всё под новый контроллер это замечательная работа. Если не оставить её себе, то тебе поручат другую работу, которая может быть уже далеко не такой хорошей.
С третьей стороны, переписывать каждый раз MCAL на другой контроллер тоже работа....

А я вот никогда не использую "HAL от вендора" -- у меня самописное всё, от GPIO до USB. Ну, это если про нижний уровень, работающий прямо с железом, говорить. На среднем уже возможны варианты (скажем, использую LVGL, а если потребуется прикрутить сеть, возьму lwIP).

Аналог FatFs тоже сами написали?

Да. Полностью асинхронный (а не чисто синхронный, как FatFs -- собсно, потому и написал свой). Причём использовал сопрограммы C++20 -- собсно, потренировался на кошечках :) Правда, сейчас перепишу на обычные процедуры обратного вызова, поскольку Кейловскому отладчику наличие сопрограмм начисто срывает крышу -- отлаживать можно только на уровне дизассемблера, причём не только сами сопрограммы, но и код более высокого уровня.

вот это не фига себе признание! Я тоже думаю что всегда лучше писать в рамках С++11 максимум. Кроме того что можно нормально отлаживаться, переносимость повышается туда куда последние достижения еще не завезли.

Вендор вендору рознь. Вот у stm плохой Hal . Хуже st Hal ни у кого в мире нет. А у китайцев artery, yuntu , flagchip - великолепный hal.

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

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

Это другая сторона большой legacy. Есть же вариант когда как бы наоборот: сделали один раз (на 8051) и отказаться никак не могут, допиливают, и даже эмулируют старое, т.е. как бы даже не переписывают. потому что где то была очень тонкая фишка которой потом нигде просто нет.

В psoc сypress например много встроенного аналога, в новых attiny всякие lut элементы, ну шины событий - как раз то изза чего можно упростить и схему и код

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

Публикации