Pull to refresh
2
0
Send message
Шаблонное программирование — это не просто кодогенерация, а программирование в пространстве типов. Поэтому вместо страшного кода с кучей параметров проще создать новый тип:

struct ExampleTrait {
  constexpr static std::uint32_t  kParam1 = 0;
  constexpr static std::uint32_t  kParam2 = 1;
};

template < class Trait > 
class Bar {
  constexpr static std::uint32_t  kParam1 = Trait::kParam1;
  constexpr static std::uint32_t  kParam2 = Trait::kParam2;

  // Далее идут другие константы и набор статических функций
};


Похожим образом сделана стандартная библиотека с++, хотя мой пример упрощен. Обрати внимание на наличие списка констант — это очень удобная фича.

Далее, необходимо четко разделять сущности. uart — это uart, gpio — это gpio. Мой опыт показывает, что смешивать разные сущности по возможности не стоит, гораздо проще сгруппировать родственную периферию, а потом настроить её одной строкой:

template < class... dev >
  class DeviceSet
  {
    public:
      inline constexpr static void Init()  { ( dev::Init(), ... ); };   
  };

  template < class... IO >
  class IoSet final: public DeviceSet< IO... >
  {
    public:
      inline constexpr static void Set()   { ( IO::Set(), ... ); };
      inline constexpr static void Reset() { ( IO::Reset(), ... ); };
      inline constexpr static void Toggle(){ ( IO::Toggle(), ... ); };
  };


Этот класс довольно туповат и неоптимален, по-крайней мере для stm32 (есть микроконтроллеры, у которых каждый gpio настраивается своей группой регистров, там быстрее не будет)
Оптимизириванный вариант, если таковой возможен, должен использовать маски для группы gpio (пример)

А применение этого добра классическое:
using Leds = mpp::gpio::IoGroup < LedBlue, LedRed, LedOrange, LedGreen >;
bsp::Leds::Init();
bsp::Leds::Toggle();


Uart же более правильно проинициализировать отдельно, включение прерываний также стоит сделать отдельно. Исключения, конечно бывают, Clock Tree и Flash в тех же stm-ках зависят друг от друга, поэтому при натройки ClockTree должен знать о конфиге флеша.

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

_Regs::CR1Pack<_Regs::CR1::UE, _Regs::CR1::RE, _Regs::CR1::TE>::Set();
    // Еще какие-то настройки.

UART->CR1 = UART->CR1 | UART_CR1_UE | UART_CR1_RE | UART_CR1_TE; 


Результат эквивалентен, тем более он будет упакован в функцию Init, вызываемую нами в коде. У меня в этом случае четкая ассоциация с блоками unsafe из Rust — я знаю, что здесь может быть ошибка, и найти её легко, тем более библиотечный код интенсивно тестируется.

Гораздо эффективнее сделать и отладить библиотеку для работы с ip-ядрами, тем более генерация обертки над каждым битом не всегда возможна (TI иногда несколько отходит от стандарта CMSIS).

А теперь скормите софтине readelf выхлоп gcc:
$readelf firmware.elf -a


Там вы увидите много интересного в коде, ниже выдержки:
Таблица символов «.symtab» содержит 192 элемента:
   Чис:    Знач   Разм Тип     Связ   Vis      Индекс имени
08000561   188 FUNC    GLOBAL DEFAULT    2 __call_exitprocs
...
08000629     4 FUNC      GLOBAL DEFAULT    2 __retarget_lock_[...]
...
0800049d   196 FUNC    GLOBAL DEFAULT    2 __register_exitproc
...
080003d9    40 FUNC    GLOBAL DEFAULT     2 exit
...


Это большая часть того, что мне не нужно, там ещё мелочи полно. Итого минимум 500 байт не нужного в микроконтроллере. От части можно избавится ключем -nostartfiles, написав замену __libc_init_array(). Можно ещё использовать -nostdlib, написав замену memcpy, memset и некоторым другим функциям, либо подтянув другую реализацию стандартной библиотеки, но это жестковато.

Результаты примерно такие:
text data bss dec hex filename
1628 224 1760 3612 e1c blink_stm32f407_disco.elf


После выкидывания ненужного
text data bss dec hex filename
716 104 1576 2396 95c blink_stm32f407_disco.elf


Размер таких маленьких проектов сильно зависит от реализации Run-Time библиотеки. Показательными будут проекты побольше, например LWIP впихнуть и графическую либину.
Также нужно понимать что уровни оптимизации являются набором отдельных флагов, совершенно разных для разных компиляторов. При правильном наборе флагов разница будет 1.5-2%, может и меньше.
Напишите как нужно. Будем учиться по Вашим статьям. Только не отсылайте читать других авторов, ведь интересен Ваш подход и консультироваться с Вами.

В коде вы используете битовые поля, порядок размещения бит в которых не регламентируется стандартом, т.е. является implementation-defined.

Отсюда, работа с регистрами, требующими строгий порядок бит, через битовый поля выливается в undefined behavior.

Это распространенная ошибка. Ещё чаще используют пакованые структуры, которых в стандарте нет и никогда не будет, для «сериализации» пакетов данных,. Это опять приводит к undefined behavior, привязывающий код к конкретной версии компилятора, на котором он хоть как-то «работает».
Эти подходы нельзя использовать категорически. Вместо битовых полей необходимо использовать маски, а вместо пакованых структур писать полноценные сериализаторы.

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

Мы эмбеддеры, наша задача — писать надежный код, строго соответствующий стандарту. Давайте оставим право на говнокод ардуинщикам.

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

CMSIS и svd генерируются автоматически по verilog-модели. С 2013г. я встречал ошибку только один раз в заголовочнике для stm32f411хе.

вот только зачем в области пользователя доступ к регистрам?

Я пока думаю, как это оформить: через профили питания или просто передавать в трейте конфигуратору PLL.
Во всяком случае, код в файлах board платформозависимый, в нем допускается использовать подобный код.
Там у вас тоже странности в коде есть

Ревью небыло, так что может что-то подобное всплыть. К сожалению, на текущий момент ключ --pedantic с этим не поможет.

Уже вот вот будет… :) Вроде даже уже есть, но пока мне еще не дали.

Учитывая наличие пакетов типа arm-none-eabi-gcc или аналогичных во всех дистрибутивах, необходимость стремится к минимуму.
Единственное, для некоторых работ может быть нужен сертифицированный компилятор, в таком случае можно рассмотреть IAR или GreenHills. Но это будет одна единственная лицензия для билд-сервера.

Но прикольно, только если это все руками писать, то накладно… если бы генерилка была какая.

Этот момент можно рассмотерть:
  1. Многие вендоры делают различные конфигураторы для периферии, например, тот же cubemx от ST. В коробке с этими конфигураторами идут файлы с описанием периферии, настоятельно рекомендую заглянуть в папку /db/mcu/IP/
    Эти файлы применими как минимум для получения ограничителей/constraints библиотечным модулем или генератором. Именно их я использую для генерации списка пинов.
  2. Не для всякой периферии такая генерация необходима. Вендоры часто консервативны и, как минимум, в пределах серии сохраняют один и тот же набор доступных польхователю регистров. В этом случае затраты для написания шаблона под генератор будут идентичны затратам на написание библиотечного модуля.
  3. Есть уникальная периферия, например PLL, вот здесь генератор может пригодится. Но шаблон для генератора придется писать., что для какого-нибудь stm32h7 задача крайне не тривиальная.


Генераторы для PLL я точно писать буду, но несколько позже. В первую очередь я планирую доделать модули и примеры, не зависящие от железа:
  • инжекция конрольных сумм прошивкки в саму прошивку (уже готово)
  • Бинарный логгер, заточенный специально под эмбеддед. Есть интересная идея, как организовать печать строк с форматированием, но при этом не хранить их в прошике и не заниматься форматированием значений в рантайме.
Очень хорошая статья на тему, как делать не нужно. На самом деле очень даже опасная — подобные статьи читают в основном новички, начинают делать также, а потом их приходится бить по рукам.

1) Вообще, после фразы «Когда я попытался сделать первый проект меня ждало разочарование — CMSIS! Кому как, но для меня это было (и остается) ужасом: много буков, длинные и для меня не понятные структуры. » программиста можно выгонять с собеседования, так как это явно не эмбеддер, язык С он точно не знает, как следствие и С++ тоже.
Ну да:
RCC_APB2ENR_bit.ADC1EN = 1; 
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;


Эти записи такие разные, явно стоит вендорлока, ещё и свой аналог для описания регистров нагородим. Вы же его автоматически генерируете? И тесты у вас есть?

2) IAR — компилятор для профессионалов(нет). Только в заголовке будет запись типа:
#error This file should only be compiled by ARM IAR compiler and assembler
IAR платный, а вы не оставляете мне возможности собрать проект другим компилятором. Может быть мне его купить за 2-3к$? А у вас он куплен?
Под линукс он не поставляется, наверное, придется ещё и винду покупать. Ну да ладно: «Я привык работать в IAR. Да, есть другие IDE, но мне хватает возможности IAR».

3) Как правило, начинающие разработчики изучают С++ со стороны ООП и это вполне себе оправданно, но это не серебряная пуля. C++ — мультипарадигмальный язык программирования, наиболее мощными возможностями которого являются метапрограммирование и программирование в пространстве типов, позволяющие сгенерировать оптимальный код, который так важен в эмбеддед.
Все расчеты в коде, который вы привели, можно выполнить на этапе компиляции, сведя все к записям в регистры GPIO, у вас это все считается в рантайме. А если у вас по ТЗ время перехода МК в некое состояние 1мс, при этом настроить нужно 200 ног?
Посчитать на этапе компиляции маски можно вот так

Использование будет примерно таким:
struct LedTrait final: mpp::gpio::LedTrait
{
    constexpr static mpp::gpio::Inversion kInversion = mpp::gpio::Inversion::Off;
};
//...
using LedBlue   = mpp::gpio::Gpio < mpp::gpio::PD15, LedTrait >;
//...
using Leds = mpp::gpio::IoGroup < LedBlue, LedRed, LedOrange, LedGreen >;


А помигать можно как-то так :
Leds::Toggle();


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

Возможно, вам стоит изучить язык более досконально, С++20 рановато брать, а вот С++17 давно мейнстрим. Уже потом учить работников и писать статьи.
1) Большинство ip-ядер, реализующих usart, так или иначе умеет генерировать прерывание IDLE, которое позволяет определять конец транзакции. Там, где данное прерывание не предусмотрено (или ядро кривое и не работает как надо, тот же cc1352), можно использовать таймер.
Приём и передача стандартно выполняются DMA, под которые нужно два буфера для приёма и передачи. Вендоры сейчас начали пихать нормальные матрицы коммутации для DMA, часто с пробросом эвентов от периферии типа таймеров, так что разработка становится проще и удобнее. Аналогично все работает и с другими интерфейсами типа SPI.
Передача байтами «вручную» конечно подходит для начальных этапов обучения, но не более.

2) Опять таки IAR. Моё руководство требует с меня лицензионной чистоты софта и отсутствие лишних расходов. Чтобы нормально пользоваться IAR нужно знать его особенности (т.е. нужно прочесть много документации), нужно знать особенности рантайма, нужно знать и уметь писать скрипты линкера, из которых следуют нюансы устройства startup-ов.
Зачем грузить этим студентов, если IAR им никто не купит? У них будет gcc, cmake и openocd. Я предпочту закупить оборудование, нанять ещё одного разработчика или дополнительную премию сотрудникам выплатить.
IAR Embedded Workbench как IDE очень плох, разработка на нём как минимум не доставляет удовольствия. Вы же не заставляете бедных студентов им пользоваться?
В этом случае как раз никаких проблем нет, тем более RTOS позволяет работать с socket-api, что несколько проще, чем callback-style. Единственно, я делал свой порт стека, так как работаем с большим количеством разных mcu.
Основная идея использования w7500p была в удешевлении решения на stm32f4, но все уперлось в баги кремния.
Делал как-то прототип конвертера Ethernet<->RS232/422/485 на wiznet w7500p. Подкупала конечная цена изделия, за счет цены на чип и наличия встроенной физики.
Чип оказался с большим количеством аппаратных багов, основная часть которых проявлялась при работе с tcp/ip стеком (а ведь это фишка камня!), библиотека забита пометками //workaround. Прототип я сделал, но в продакшн решил не пускать из-за кривого кремния.

STMicroelectronix недавно выпустили новое семейство STM32MP15x, представляющее собой гетерогенный процессор (Cortex-A7 + Cortex-M4).
Для работы с графическими интерфейсами выгоднее ставить его, так как на ядре А7 крутится обычный linux, а реалтайм обеспечит CM4.
Заинтересовался данной статьей, так как сейчас занимаюсь написанием библиотек для stm32mp15x. Статью прочитал несколько раз, вдумчиво, заметил следующий минус:
Тип доступа RO/WO/RW должен отностится не к регистру, а строго к каждому биту в отдельности
Дело в том, что во многих регистрах соседствуют биты rw и ro, некоторые биты сбрасываются записью 1, а не 0. Отсюда минус «можно сделать глупость» никуда не уходит.
В заголовочниках от производителя нет описания типов доступа для отдельных бит, а значит автоматически пересобрать заголовок с регистрами через python не получится. Теоретически это может делать производитель путем обработки исходных hdl-кодов (собственно, стандартные cmsis-заголовочники так и делаются), но вряд ли они будут делать это в сколь-нибудь обозримом будущем.
У микроконтроллеров dual ethernet не встречал. Ближайшее подобное есть у nuvoton — NUC980, чипы позиционируются для IoT, стоят в районе 5-7$ в партиях от 1000 шт, контрактная цена должна быть немного ниже.
Но это уже MPU, со всеми вытекающими.
Мастером будет приёмный контроллер, слейв получается программный, да. У меня не было проблем со слейвом, так как в качестве него выступала FPGA, для которой было написано соответствующее ip-ядро.
Вообще у вас интересная задача. Вы получаете данные по eth, но, похоже, не имеете возможности обработать их на этом же камне. Поэтому пересылаете все в следующий МК. У вас данные требуют специфической обработки? Или просто скорости не хватает? Или одна из железок готова и нужно «расширить» её возможности?
Вы в статье размышляли об использовании eth для решения задачи, неужели отбросить лишний заголовок кадра для вас такая большая проблема?))
Конечно, вы можете есть кактус, если хотите.
Все очень просто, думайте о QSPI как об обычном spi с 4-мя параллельными шинами данных.
C quad-spi все просто, линейки stm32f46x/47x и линейки stm32f7/h7. С octo-spi сложнее (линейка на ядре L4+), у ST пока нет чипов с OSPI и ETH на одном кристалле, обещали сделать. QUAD-SPI должно быть достаточно.
Кстати, в CoubMX есть удобный фильтр для подбора чипов.
Для скоростной передачи данных есть контроллеры c quad/octo-spi, необходимость которых определяется на этапе проектирования.
Параллельный порт называется параллельным, потому что его можно использовать, внезапно, как параллельный. Вот только прерываний и флагов, необходимых для работы с данными у него обычно нет, соответственно и контроллер DMA с ним работать не сможет.
Поэтому для реализации скоростных интерфейсов, например для связи с FPGA, и используются OCTO/QUAD SPI, позволяющие реализовать пропускную способность выше 100 мбит/с.
Цена на чип от производителя 2$ в партиях по 10к. Конечно, контрактная цена может быть меньше 2 баксов, но в данном случае это именно отбраковка.
У DI HALT'а есть целая статья на эту тему.
Там все не так просто. В контроллере предусмотрены MCU device ID code, в частности DBGMCU_IDCODE (+для JTAG), по которым отладчик определяет камень:
DEV_ID[11:0]: Device identifier (STM32F405xx/07xx and STM32F415xx/17xx)
The device ID is 0x413
DEV_ID[11:0]: Device identifier (STM32F42xxx and STM32F43xxx)
The device ID is 0x419
Вот только внутри одной серии камни может быть невозможно отличить, например камни stm32f407vg и stm32f407zg. У них разные корпуса с разным числом ног, но с точки зрения отладчика это одно и тоже.
К слову, размер flash также указан в регистрах.
Плата не поддельная, просто камень — отбраковка. Предприимчивые китайцы скупают отбраковку за копейки и перепродают на алиэкспрессе.
У меня таких плат в ящиках штук 50, используются для макетирования. У половины китайцев что-то не работает: GPIO (самое распространенное), usart, гуляющие ошибки во flash…
1

Information

Rating
Does not participate
Registered
Activity