STM32 加 FreeRTOS 崩溃排查指南:读硬故障寄存器、中断优先级陷阱、栈和堆溢出、DMA 与内存放置的错误、优先级反转,以及能记录复位原因的看门狗设计。
设备随机复位、跑一天就卡死、或者在传输中途停住,是固件里最昂贵的一类问题,主要是因为证据会随着复位一起消失。第一条规矩就是别再丢掉它。在 STM32 这样的 Cortex-M 芯片上,每一次严重的崩溃都会经过故障处理函数,处理器已经保存了能解释崩溃的状态。要做的,就是在任何东西把它覆盖之前,把这个状态读出来。
默认的硬故障处理函数是一个空循环,什么信息也给不了你。把它换成一个处理函数:先找出故障时使用的栈,再把它交给一个能读取保存的寄存器和故障状态寄存器的函数。
__attribute__((naked)) void HardFault_Handler(void)
{
__asm volatile (
"tst lr, #4\n"
"ite eq\n"
"mrseq r0, msp\n"
"mrsne r0, psp\n"
"b hard_fault_c\n");
}
void hard_fault_c(uint32_t *stack)
{
volatile uint32_t pc = stack[6]; /* 出事的地方 */
volatile uint32_t lr = stack[5]; /* 谁调用的 */
volatile uint32_t cfsr = SCB->CFSR; /* 故障类型 */
volatile uint32_t hfsr = SCB->HFSR;
volatile uint32_t mmfar = SCB->MMFAR; /* 出错地址(若有效) */
volatile uint32_t bfar = SCB->BFAR;
(void)pc; (void)lr; (void)cfsr; (void)hfsr; (void)mmfar; (void)bfar;
for (;;) { } /* 在这里打断点,或者保存这些值然后复位 */
}
程序计数器的值指向出错的那条指令,到映射文件或反汇编里去查。可配置故障状态寄存器会告诉你是未定义指令、未对齐访问、数据访问的总线错误,还是内存管理违规,地址寄存器则告诉你是哪个地址。对于现场的产品,把这些值写到复位后仍然保留的 RAM 区或闪存里,下次启动时上报:一行崩溃记录,胜过一周的猜测。
这是 STM32 上最常见的 FreeRTOS 故障,它造成的死机和硬故障,可能相隔好几天才出现一次。在 Cortex-M 上,优先级数字越小,优先级越高;FreeRTOS 定义了一个阈值 configMAX_SYSCALL_INTERRUPT_PRIORITY。调用任何 FreeRTOS 函数(哪怕是"FromISR"版本)的中断,其优先级数字必须大于等于这个阈值(也就是逻辑上优先级不高于它)。高于阈值的中断完全不能碰内核。
每个任务有自己的栈,而在 FreeRTOS 里,创建任务时传入的大小以字为单位,不是字节,这是栈小了四倍的经典原因。溢出的任务会破坏它下面的任何东西,崩溃发生在后来、发生在别处。
如果任务、队列和缓冲区在运行时反复创建和删除,堆就会碎片化,最终某次分配失败,而且通常发生在你没有测试到的时刻。定义 malloc 失败钩子,随时间记录堆剩余的统计,凡是只创建一次的东西尽量用静态分配。长期存在的对象只在启动时动态创建,之后绝不再创建,可以消除这一整类问题。
有些 STM32 系列的内存区域,处理器可以用,DMA 却访问不到。例如 F4 系列的核心耦合 RAM,速度快、适合放栈和变量,但 DMA 不能访问;把缓冲区放在那里,传输会静默失败或触发故障。要查看链接脚本里栈和 DMA 缓冲区实际落在哪里。在带数据缓存的芯片上,DMA 缓冲区还必须按缓存行对齐,并在传输前后做清理或失效,否则处理器会看到旧数据。在我们的 STM32F407 工业控制项目里,FreeRTOS、通过 SDIO 访问 SD 卡上的 FAT 文件系统和外扩 SRAM 结合在一起,缓冲区放置和任务栈大小,决定了持续记录下的稳定性。
独立看门狗把死机变成复位;关键是让这次复位带有信息。不要在定时器中断里或某一个低优先级循环里喂狗,因为那只能证明定时器是好的。让每个重要任务通过标志或事件位来报到,只有所有任务都报到了,才由一个监督任务去喂狗。有任务没有报到时,在复位发生之前,记录下是哪个任务缺席。下次启动时,读复位原因寄存器(看门狗、掉电、软件、引脚),和保存的故障数据一起记录。
当原因是时序上的相互作用,而不是某一个坏指针时,你需要的是任务切换和中断的跟踪。许多 STM32 芯片通过调试口支持指令和事件跟踪,识别 RTOS 的跟踪工具能显示哪个任务在运行、哪个中断抢占了它,以及内核在等什么。让系统在高温下连续运行数天,并对通信和存储施加有意的压力,这样的硬件测试,能抓住短时间台架测试永远发现不了的故障。
我们编写并抢救 FreeRTOS 和裸机下的 STM32 固件,包括长期运行的工业产品和低功耗产品。请看我们的固件开发服务,以及嵌入式软件架构与 RTOS 设计指南。
联系我们获取定制化解决方案和报价