habrahabr

Самые эпичные баги при программировании Микроконтроллеров

  • суббота, 22 февраля 2025 г. в 00:00:17
https://habr.com/ru/articles/884100/

Электротехника — это наука о контактах, а вернее — об их отсутствии.

У каждого программиста микроконтроллеров с опытом формируется коллекция решенных багов. Баги появляются и исчезают, как вспышки на Солнце. Некоторые из них весьма эпичные. Многие уже читали про ошибку переполнения при конвертации double в int16_t в коде ракеты Ариан-5.

Самый типичный баг - это зависание прошивки. Переслали мигать heart beat LEDы, UART-CLI перестала отвечать на команды. В таких случаях не надо подвергаться конвульсиям, судорогам и параличу. Надо просто спокойно и методично разбираться в ситуации.

Выявление причин багов порой сродни работе детективом. Это проявляется в том, что очень трудно выявить причину бага. Сначала разработчик идет по ложному следу, ходит кругами, а конце концов выясняется (порой случайно), что причина на самом деле была проста, как солдатский валенок.

Вот об этом сейчас и поговорим...

1-- Отлаживаем телематическую плату в CAN сети. Подключили переходник USB-CAN. Автомобиль не заводится. Стартер работает, но зажигания (вспышек в цилиндрах) не происходит. Разобрали половину впускного коллектора. Рукой шевелили дроссельную заслонку. Три часа искали причину поломки. Оказывается в OBD-II разъем попал кусок фольги от папиросок, которые замкнули три крайних пина. Стоило пинцетом извлечь мусор, как автомобиль завёлся с пол-оборота. Этот баг называется "курящий разработчик". Вот так просто и не затейливо.

Мусор между разъёмами
Мусор между разъёмами

2-- Схемотехники разработали электронную плату с разъемом Tag-Connect. Плату разрабатывали полгода. Плата скомпонована с плотностью нейтронной звезды. А после сборки выяснилось, что никак не примонтировать самый главный разъём - тот что для программирования. Ручной зажим Tag-Connect не защелкнуть, так как пальцы банально не пролезают между стабилизатором напряжения Traco Power и разъёмом Tag-Connect. Да... Всё не предвидишь. В результате пришлось просить женщин с тонкими пальцами, чтобы соединить программатор и электронную плату. Этот баг называется: "толстые пальцы".

3--При питании от USB электронная плата не включается. Вернее она на мгновение мигнет и сразу отрубается. Сначала я подумал, что происходит это из-за инициализации BLE и UWB. Якобы они сильно просаживают напряжение. Но при отключении всего wireless connectivity из сборки пропадание электропитания осталось. Но потом случайно заметил. Когда USB вилка вставлена до упора, то на плату не подается питание. Однако если же USB вилку аккуратно выдвинуть всего только на полдлины, то так питание поступает! Оказывается был просто бракованный разъём гнезда для USB-A. На плату просто не поступало питание по USB. Мораль: "Не надо жалеть денег на кабели USB".

4-- Отсутствие SWD link(a). Программатор не видит target по SWD. Собрали стенд, положили SWD длиной 90 см и нет Link(а) с MCU. Когда кабель 12 см, то link есть. Оказывается, что пакеты SWD шины просто не проходили через slip-ring. Slip-ring - это такое устройство, чтобы пропускать провода через подшипник. Чтобы провода не наматывались на ось и не рвались. Пришлось прошивать электронные платы внутри турели навесу.

5-- Плата с СС26x2 зависает при перезагрузке. Плата работает под отладчиком, а при пере сбросе питания не стартует прошивка. На другой плате это не проявляется. Эта ситуация потребовала неделю на выяснение причины и устранение.
Оказывается у СС26x4 MCU от TI надо прописать спец адреса в конце Flash памяти, чтобы чип переключился на внешний осциллятор. Там в последней странице Flash живет сектор конфигурации SoC-а. Поэтому во все новые платы надо первым делом записывать прошивку blinky_backdoor_select_btn26x2.bin из SDK при помощи утилиты SmartFR Flash Programmer 2. Прошивка blinky_backdoor_select_btn26x2.bin корректно настраивает конфигурационные CFG регистры за счет того, что они лягут в последней страницу Flash. Потом уже накатывать свою прошивку поверх. Вот так.

6--Статическое электричество постоянно портит жизнь. Порой ставишь электронную плату на недельный тест, чтобы наработать логи. Приходишь через неделю cнять характеристики по UART, ценнейшие логи из RAM памяти. Только дотрагиваешься до края электронной платы... Дзык! Проскальзывает искра статического электричества, которая убивает содержимое RAM памяти и прошивка заклинивает прямо на глазах.
И весь недельный тест с логами внутри накрывается медным тазом... Пришлось поднимать на устройстве NVRAM и черный ящик в SD карту, чтобы писать логи в энергонезависимую память. Вот такие вот пирожки с капустой... Понимаете?...

14--Хрипящий голос. Из Bluetooth classic модуля BT1026 по I2S приходит хриплый голос. Сначала подумал, что DMA не так настроено. Микроконтроллер тактирует WS на частоте 50kHz. Оказывается, что если BT1026 опрашивать с частотой дискретизации чаще 48kHz, то по I2S data out будут идти нули. Модуль FSC-BT1026С не может выдавать I2S семплы чаще чем 48kHz. Эти вкрапления нулей и были слышны как хрип. Решение было в том, чтобы добиться частоты следования семплов не выше 48kHz. Микроконтроллер nRF5340 самое близкое мог выдавать на I2S WS только 50kHz. Пришлось сконфигурировать мастером шины I2S сам BT1026. Так звук стал непрерывным и чистым.

FSC-BT1026С + nRF5340
FSC-BT1026С + nRF5340

7--У меня был случай, когда три схемотехника спроектировали электрическую цепь электронной платы (схемотехнику), развели топологию (layouts), отправили производство в другую страну, a спустя три месяца на выходе выяснилось, что они забыли вообще даже подать на микроконтроллер электропитание! Вот тот самый МГТФ проводок, который пофиксил ошибку дизайна.

Вот так... Как говорят:

У семи нянек дитя без глаза.

8--Никак не проходит auto-negotiation в Ethernet физике по 100-Base-TX. Не проходит ping. Оказывается в монтажных мастерских техник по ошибке припаяла кварц для физики fast-ethernet, не на частоту 25 MHz, а на частоту 23 MHz! Перекинули кварц и ping-и сразу побежали, как горный ручей. Не тот был кварц.

Или вот, плата Olimex-STM32-H407 в UART3 вместо лога загрузки сыплются кракозабры. Оказывается, что прошивка думает, что кварц 25 мегагерц, а там на самом деле 12 мегагерц. Пересобрал прошивку с настройками тактирования от 12 мегагерц и побежал понятный лог на ожидаемой битовой скорости .

9--Собранная прошивка для платы с МК STM32F413ZGJ6 зависает при прыжке из загрузчика в приложение. При пошаговой отладке всё стартует отлично. При выяснении причины выяснилось, что перед прыжком в приложение загрузчик смотрел на указатель
стека и не запускал прошивку, если в таблице векторов прерываний указатель
на верхушку стека ссылается на размер RAM больше, чем 128kByte. В таких случаях загрузчик просто прыгал в бесконечный цикл. Загрузчик, к слову, писал коллега из другого дивизиона.
При этом напомню на MCU STM32F413ZGJ6 320k Byte RAM! Оказывается коллега разработчик загрузчика по ошибке всегда собирали прошивку вообще для другого MCU: STM32F205VC. И generic приложение он тоже собирали для STM32F205VC, а записывал на STM32F413ZGх. Поэтому раньше у него это зависание не проявлялось. Его прошивка работала чисто только благодаря обратной совместимости между ядрами ARM Cortex-M4 и ARM Cortex-M3. Оказалось, что ему просто лень было посмотреть, что написано на корпусе микросхемы DD14. Упс!, Микроконтроллер перепутали...

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

Техника в руках дикаря - кусок железа.

10-- Ошибка snprintf. Прошивка для ARM Cortex-M33 неожиданно сваливается в исключение Default Handler внутри функции snprintf(). А конкретно на функции svfprintfr и ассемблерной команде VSTMDB. Оказывается snprintf использует вычисления с действительными числами. При этом код прошивки был собран с активированным аппаратным FPU одинарной точности. Это опции компилятора -mfloat-abi=hard -mfpu=fpv5-sp-d16, одновременно с этим startup код не включал сопроцессоры FPU в регистре SCB->CPACR.
Пришлось либо отрубать аппаратный FPU ( -mfloat-abi=soft ), либо включать сопроцессоры FPU.

#ifdef ENABLE_FPU
  /* Enable CP10 and CP11 coprocessors */
  SCB->CPACR |= (3UL << 20 | 3UL << 22);
#endif 

11--Загадочные прерывания. Почему-то при инициализации CAN трансивера прошивка зависает в DefaultISR. Сначала я думал, что нет тактирования на CAN трансивере, ибо SoC весьма сложно раздавал тактирование. Однако нет. Тактирование настроено нормально. Это показал вывод частоты "на улицу". Оказывается, при инициализации CAN вызываются прерывания, на которые нет обработчика. Причём это были зарезервированные для данного MCU номера прерываний, для которых для данного MCU по спеке вообще ничего не предусмотрено. И тем не менее, иногда эти номера выстреливают, что приводит к сваливанию прошивки в бесконечный цикл default_ISR. Чтобы починить инициализацию CAN трансивера пришлось определить обработчики прерываний по умолчанию для всех 196 ISR. И определить эти обработчики, как weak функции.

12--Так называемая "ошибка 71-ой минуты". Прошивка всегда стабильно зависает на 71-ой минуте работы (4291 секунде). Светодиоды перестали мигать, UART-CLI перестала отвечать на команды. Оказывается переполнилась переменная up_time_us = 4294967296 us = 4 294 967 ms = 4 294 s= 71 m. И программный компонент limiter просто ждет, когда переменная up_time_us превысит значение 4294967297 us и не вызывает никаких функций. А это и не происходит, так как 32-х битный счетчик up_time_us пошел по второму кругу. Пришлось добавить обработку на переполнение типа данных uint32_t.

13--Очень низкая дальность LoRa link-a. У нас в компании спроектировали PCB c дальнобойной радиосвязью LoRa. Однако почему-то уже на расстоянии 30 метров обрывается сеанс связи. Радиокоманды не поступают. При этом тот же код драйвера SX1262 на покупной плате T-Beam работает аж на 15 километров. Это было специально проверено на полигоне. Оказывается на нашей плате схемотехник сильно отошел от reference дизайна и заложил ключ BGS12WN6E6327XTSA1 с инверсными логическими уровнями. В переводе на кухонный язык, когда LoRa трансивер передавал сигнал, антенна была просто отключена! Когда трансивер принимал, мультиплексор DA10 подключал антенну на передатчик и ASIC ничего не принимал. Вернее антенна была в виде дорожки PCB длинной 7 миллиметров. Вот так...

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

Итог

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

В программировании микроконтроллеров до того, как вы дойдете до всяких так алгоритмов и структур данных пройдет год, а то и два колупания с электропитанием, макетированием, тактированием, прерываниями и прочим. А проблемы отсутствия всяческого Link(а) в программировании микроконтроллеров и вовсе красной нитью прошивают всю мою карьеру. Особенно в случае беспроводных интерфейсов.

На сам процесс программирования уходит максимум 10...30 процентов времени. В основном приходится что-то бесконечно ремонтить.

Суммируя аппаратные баги, это либо ошибка в схемотехнике или ошибка в топологии.

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

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

Ссылки

#

Текст

URL

1

Как Чинить Программные Ошибки?


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

3

Космическая ошибка: $370 000 000 за Integer overflow

https://habr.com/ru/companies/pvs-studio/articles/306748/

4

Невероятно неуловимый баг по электропитанию в Пастильде

https://habr.com/ru/companies/thirdpin/articles/466533/

2

16 Способов Отладки и Диагностики FirmWare

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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
У вас были неочевидные баги при программировании микроконтроллеров?
90% да126
10% нет14
Проголосовали 140 пользователей. Воздержались 36 пользователей.