PSoC4 SoftUart & CmbackTrace – Hardfault
使用CmBackTrace定位PSoC4的hardfault!
背景
调试PSoC4 时很不巧遇到了Hardfault,debug发现程序运行了一段时间后跳到了Cm0plusStart.c中的CY_ISR ( IntDefaultHandler ) 中,查看vector发现,NMI中断和hardfault的处理函数都是调用该函数,于是想到使用CmbackTrace来定位是哪条语句引起的!
CmBackTrace简介
CmBacktrace (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下:
- 支持的错误包括:
- 断言(assert)
- 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
- 故障原因 自动诊断 :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
- 输出错误现场的 函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈;
- 支持 裸机 及以下操作系统平台:
- RT-Thread
- UCOS
- FreeRTOS
- 根据错误现场状态,输出对应的 线程栈 或 C 主栈;
- 故障诊断信息支持多国语言(目前:简体中文、英文);
- 适配 Cortex-M0/M3/M4/M7 MCU;
- 支持 IAR、KEIL、GCC 编译器;
Note: 作者朱天龙 (Armink),开源了FlashDB、EasyLogger、CmBacktrace、EasyFlash、SFUD等等非常好用的嵌入式组件,感恩~ 作者Github:https://github.com/armink ,前往作者的Github下载下最新版本的CmBackTrace,开始移植。
PSoC Soft UART – printf
PSoC Creator中神奇的uart组件 – Software Transmit UART,只用一个IO,汇编指令实现的软uart tx。选择它并配置波特率9600,UARTDbg_Start() 后即可使用。
printf输出重定向至软串口发送:
//Heap Size需要改大 -- 0x0400?
int _write(int file, char *ptr, int len)
{
int i;
file = file;
for (i = 0; i < len; i++)
{
UARTDbg_PutChar(*ptr++);
}
return len;
}
Note: Heap Size一定要改大!!!
程序中包含下头文件 “stdio.h”,然后就可以愉快的调用printf啦。
CmBackTrace移植
- 将cm_backtrace相关的文件添加到工程中,配置cmb_cfg.h:
#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_
/* print line, must config by user */
#define cmb_println(...) printf(__VA_ARGS__);printf("\r\n")
/* enable bare metal(no OS) platform */
#define CMB_USING_BARE_METAL_PLATFORM
/* cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M0
/* enable dump stack information */
#define CMB_USING_DUMP_STACK_INFO
/* language of print information */
#define CMB_PRINT_LANGUAGE CMB_PRINT_LANUUAGE_ENGLISH
#endif /* _CMB_CFG_H_ */
- 修改Cm0plusStart.c中的 //&IntDefaultHandler, /* The hard fault handler 3 */
注释掉并修改为&HardFault_Handler, 使用CmBackTrace的HardFault_Handler处理。 - 修改cmb_def.h中CMB_CSTACK_BLOCK_START和CMB_CSTACK_BLOCK_END的定义,
Creator使用的是GNUC的GCC,所以修改#elif defined(GNUC)中:#define CMB_CSTACK_BLOCK_START __cy_heap_end #define CMB_CSTACK_BLOCK_END __cy_stack
- 移植demo中的hardfault测试函数:除0和unlign,结果发现都无法引起hardfault,增加一个abnormal_access的测试函数:
void fault_test_by_unalign(void) {
volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
volatile int * p;
volatile int value;
*SCB_CCR |= (1 << 3); /* bit3: UNALIGN_TRP. */
p = (int *) 0x00;
value = *p;
printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
p = (int *) 0x04;
value = *p;
printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
p = (int *) 0x03;
value = *p;
printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
}
void fault_test_by_div0(void) {
volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
int x, y, z;
*SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */
x = 10;
y = 0;
z = x / y;
printf("div0: %d\r\n", z);
}
void fault_test_abnormal_access()
{
uint32_t addr = 0xCCCCCCCC;
uint32_t val = *(volatile uint32_t*)addr;
printf("abnormal_access: %d! \r\n",val);
}
运行测试
main中调用,编译、下载运行:
cm_backtrace_init("SoftUart", HARDWARE_VERSION, SOFTWARE_VERSION);
uartDbgInit();
fault_test_by_div0();
fault_test_by_unalign();
fault_test_abnormal_access();
打开串口调试助手:(除0和非对齐访问,都可以正常运行..)
将add2link拷编译目录下并打开命令行程序,运行其中提示的
addr2line -e SoftUart.elf -a -f 000006b4 000006e6 000007a8 0000001a
找到对应的 main.c : 64行:
就是它引起的Hardfault !有了它,再也不怕hardfault了~
by the way:
修改Cm0plusStart.c,重新编译生成时有时修改了的HardFault会被默认替换掉,使用时注意下即可。欢迎交流~