Перейти до основного вмісту

11 записів з тегом "C"

Вбудований C

Переглянути всі теги

Налагодження HardFault на Cortex-M: від загадки до рядка коду

· 10 хв читання

Кожен розробник вбудованих систем знає цей момент: прошивка працювала, а потім перестала. Налагоджувач показує, що програма сидить у нескінченному циклі з назвою на кшталт HardFault_Handler або Default_Handler, а стек викликів — це кілька беззмістовних фреймів. Цей цикл — типовий обробник зі startup-коду, і він не каже абсолютно нічого про те, що сталося. У збою є причина, і процесор уже її записав: у вісім регістрів на стеку та в чотири регістри стану. Їх залишається лише прочитати.

Ця стаття показує, як перетворити цей цикл на рядок вихідного коду, на двох реальних прикладах, які я запускав у QEMU (моделі плати з STM32F4) і досліджував за допомогою GDB: виклик NULL-вказівника на функцію та запис за адресою, де нічого немає. Усі виводи справжні.

Запуск справжньої прошивки без апаратної частини: симулятор у CI

· 8 хв читання

У статті про unit-тестування з Unity та CMock я казав, що тести на ПК не знаходять «відмінностей між ПК і MCU». Ця стаття — про ці відмінності та про інструмент, який їх знаходить: симулятор, що запускає справжній бінарний файл. Не логіку, зібрану для ПК, а саме той .elf, який створив ARM-компілятор, зі справжнім startup-кодом і справжнім linker script, на моделі процесора та його периферії.

Я покажу два експерименти, які провів: той самий вихідний код тесту, що проходить на ПК і падає на Cortex-M4, та прошивку, яка пише в регістри USART за справжніми адресами STM32F4, а її текст з'являється в терміналі. Наприкінці — чесний перелік того, чого симулятор вам не скаже.

Проєктування API периферії: вісім рішень за модулем Gpio

· 9 хв читання

GPIO — найпростіша периферія мікроконтролера: пін має високий або низький рівень. Саме тому це добра тема для статті про проєктування інтерфейсу. Тут немає апаратної складності, за якою можна сховатися, і кожне рішення — це вибір проєктувальника: як називається пін, що повертає функція, де живе полярність світлодіода. MCAL-модуль Gpio з BSP від Embedbits — реальний приклад із реальними відповідями, і я пройдуся по них одну за одною, з альтернативами та ціною.

Код модуля цитується з гілки STM32H5 репозиторію Bsp-Mcal-Gpio. Приклади, що його використовують, були скомпільовані та запущені з справжніми Gpio_Port.h і Gpio_Types.h та невеликою заглушкою реалізації, щоб API можна було спробувати на ПК.

Doxygen для embedded C: документація, про яку неможливо забути

· 7 хв читання

У кожного проєкту є документація, і в кожного проєкту є документація, яка бреше. Word-файл з описом інтерфейсу був правильним у тиждень, коли його писали. Коментар над функцією чесніший, бо він за кілька рядків від коду, який описує, але його пишуть ті самі люди, які забувають. Вихід не в більшій дисципліні, а в інструменті, який читає коментарі, будує з них документацію й ламає збірку, коли чогось бракує. Цей інструмент - Doxygen.

Ця стаття показує, як у моїх проєктах виглядає задокументований модуль, як його генерують і як зробити документацію частиною CI, яку не можна оминути. Приклади зібрано з Doxygen 1.9.8 та Graphviz, а виводи - справжні.

Переривання та main(): як безпечно ділити дані

· 9 хв читання

Обробник переривання та головний цикл - це дві програми, що працюють в одній пам'яті й нічого не знають одна про одну. Компілятор C не знає, що переривання існує, а CPU не знає, що дві змінні пов'язані між собою. Знає лише програміст, а помилки, які з цього випливають, найгірші з можливих: вони трапляються раз на тисячу запусків, зникають, коли підключаєш налагоджувач, і ніколи не з'являються на code review, бо код виглядає правильно.

Ця стаття розбирає три проблеми спільних даних, одну за одною, на коді, який не працює, а потім показує шаблони, які працюють. Експерименти виконувалися на Cortex-M4 (у QEMU, на моделі плати STM32F4) або на PC, а виводи - справжні.

Скінченні автомати на практиці: кнопка з debounce та довгим натисканням

· 9 хв читання

У першій статті про скінченні автомати я дав вам шаблон і закінчив словами «далі буде». Це продовження, а найкращий спосіб продовжити теорію — це задача. Я обрав ту, що є в кожному embedded-проєкті і яку ніхто не робить правильно з першого разу: кнопка.

Кнопка виглядає тривіальною: пін, високий або низький рівень. Але механічний контакт деренчить (bounce), тож пін робить 1 0 1 1 0 1, перш ніж заспокоїться, а продукт зазвичай хоче від тієї самої кнопки двох різних речей: коротке і довге натискання. Якщо написати це з прапорцями та лічильниками в головному циклі, вийде кілька if, що залежать один від одного так, що за місяць ніхто не зможе пояснити як. Скінченний автомат вирішує це так, що пояснити можна таблицею.

Що відбувається перед main(): скидання, startup-код і linker script

· 10 хв читання

Кожен підручник із C починається з int main(void). Ніхто не пояснює, хто його викликає. А втім, коли ви пишете uint32_t counter = 5; як глобальну змінну і перший рядок main() зчитує 5, вже була виконана велика робота. Коли ця робота не виконана, симптом — змінна з випадковим значенням, яка «вчора працювала».

У цій статті ми простежимо шлях мікроконтролера від скидання до першого рядка main(): що апаратна частина робить сама, що каже linker script і що має зробити startup-код. Це вміст двох модулів BSP Embedbits (Linker і Startup), але принцип той самий на кожному Cortex-M. Увесь код у статті зібрано за допомогою arm-none-eabi-gcc 13.2.1 і запущено в QEMU на моделі плати STM32F405, тож адреси та виводи справжні.

Unit-тестування вбудованого C на вашому ПК за допомогою Unity і CMock

· 9 хв читання

«Вбудоване ПЗ неможливо тестувати unit-тестами, для цього потрібна апаратна частина». Я чую це часто, і це правда рівно для одного виду коду: того, що торкається регістрів. Усе решта — скінченні автомати, парсери протоколів, логіка керування, перетворення значень — це звичайний C, який компілюється на вашому ПК. А на вашому ПК він виконується за мілісекунди, без налагоджувача, без кабелю і без прошивання.

Ця стаття показує, як тестувати модуль за допомогою Unity (тестовий фреймворк) і CMock (генератор моків) — від дизайну, який це уможливлює, до файлу CMake, що все збирає. Усе було зібрано й запущено, а вивід нижче справжній.

Організація файлів у вбудованому C: як тека стає модулем

· 9 хв читання

Мій стиль кодування каже, що імена файлів починаються з імені модуля і що «повна організація файлів описана в іншому місці». Ось це інше місце.

У C немає private, просторів імен і пакетів. Щойно код розділено на файли, файли та система збірки — єдині інструменти, які в нас є, щоб сказати, що належить разом, що є публічним, а що нікого більше не стосується. Тож організація файлів — це не питання смаку, а частина дизайну. Ця стаття описує, як я організовую вбудований проєкт на трьох рівнях: проєкт, модуль і окремий файл.

Правила MISRA C на практиці: що це таке, навіщо вони існують і як з ними жити

· 17 хв читання

Скажіть «MISRA» у кімнаті, повній embedded-розробників, і половина зітхне, а друга половина запитає, яким інструментом ви користуєтеся. Репутація цих правил неоднозначна: для одних це бюрократія, що з'їдає тиждень перед кожним релізом, для інших — причина, чому автомобіль не перезавантажується посеред траси. Обидві групи частково мають рацію.

У цій статті я пройдуся по правилах, які трапляються мені найчастіше, одне за одним: чого вимагає правило, що ви за це отримуєте, як виглядає поганий код, як виглядає добрий і хто знаходить проблему за вас. Усі приклади — це справжній код на C, який я скомпілював і запустив.