12

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

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

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

编辑

其中一位开发人员激活了 WWDG 看门狗,它正在重置硬件,然后我才能从故障处理程序中获取我的信息。这是一个硬故障,因为通过指向错误位置的指针调用函数。但是,我会保留这个问题,希望有人能提供更多详细信息(或有关它的材料),以便从保存在硬故障(@dwelch 想法)中的寄存器中指向 C 代码。

4

6 回答 6

11

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

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

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

于 2012-01-10T17:57:26.740 回答
3

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

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

于 2015-02-17T05:02:45.340 回答
1

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

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

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

于 2012-01-10T16:38:10.650 回答
1

当您说重置时,我认为您点击了重置向量,而不是中断或处理程序之一。您是说它确实会重置芯片并重新启动您的软件,还是说它挂在某个地方?还是您的向量表都指向重置向量?

如何进行取决于您真正看到的内容,您需要更清楚或更具体,或者您可能需要帮助来解决这个问题。

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

cortex-m 非常好,因为您可以指向 C 代码。如果您认为您遇到了异常,请将其指向一个例程,该例程可以帮助您确定您所处的模式,链接寄存器可能有该信息,或者某处的 csr,将其打印出来并进入无限循环. 用这个通用调试函数的地址填充向量表的未使用部分。

从那里您需要弄清楚为什么会遇到该异常,例如,它可能类似于未对齐的访问。可能是您在完全设置处理程序之前尝试初始化设备时产生了中断,谁知道呢。

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

于 2012-01-10T18:48:50.790 回答
1

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

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( ;; );
}

在进入硬故障之前,我正在使用它来获取寄存器的任何值。如果您愿意,还可以添加更多寄存器。

于 2018-04-09T16:19:46.937 回答
0

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

有一些方法可以欺骗您的 IDE,有时会为您提供调用堆栈。我会提供细节,但我忘了把它们写下来,所以我忘记了。我认为它必须手动将堆栈指针从一个影子寄存器更改为另一个。

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

于 2012-02-07T01:59:07.367 回答