Как отлаживать неожиданные сбрасывания на устройстве STM32?

Я делаю некоторую разработку на C с чипом STM32F107, и в какой-то момент устройство начинает сбрасываться, когда я вызываю определенную функцию. У меня нет отладчика, и моя отладка - это просто текст через последовательный порт.

Я использовал некоторые другие микроконтроллеры, в которых я смог получить доступ к регистру, чтобы увидеть причину перезагрузки, но я не могу найти эквивалент для этого устройства. Я знаю об аппаратных исключениях Cortex-M3, но я не знаю, запускается ли один из них, поскольку я не могу отправить текст поверх usart, когда я внутри этих обработчиков (возможно, потому, что мой TX функции используют прерывания?).

Итак, я решил попросить людей с большим опытом, чем я в этом устройстве: что обычно делается для отладки таких ситуаций?

РЕДАКТИРОВАТЬ

Один из разработчиков активировал сторожевой таймер WWDG, и он переписывал оборудование, прежде чем я смог получить информацию от обработчиков ошибок. Это был Hard Fault из-за вызова функции указателем, который указывал на неправильное место. Тем не менее, я буду держать этот вопрос в надежде, что кто-то даст более подробную информацию (или материал об этом), чтобы указать на код C из регистров, сохраненных в, скажем, Hard Fault (идея @dwelch).

Ответ 1

Cortex M3 обладает отличными функциями обработки ошибок, которые облегчат вашу жизнь. При попадании на ошибку он автоматически складывает несколько регистров, таких как ПК и LR, а регистры состояния неисправности будут сообщать вам такие вещи, как адрес ошибки шины и т.д.

Вы должны реализовать хороший обработчик ошибок (например, жесткий обработчик ошибок здесь: http://blog.frankvh.com/2011/12/07/cortex-m3-m4-hard-fault-handler/), чтобы распечатать регистровых регистров и регистров состояния отладки.

Вы должны использовать UART для печати, просто напишите свою собственную пользовательскую версию printf для использования от вашего обработчика ошибок, которая не зависит от прерываний. Просто напишите байты непосредственно в регистр данных uart Tx и опрос для завершения байта.

Ответ 2

Помимо того, что было сказано о обработчиках прерываний для отладки, некоторые ST-микрофоны также имеют регистр исходных данных сброса, который вы можете прочитать при включении питания (то есть после сброса). Для семейства коры M (m0/m3/m4) регистр RCC_CSR. http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf

К сожалению, вы не сможете узнать, были ли такие особенности, как жесткая ошибка, но это скажет вам, сработал ли сторожевой таймер (окно или независимый).

Ответ 3

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

Одним из преимуществ выводов GPIO в этом случае является отсутствие прерывания. Лучше избегать всего, что требует прерывания (например, ваш USART в этом случае). Если сброс вызван более высоким приоритетом, ваш код отладки никогда не будет выполнен.

Также часто бывает, что сброс вызван неинициализированным указателем. Указатель функции, установленный на ноль, заставит выполнение выглядеть так же, как сброс. Если это так, код инициализации USART, вероятно, выполняется до того, как байт может быть полностью передан USART, который сделает этот USART бесполезным в качестве инструмента отладки в этом случае.

Ответ 4

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

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

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

cortex-m очень приятен тем, что вы можете указать на код C. Если вы считаете, что получаете исключение, укажите, что оно указывает на рутину, которая захватывает что-то, что поможет вам понять, в каком режиме вы находитесь, в реестре ссылок может быть такая информация или где-то csr, распечатать это и перейти в бесконечный цикл, Заполните неиспользуемые части векторной таблицы адресом этой общей функции отладки.

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

отредактируйте свой вопрос с помощью большего количества ответов или информации, когда вы работаете с этим.

Ответ 5

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

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

То, что я обычно делаю, это поставить точку останова на вектор исключения жестких ошибок, а затем просмотреть все регистры, когда точка перерыва попадает. Рассмотрите их судебные доказательства от убийства, совершенные с использованием пластиковых взрывчатых веществ. Значения регистров дадут вам идеи. Значения регистра, начинающиеся с 0x20000000, - это адреса ОЗУ. Значения регистра, начинающиеся с 0x08000000, - это адреса Flash. Откройте дизассемблер и введите эти адреса. Вероятно, это будет прямо на переменную или функцию в этих местах памяти. Если это не поможет, посмотрите на указатель стека. Посмотрите на ячейки памяти в указателе стека и выполните тот же трюк. Я всегда нашел достаточно осколков для определения местоположения функции, где происходило исключение.

Ответ 6

Вы можете использовать нижеприведенный код для отладки.

void HardFault_Handler(void)
{
    __asm volatile
       (
           " tst lr, #4                                                \n"
           " ite eq                                                    \n"
           " mrseq r0, msp                                             \n"
           " mrsne r0, psp                                             \n"
           " ldr r1, [r0, #24]                                         \n"
           " ldr r2, handler2_address_const                            \n"
           " bx r2                                                     \n"
           " handler2_address_const: .word prvGetRegistersFromStack    \n"
       );

  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

Также добавьте это.

void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )
{
    /* These are volatile to try and prevent the compiler/linker optimising them
    away as the variables never actually get used.  If the debugger won't show the
    values of the variables, make them global my moving their declaration outside
    of this function. */
    volatile uint32_t r0;
    volatile uint32_t r1;
    volatile uint32_t r2;
    volatile uint32_t r3;
    volatile uint32_t r12;
    volatile uint32_t lr; /* Link register. */
    volatile uint32_t pc; /* Program counter. */
    volatile uint32_t psr;/* Program status register. */

    r0 = pulFaultStackAddress[ 0 ];
    r1 = pulFaultStackAddress[ 1 ];
    r2 = pulFaultStackAddress[ 2 ];
    r3 = pulFaultStackAddress[ 3 ];

    r12 = pulFaultStackAddress[ 4 ];
    lr = pulFaultStackAddress[ 5 ];
    pc = pulFaultStackAddress[ 6 ];
    psr = pulFaultStackAddress[ 7 ];

    /* When the following line is hit, the variables contain the register values. */
    for( ;; );
}

Я использую это, чтобы получить какое-либо значение регистра перед переходом в hardfault. Вы также можете добавить больше регистров, если хотите.