首页 / 资讯中心 / 文章详情

PN学堂GD32课程第15篇——HardFault异常

PN学堂GD32课程第15篇——HardFault异常 ★ FEATURED ARTICLE
15.1 ARM内核中SP PC LR的作用增加一个空指针bug如下下载之后按键失效RS485串口也死了代码卡死在了HardFault先复习一下这个知识点然后这节课学习一下其他的在ARM单片机的内核当中除了SysTick、NVIC、DWT这些模块的控制寄存器之外还有和下图所示的这些32 位通用寄存器General Purpose RegisterGPR通用目的寄存器R0、R1、R2、R3通用寄存器在函数调用约定里专门用来传递前 4 个函数参数R0 同时存放函数返回结果属于调用者保存寄存器Caller-saved函数内可以随便改写。这也是为什么单片机C程序推荐函数形参不超过4个的核心原因。R4~R11通用寄存器被调用者保存寄存器Callee-saved如果函数要使用它们必须先入栈保存退出前恢复原值。R12通用寄存器也叫IPIntra-Procedure Call Scratch Register过程间临时寄存器临时寄存器。R13SPStack Pointer栈指针寄存器虽然编号属于通用寄存器但功能固定。R14LRLink Register链接寄存器保存函数返回地址。R15PCProgram Counter程序计数器指向当前取指地址。引入下面的例程进行后续讲解#include stdint.h #include stdio.h #include usb2Com_drv.h #include delay.h int32_t DivideCal(int32_t value1,int32_t value2) { return value1 / value2; } int32_t Process(void) { volatile int32_t aVal 10; volatile int32_t bVal 2; return DivideCal(aVal, bVal); } #define PrintfEnable 0 int main(int argc, char *argv[]) { volatile int *SCB_CCR (volatile int *)0xE000ED14; *SCB_CCR |(14); volatile int32_t mVal Process(); #if PrintfEnable DelayInit(); Usb2ComDrvInit(); #endif for(;;) { #if PrintfEnable printf(mVal %d\r\n,mVal); DelayNms(1000); #endif } }注意ARM单片机一般使用16 bit的thumb指令集减小镜像文件大小因此每条代码指令1都占用2个bytes的大小所以PC指针逐步调试时的步长一定是2。0xE000ED14这个地址上存在一个特殊的寄存器Cortex-M 内核里这堆 0xE000E_xxx 的地址都是系统控制块SCB的寄存器不属于任何片上外设是 ARM 内核自带的这里需要手动打开方便定位除0的问题。15.2 调试除数为0导致的死机问题什么是HardFault产生HardFault的原因基于栈帧分析HardFault把上一讲的代码除数改为0然后下载到开发板上下面用JLink Commander开始调试按照提示交互识别到cortex -M4内核之后直接HALT:重点看SP MSP和LR寄存器数值注意不要用寄存器窗口里的 PC 去查寄存器窗口的 PC 0x08001408它在 HardFault_Handler 内部gd32f30x_it.c:59 的 while(1)只能说明“已经进 HardFault 了”。 真正的出错地址是异常入口时硬件压栈的那份 PCMSP 0x20003334基本帧LR 0xFFFFFFF9非扩展帧计算器算一下用JLINK去找然后再开一个powershellarm-none-eabi-addr2line.exe -e build\DEMO.elf -f -a -i 0x08000BD5定位到15.3 调试空指针导致的死机问题第三个“任务”的函数指针指向了NULL#include stdint.h #include stdio.h #include sys/_intsup.hW #include app_log.h #include modbus_app.h #include systick.h #include delay.h #include led_drv.h #include key_drv.h #include usb2Com_drv.h #include sensor_drv.h #include fmc_drv.h #include eeprom_bsp.h #include norflash_bsp.h #include hmi_app.h #include sensor_app.h #include store_app.h typedef struct { uint8_t run; //调度标志;1:运行,0:挂起 uint16_t timCount; //时间片计数值 uint16_t timRload; //时间片重载值 void (*pTaskFuncCb)(void); //函数指针用来保存业务功能模块函数地址 }TaskComps_t; static TaskComps_t g_TaskComps[] { {0,5,5,HmiTask}, {0,1000,1000,SensorTask}, //{0, 1, 1, ModbusTask}, {0, 1, 1, NULL}, }; #define TASK_NUM_MAX (sizeof(g_TaskComps)/sizeof(g_TaskComps[0])) static void TaskHandler(void) { uint8_t i; for(i0;iTASK_NUM_MAX;i) { if(g_TaskComps[i].run) { g_TaskComps[i].run 0; g_TaskComps[i].pTaskFuncCb(); } } } static void TaskScheduler(void) { uint8_t i; for(i0;iTASK_NUM_MAX;i) { if(g_TaskComps[i].timCount) { g_TaskComps[i].timCount--; if(g_TaskComps[i].timCount0) { g_TaskComps[i].run 1; g_TaskComps[i].timCount g_TaskComps[i].timRload; } } } } static void AppInit(void) { TaskScheduleCbReg(TaskScheduler); ModbusAppInit(); InitSysParam(); } static void DrvInit(void) { DelayInit(); SystickInit(); LedDrvInit(); KeyDrvInit(); Usb2ComDrvInit(); SensorDrvInit(); EepromBspInit(); NorflashBspInit(); } int main(int argc, char *argv[]) { DrvInit(); AppInit(); FlashDrvTest(); for(;;) { TaskHandler(); } }同样的套路没用GDB发现代码卡在了HardFault重复操作
阅读完成 · 觉得有帮助?
咨询建站