Почти детективная история одной «кровавой» ошибки

s4x_vxz0mwf-d4p_3oplku7dvys.jpeg

Дополнение статьи «Размещение кучи FreeRTOS в разделе CCMRAM для STM32», и в продолжение серии статей про различные полезности для STM32 (1, 2 и 3), хочу обратить внимание на одну особенность работы с CCM RAM памятью, которая может быть причиной совершенно не очевидных ошибок в работе устройств, одна из которых выпила у автора достаточно много крови, так что с чистой совестью её действительно можно назвать «кровавой».

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

Вот только костыль не помог. Помогало только физическое выключения питания, а программная перезагрузка микроконтроллера не помогала! Более того, даже перезагрузка с помощью кнопки RESET иногда не срабатывала! Устройство перезагружалось, но неправильное поведение устройства никуда не исчезало и в итоге все равно приходилось отключать питание физически.


Так как даже аппаратный сброс не всегда помогает, я сперва грешил на проблемы с железом. Ведь если проблема остается и после перезагрузки по кнопке RESET и по NVIC_SystemReset (), то самое логичное, это искать ошибку в аппаратной части. Если бы не одно, но очень веское НО, подобное поведение было у всех устройств, а не на единичном экземпляре оборудования.

Детальный анализ проблемы показал, что такое поведение (некорректная работа устройства после программной перезагрузки), было характерно только для одного класса микроконтроллеров, а именно для STM32F4xx, и тот же самый алгоритм вполне корректно работал на микроконтроллерах младших серий (STM32F1xx и STM32F2xx).

Изучение под отладчиком позволило выявить участок кода, который игнорируется (не выполняется) после программной перезагрузки прошивки. Это позволило найти фактическую причину неработоспособности устройств. Данный код сравнивал текущие параметры работы линий связи и при необходимости настраивал их под заданные параметры работы, если текущие настройки отличались от требуемых.

По всему выходило, что логика программы почему-то ошибочно считала, что текущие настройки линий связи соответствуют заданным (сравнивались параметры, сохраненные в памяти), хотя после программной перезагрузки это было уже не так! Причину неработоспособности оборудования была выявлена и оставалось понять, почему сбоит логика работы прошивки.

Вы наверно уже догадались, что причина была в том, что куча для STM32F4xx была перенесена в регион CCMRAM, а параметры работы оборудования хранились именно там.

uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__((section(".ccmram"))) = {0};


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

А дальше все было просто, достаточно при перезагрузке или при старте приложения обнулять данную секцию памяти (наверно было бы правильнее поправить ассемблерный загрузчик, но я сделал это с помощью обычного memset), и все стало корректно работать.

Имейте ввиду, что раздел CCMRAM не очищается загрузчиком прошивки и после сброса микроконтроллера содержит остаточную информацию от предыдущего запуска, что может являться причиной не очевидных ошибок в работе устройств!

image-loader.svg

© Habrahabr.ru