如何在STM32设备中调试意外重置?

时间:2012-01-10 16:07:30

标签: embedded interrupt-handling cortex-m3 stm32

我正在使用STM32F107芯片在C中进行一些开发,并且在某些时候,当我调用特定函数时,设备开始重置。我没有调试器,我的调试只是串口上的纯文本。

我已经使用了一些其他微控制器,我可以访问寄存器以查看复位的原因,但我似乎无法找到此设备的等效物。我知道Cortex-M3的硬件异常,但我不知道其中一个是否被触发,因为当我在这些处理程序中时,我似乎无法通过usart发送文本(可能是因为我的TX函数使用中断?)。

所以,我决定问一下在这个设备上比我更有经验的人:调试这些情况通常会做些什么?

修改

其中一位开发人员激活了WWDG监视器,并且在我从故障处理程序获取信息之前,它正在重置硬件。由于指向错误位置的指针调用函数,这是一个硬故障。但是,我会保留这个问题,希望有人会提供更多详细信息(或关于它的材料),以便从保存的寄存器中指回C代码,比如硬故障(@dwelch idea)。

6 个答案:

答案 0 :(得分:11)

Cortex M3具有出色的故障处理功能,可让您的生活更轻松。在发生故障时,它会自动堆叠多个寄存器,如PC和LR,故障状态寄存器会告诉您总线故障地址等信息。

您应该实现一个好的故障处理程序(例如,这里的硬故障处理程序:http://blog.frankvh.com/2011/12/07/cortex-m3-m4-hard-fault-handler/)来打印堆栈寄存器并调试故障状态寄存器。

您应该使用UART进行打印,只需编写自己的简单自定义版本的printf,以便从不依赖于中断的错误处理程序中使用。只需将字节直接写入uart Tx数据寄存器并轮询字节完成。

答案 1 :(得分:3)

除了已经提到的用于调试的中断处理程序之外,一些ST微处理器还有一个复位源寄存器,您可以在上电时读取(即复位后)。对于皮层M族(m0 / m3 / m4),寄存器是RCC_CSR。 http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf

不幸的是,您无法知道具体信息,例如硬故障,但它会告诉您看门狗(窗口或独立)是否已经跳闸。

答案 2 :(得分:1)

鉴于您没有调试器,我建议您在微控制器上找到一些外围设备来帮助您。也许您有一个可以切换的LED或一个没有使用的简单GPIO引脚,您可以连接到示波器。如果您足够慢地切换GPIO引脚(不高于1 Hz,可能更慢,具体取决于仪表),您可以使用电压表而不是示波器。将代码一次一个地切换到每个异常处理程序中的LED或GPIO引脚,直到您将其跟踪为止。如果您有多个GPIO引脚可用,则可以加快该过程。您还可以为导致重置的特定函数编写包装器。包装器函数将发送在执行中断函数之前启用的中断列表。这样你就不必浪费时间测试未启用的那些。

在这种情况下,GPIO引脚的一个优点是它们不需要中断。最好远离任何需要中断的东西(如本例中的USART)。如果重置是由更高优先级的异常引起的,则调试代码将永远不会执行。

重置是由未初始化的指针引起的。将函数指针设置为零会导致执行看起来很像重置。如果是这种情况,USART初始化代码可能在USART完全传输字节之前执行,这会使USART在这种情况下无法用作调试工具。

答案 3 :(得分:1)

当你说复位时,我认为就你击中复位向量而言,不是其中一个中断或处理程序。你是说它确实重置芯片并重新启动你的软件,或者你是说它挂在某个地方?或者你的向量表都指向复位向量吗?

如何继续取决于你真正看到的东西,你需要更清楚或具体,或者你想要帮助解决这个问题。

通常我将未使用的向量映射到一个简单的代码挂起代码,这些代码分支到自身。稍后我可能会将其中的一些重新映射到实际代码中。

cortex-m非常好,因为你可以指向C代码。如果你认为你得到一个异常,它指向一个例程,它可以抓取一些东西,帮助你找出你所处的模式,链接寄存器可能有这个信息,或者某个地方的csr,打印出来并进入一个无限循环。使用此通用调试功能的地址填充向量表的未使用部分。

从那里你需要弄清楚为什么你要击中那个例外,例如它可能就像一个未对齐的访问。可能是你在完全设置处理程序之前尝试初始化设备时产生了一个中断,谁知道呢。

在您完成此操作时,使用更多答案或信息编辑您的问题。

答案 4 :(得分:0)

遗憾的是,对于STM32来说,“正确”的事情是不可行的。那就是放入一个知道源代码的大型异常处理程序,并且可以展开堆栈并为您提供导致错误的完整调用堆栈和行号。这需要将应用程序中的所有调试信息添加到STM32的闪存中,这是不切实际的。

有些方法可以欺骗你的IDE,有时会给你调用堆栈。我会详细说明,但是我忘了写下来,所以我忘记了。我认为必须手动将堆栈指针从一个影子寄存器更改为另一个影子寄存器。

我通常做的是在硬故障异常向量上放置一个断点,然后在断点命中时查看所有寄存器。考虑一下谋杀的法医证据,用塑料炸药完成。寄存器的值将为您提供想法。以0x20000000开头的寄存器值是RAM地址。以0x08000000开头的寄存器值是Flash地址。打开反汇编程序并输入这些地址。它可能会直接转到那些内存位置的变量或函数。如果这没有帮助,那么看看堆栈指针。查看堆栈指针处的内存位置,并执行相同的操作。我总是找到足够的弹片来定位发生异常的函数。

答案 5 :(得分:0)

您可以使用以下代码进行调试。

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。如果您愿意,还可以添加更多寄存器。