diff --git a/embedded_c.en.md b/embedded_c.en.md index b8b5864..6506661 100644 --- a/embedded_c.en.md +++ b/embedded_c.en.md @@ -294,6 +294,31 @@ if (cond1) { Часто бывает что устройство имеет несколько входов/выходов/шин/светодиодов/кнопок итд, абсолютно всегда все что потенциально может быть больше чем в одном экземпляре должно обрабатываться через индексы и циклы. +Плохой пример: +```C + #define INPUT_1 { GPIOA, 10 } + #define INPUT_2 { GPIOA, 11 } + + ... + + gpio_init(INPUT_1); + gpio_init(INPUT_2); +``` + +В таком виде трудно вносить изменения, легко допустить ошибку, поправить одно и забыть другое. + +Лушче написать так: +```C + #define INPUTS { { GPIOA, 10 }, { GPIOA, 11 } } + + ... + + input_t input_list = INPUTS; + for (int i = 0; i < ARRAY_SIZE(input_list); i++) { + gpio_init(INPUTS[i]); + } +``` + ## Исправление ошибок в master Ранее допущенные ошибки форматирования исправляются в отдельной ветке с PR согласно кодстайлу. @@ -395,3 +420,61 @@ enum w1_protocol_transaction { W1_PROTOCOL_TRANSACTION_RECEIVE } ``` + +# Не плодите макросы конфигурации +Часто макросами в коде выбирается какая переферия используется в данном варианте, вариантов обычно не много, а переферии наоборот много. Вместо того чтобы прописывать макрос на каждый регистр или значение используйте структуры. + +Вместо такого: +```C + #define MODBUS_HW_USART USART1 + #define MODBUS_HW_USART_IRQn USART1_IRQn + #define MODBUS_HW_DMA_TX_CH DMA1_Channel2 +``` + +Лучше написать так: +```C + struct modbus_hw { + USART_TypeDef *usart; + IRQn_Type irqn; + DMA_Channel_TypeDef *dma_tx_ch; + }; + + #define MODBUS_USART_HW { \ + .usart = USART1, \ + .irqn = USART1_IRQn, \ + .dma_tx_ch = DMA1_Channel2 \ + } + +``` + +# Переиспользование +Нужно использовать код повторно. Не нужно писать код если он уже написан. Если чтото существующее подходит не нужно делать чтото новое. +Чтобы не оставлять ошибки в дубликатах, экономить силы программиста и прочее. Читать подробнее в [Википедии](https://ru.wikipedia.org/wiki/Повторное_использование_кода). +Если есть 2 одинаковые строчки, подумайте, не оформить ли это в функцию, библиотеку или использовать в уже существующую. +Для использования кода с другого устройства - сделайте его библиотекой. + +# Старайтесь не использовать поллинг +У любого кода есть причина выполнения, вот в обработке причины и нужно вызывать выполнение кода, или запланировать его выполнение. +Используйте инструменты организации кода - корутины и таймменеджер. В прерываниях планируйте задачу с обработкой в бэкграунде. Не используйте флаги. +Аналогично прерываниям такой подход работает быстрее и понятнее. Периодически вызываемые функции с проверкой флагов расходуют процессорное время и память, усложняют отладку, так как не понятно кто этот флаг ставит, кто его проверяет. Непонятно на ком ответственность за выполнение и сколько времени выполнется функция do_periodic_work. + +# Не пишите лишний код, не выполняйте не нужные действия в коде +Пример: если файл с настройками не найден, прошивка использует дефолтные настройки. Не нужно сохранять файл с дефолтными настройками. Файл нужно записывать только если пользователь чтото поменял. +Чтение файла с дефолтными настройками ничем не отличается от отсутствия файла. Поэтому, можно избавиться от лишней операции записи, расхода места и износа флеш памяти, кода который эту запись делает. + +# Не нужно делать в run time все что можно сделать в compile time +Все вычисления констант, генерацию макросов итд, то что не меняется в ходе работы программы нужно вычислять препроцессором на этапе сборки и записывать readonly константами: ресурсы ограничены, и их нужно экономить! Пусть мощный компьютер подготовит все необходимые данные. +По этой же причине следует выделять память статически и не использовать кучу. + +# Не нужно делать в контексте прерывания все, что можно сделать в контексте background +Синхронизация асинхронных процессов это источник проблем. У нас есть разные инструменты для организации такой архитектуры. Тайм менеджер, корутины. Мы всегда должны знать что выполняется в прерываниях ВО ВСЕЙ ПРОШИВКЕ, чтобы быть уверенными что там нет ничего лишнего. +Например: в прерывании вызывать только планирование таски, а не само её выполнение. + +# Не усложняйте код +Не нужно рассчитывать на тонкости стандартов языка. Не нужно заворачивать сложные конструкции. Код должен прочитать школьник который осилил только первую главу книжки по Си. +Подумайте можно ли сделать ваш код проще ? Часто можно выделить функции, чтото перегруппировать, распутать. +Помните про бритву Оккама. + +# Делайте сразу хорошо если понимаете как +Не нужно писать TODO и FIXME. Если в код можно сделать компактнее, алгоритм быстрее, и вы знаете как, то сделайте сразу хорошо. Не надо лениться и аргументировать почему и так пойдет. +Плохо аргументировать фразами "жалко чтоли ? там не много". Такая аргументация не учитывает всю систему целиком, все возможные комбинации и многообразие кейсов применения.