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

Самые Эпичные Баги при Программировании Микроконтроллеров

Уровень сложностиПростой
Время на прочтение10 мин
Количество просмотров23K

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

У каждого программиста микроконтроллеров с опытом формируется коллекция решенных багов. Баги появляются и исчезают, как вспышки на Солнце. Некоторые из них весьма эпичные. Многие уже читали про ошибку переполнения при конвертации 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 мегагерц и побежал понятный лог на ожидаемой битовой скорости .

17--Пад теплоотвода. Как то раз надо было написать драйвер для микросхемы SPI-NAND Flash W25M02GV памяти 256 MByte. Как водится, боевая электронная плата ещё в трассировке, а драйвер для ASIC-а W25M02GV написать и отладить как-то надобно. Отладочных плат с чипом W25M02GV, как назло нет в продаже. Пришлось делать прототип. Попросил схемотехника сделать какой-нибудь прототип. Схемотехник принес мне сборку на красной платочке из чип и дип. Я думаю, все уже поняли в чем косяк?...

Я соединил прототип с отладочной платой и с удивлением обнаружил, что SPI регистры не читаются. Три часа разбирался с настройками GPIO и SPI. Искал ошибку в коде. Оказывается, что у чипа W25M02GV внизу есть огромный металлический PAD теплоотвода.

При монтировании на макетку-переходник боковые 4 пина просто коротили на этот пин теплоотвода. Решением стало сдувание чипа и сборка другого прототипа. На зелёном прототипе достучаться до регистров, как раз удалось без проблем.

15-- Ток которого нет. Существует чип VNQ7E100AJ - это драйвер микросхемы 4х канального high-side. High-side - это когда провод коммутируют на шину силового питания. Проблема в том, что чип VNQ7E100AJ при отключённой нагрузке и скважности PWM на входе менее 100 % на проводе CS измеряется ток, которого на самом деле нет (ибо ничего не подключено).

Вот, что показывала стрелка осциллографа
Вот, что показывала стрелка осциллографа

При внимательном изучении спеки выяснилось, что это оказывается такая функция чипа VNQ7E100AJ. Просто VNQ7E100AJ так своеобразно сообщает об ошибке. В случае ошибки выставляет напряжение V_SENSEH. По факту происходит подключение Pull-up к пину CS. На пине CS получалось 5....6.6V. Ошибкой являлось событие Open-Load.

А прошивка глядя на показания ADC думала, что это ток в силовой цепи так подскочил. Решением стало отказаться от чипов VNQ7E100AJ в следующей ревизии схемотехники ECU в пользу другого драйвера.

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 

18-- Циклопический snprintf(). Прошивка занимает 50kByte+, когда должна занимать всего 4 kByte. Это было видно утилитой size при изучении объектных файлов в папке с проектом.

riscv-none-elf-size.exe -Bdt  build/*.o | sort

Все объектные файлы проекта занимают максимум 2,5 kByte. Оказывается в UART драйвере оказалась функция snprintf(), компоновщик подтянул её реализацию из SDK от компилятора которая и заняла 46kByte Flash памяти! Решением оказалось закомментировать функцию snprintf и бинарь стал 4728 байт. Вот так.

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

16--Заклинивший CAN трансивер. У нас в организации коллега написала CAN драйвер для микроконтроллера YTM32B1ME05G0MLQ. При интеграции этого драйвера в сборку выяснилось, что CAN не работает на повторный приём. На железо тут пенять бессмысленно, так как драйвер FlexCAN из SDK от вендора на этом же железе работает безупречно.

Соединил перемычками CAN0 - CAN5. Инициировал отправку пакета в CAN0. Первый вызов CAN_WriteFrame() дал 1 подтвержденный пакет на оссциллографе. CAN5 принял пакет. Второй вызов CAN_WriteFrame для CAN0 привел к тому, что CAN0 стал без конца непрерывно флудить отправку, как будто к шине ничего не подключено. Драйвер CAN5 просто не принимает второй пакет от CAN0. Не происходит выставление ACK бита подтверждения.

Оказывается, что после срабатывания прерывания CAN5_Interrupt_16_31_Handler
CAN трансивер аппаратно переключается в состояние Freeze. Согласно спеке в Freeze режиме трансивер игнорирует RX. В CAN драйвере после каждого приема CAN трансивер остается в замороженном режиме (Freeze). Решением оказалось принудительное сбрасывание состояния Freeze внутри обработчика прерываний по приёму пакета.

static RESULT CAN_ReloadLowLevel(CanHandle_t* const Node) {
    RESULT ret = RESULT_NOT_OK;
    if (Node) {
        if (Node->CANx->MCR.B.HALT) {
            /* The CPU should clear HALT after initializing the message buffers and
               the control registers CAN_CTRL1 and CAN_CTRL2 */
            Node->CANx->MCR.B.HALT = 0;
            ret = RESULT_OK;
        }
    }
    return ret;
}

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.67% да175
9.33% нет18
Проголосовали 193 пользователя. Воздержались 43 пользователя.
Теги:
Хабы:
Всего голосов 92: ↑87 и ↓5+110
Комментарии189

Публикации

Работа

Программист С
41 вакансия

Ближайшие события