完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
程序开发阶段,调试程序开发阶段,排除编程障碍,无法少而少或少地对这些异常所造成的错误进行调试,无法将这些异常的出现或出现的故障大量溢出'、'经常分类下标越界'''数学异常异常';而本篇错误则主要进入Hardfault以来进行分析的错误来源。 一机M核心 ARM RIS机器过去高级机器指令集、ARM处理器、Cortex-C机器:高级机器、更早机器指令集,ARM RISC机器,是一个精密指令集(RISC)处理程序器件架构,其阶系列广泛地使用在嵌入式设计中。 而对于ARM Cortex-M是众多架构处理器中,低位的,由安谋控股所授权的这组内核的特征为ARM架构。以及高品质的顶级品质的千万顶级精品,其中包括这款Cortex-M0+、Cortex-M10+、Cortex-M10+、Cortex-M0+、Cortex-M1、Cortex-M、Cortex-M、Cortex-M、Cortex-M、Cortex-M、Cortex-M、Cortex-M、Cortex-M、Cortex-M的顶级设计Cortex-M4Cortex-M47、Cortex35P和Cortex333、Cortex35P和Cortex-M4/Cortex3-M3/M35P的内核有处理器的选项,如果内核有M/M-Cortex5、M-M-Cortex5、M-Cortex-5、Cortex2-M4-M4/Cortex2-M4-M4/M35P的选项。 如标题,其中 Cortex-M3/M4/M7 同属ARMv7 -M架构,再细分一点,Cortex-M3是ARMv7-M架构,Cortex-M4 / Cortex-M7实现是ARMv7E-M架构。 二、ARMv7-M和ARMv7E-M架构的区别 如果芯片有玩都过Cortex-M4/M7系列的芯片或多或少知道Cortex-M4/M7比Cortex-M3更多的DSP跟硬件的处理器,以下是ARMv7E-M架构的拓展功能介绍: 由于ARMv7E-M是ARM7-M的一个拓展子集,所以下面统称ARMv7-M为“ARMv7-M”和“ARMv7E-M”的集合。 三、ARM 1、通用(R0) - R12) R0-R7被称为高组。所有的字被低调用它们。它们的长是32位,之后的最终值是不可预料的。R8 -R12被称为高组。这是因为只有很少的 16 位拇指指令能够访问,32 位拇指指令则可以访问,32 位拇指指令则不受它们位限制。它们的字也是长的,而且以后的最终值是不可预料的。 2、指针指针( SP 在v7-M架构中,共有两个SP_进程和SP_进程,ARM又把这个属性称为13。
)作为调用程序时的返回地址(LR)作为调用地址1、把这个连接(PC程序发送)4、把PC(PC程序发送)调用。它根据 R15 执行指令访问到 ARM 的时间(每 4 个字节),并直接根据指令状态指令处理指令将指令加载到 PC 中。描述:R0-R12、SP、PC被称为Arm核心服务和这些描述的详细描述R0-R15。可以看《ARMv7-M架构参考手册》B1.4章寄存器部分、更多异常定义1、异常类型2、异常更新在入栈和服务量操作完成,之后,更新跟踪流程的示例:
3、异常返回值 在进入异常服务后,将自动更新LR的特殊值为EXC_RETURN。这是一个高28位全为1的值,只有[3:0]的值有特殊含义,如下图所示。当PC出现异常时,服务器会启动这个异常设置,当程序出现异常时,是进程的 异常返回序列。因为LR的值是由内核自动执行的,因为没有特殊需求,就不要它了。入口的Armv 7 -M系列画面保证值是4个字节的镜头,至少在一个标准的黑白分辨率范围内,并且可以强制执行此任务。 配置和控制画面的 CCR。STK 指示的入口部分,决定着一个不同的位置: 4 位 RW 到 8 位。 该是由编译器是否决定 ,在这个下一个,它的替换值是由编译器决定的 • RO,在这种情况下是 RAO,8 字节 SP 下表示。 Arm 不支持的或使用 4 字节 SP 它表示。 注册 — CCR。STKALI 的下一个地方要分析异常的时候进入它的信息框以及处理我们在上面的一个额外的位置(如果有的话) ) 六、HardFault异常分析处理 HardFault是一种通用故障,它适用于所有不能被任何其他异常机制处理的故障类。通常,HardFault用于不可恢复的系统故障,尽管这不是必需的,而且 HardFault的某些用途可能是可恢复的。HardFault永久启用,优先级固定为 -1。 方法一:先来个简单的,使用第三方组件 “cm_backtrace” 项目地址:https://github.com/armink/CmBacktrace 怎么用这里就不说了,毕竟官方已经写得很清楚了,而且还有相应的 Demo例程。 使用该方法的特点是不需要过多的关注更底层的东西,只需移植好后配置相应的功能就好了,而且它可以离线(脱离仿真器)来寻找错误点;但是前提是你的串口正常,而且还需要预留部分内存供其执行。 方法二:仿真情况下的 bug寻找 如果是平常的 while循环执行,导致挂掉了无法切换任务,那么我们可以轻松通过上下文切换寻找问题;但是在 HardFault异常中它并不给你显示执行错误的所在地方,而是直接跳到 HardFault中断中,这往往让我们头大。 Keil平台的可以看: https://www.keil.com/appnotes/files/apnt209.pdf IAR平台的可以看: https://www.iar.com/knowledge/support/technical-notes/debugger/debugging-a-hardfault-on-cortex-m/ GCC平台的可以看: 方法三。。。 方法三:通过 ARM 寄存器逆向推导 使用该方法的前提是获取到第五个大点最后一张图展示的核心寄存器(R0-R3、R12、LR、PC、xPSR以及原 SP)的数值。 1、常见的核心寄存器数据获取 一般地,我们会使用以下代码嵌入到 HardFault中断中,使其在入口处打印服务异常时各核心寄存器的值: /* Exception frame without floating-point storage * hard fault handler in C, * with stack frame location as input parameter */ void hard_fault_handler_c(unsigned int * hardfault_args) { unsigned int stacked_r0; unsigned int stacked_r1; unsigned int stacked_r2; unsigned int stacked_r3; unsigned int stacked_r12; unsigned int stacked_lr; unsigned int stacked_pc; unsigned int stacked_psr; //Exception stack frame stacked_r0 = ((unsigned long) hardfault_args[0]); stacked_r1 = ((unsigned long) hardfault_args[1]); stacked_r2 = ((unsigned long) hardfault_args[2]); stacked_r3 = ((unsigned long) hardfault_args[3]); stacked_r12 = ((unsigned long) hardfault_args[4]); stacked_lr = ((unsigned long) hardfault_args[5]); stacked_pc = ((unsigned long) hardfault_args[6]); stacked_psr = ((unsigned long) hardfault_args[7]); printf ("[Hard fault handler]n"); printf ("R0 = %xn", stacked_r0); printf ("R1 = %xn", stacked_r1); printf ("R2 = %xn", stacked_r2); printf ("R3 = %xn", stacked_r3); printf ("R12 = %xn", stacked_r12); printf ("LR = %xn", stacked_lr); printf ("PC = %xn", stacked_pc); printf ("PSR = %xn", stacked_psr); #ifndef CW printf ("BFAR = %xn", (*((volatile unsigned long *)(0xE000ED38)))); printf ("CFSR = %xn", (*((volatile unsigned long *)(0xE000ED28)))); printf ("HFSR = %xn", (*((volatile unsigned long *)(0xE000ED2C)))); printf ("DFSR = %xn", (*((volatile unsigned long *)(0xE000ED30)))); printf ("AFSR = %xn", (*((volatile unsigned long *)(0xE000ED3C)))); #else printf ("BFAR = %xn", (*((volatile unsigned int *)(0xE000ED38)))); printf ("CFSR = %xn", (*((volatile unsigned int *)(0xE000ED28)))); printf ("HFSR = %xn", (*((volatile unsigned int *)(0xE000ED2C)))); printf ("DFSR = %xn", (*((volatile unsigned int *)(0xE000ED30)))); printf ("AFSR = %xn", (*((volatile unsigned int *)(0xE000ED3C)))); #endif for(;;) {} } /* The prototype shows it is a naked function - in effect this is just an assembly function. */ void HardFault_Handler( void ) __attribute__( ( naked ) ); /* The fault handler implementation calls a function called prvGetRegistersFromStack(). */ void HardFault_Handler(void) { #ifdef CORTEX_M3_M4_M7 asm volatile( " tst lr, #4 n" /* Check EXC_RETURN[2] */ " ite eq n" " mrseq r0, msp n" " mrsne r0, psp n" "b hard_fault_handler_c n" : /* no output */ : /* no input */ : "r0" /* clobber */ ); #else asm volatile( "movs r0, #4 n" "mov r1, lr n" "tst r0, r1 n" /* Check EXC_RETURN[2] */ "beq 1f n" "mrs r0, psp n" "ldr r1,=hard_fault_handler_c n" "bx r1 n" "1:mrs r0,msp n" "ldr r1,=hard_fault_handler_c n" : /* no output */ : /* no input */ : "r0" /* clobber */ ); #endif } Note: 值得注意的是 void HardFault_Handler(void);函数是相应的 HardFault中断函数,不同的厂家会定义不同的名称。 对于不同的编译器,如:armcc、iar、gcc for arm等,需要把相应的关键字替换掉(eg:asm、naked)。 naked 关键字拓展: https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_jhg1476893564298.htm https://zhuanlan.zhihu.com/p/33933891 2、逆向定位入口 如果有看过方法二的两个链接,那么就很容易理解以下的分析了: 确保你能正常获取输出数据(包括但不限于仿真查看、串口打印、SWO输出、SEGGER_RTT输出等等)。 由于我们在第一点修改过代码,所以可以直接查看 LR和 PC两个的值;因为这两个的值是关键。 使用 addr2line软件定位故障代码位置(使用方法:https://sourceware.org/binutils/docs-2.27/binutils/addr2line.html#addr2line),addr2line属于 GNU Binutils组件之一,获取可以从方法一里面的 tools文件夹里获取,也可以从 安装路径GNU Tools ARM Embedded5.4 2016q3binarm-none-eabi-addr2line.exe 提取出来。 3、使用演示 先制造一个 HardFault: 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("z:%dn", z); } /************************************************ 函数名称 : main 功 能 : 主函数入口 参 数 : 无 返 回 值 : 无 *************************************************/ int main( void ) { #ifdef DEBUG debug(); #endif BaseType_t xReturn = pdPASS; /* 定义一个创建信息返回值,默认为 pdPASS */ prvSetupHardware(); fault_test_by_div0(); /* Start the tasks defined within this file/specific to this demo. */ xReturn = xTaskCreate( (TaskFunction_t)prvUser_Task, /* 任务入口函数 */ (const char *)"prvUser_Task", /* 任务名字 */ (uint16_t)configMINIMAL_STACK_SIZE, /* 任务栈大小 */ (void *)NULL, /* 任务入口函数参数 */ (UBaseType_t)mainCREATOR_TASK_PRIORITY, /* 任务的优先级 */ (TaskHandle_t *)UserTaskCreate_Handle ); /* 任务控制块指针 */ if(pdPASS == xReturn) { /* Start the scheduler. */ vTaskStartScheduler(); } /* Will only get here if there was not enough heap space to create the idle task. */ return 0; } /*----------------------------- End -----------------------------*/ 接着可以观察到程序已经跑到 HardFault_Handler里并输出相应信息了: 然后利用addr2line,执行命令 arm-none-eabi-addr2line.exe -e "任务截图" -a -f "相应的值" (注:实际命令不需要引号“ ”,应用可看下图) :
运行方式进行分析 ,直接执行这种方法不需要修改什么,在 HardFault 中直接执行什么方法修改。分析核心任务。 同样的,还是先制造一个HardFault,沿用方法三的fault_test_by_div0();函数代码,直到进入HardFault_Handler之后,(注意,这里的HardFault_Handler中断并没有像三个那样的函数嵌入代码,而是保持它的样子)获取: 接着跟的一样,用各种核心的值(ps:这里的这些的仿真查看): 这里就像方法一样直接拿LR和PC这几个值来用了,必须进行层层分析:
|
|
|
|
只有小组成员才能发言,加入小组>>
3316 浏览 9 评论
2995 浏览 16 评论
3494 浏览 1 评论
9060 浏览 16 评论
4088 浏览 18 评论
1180浏览 3评论
605浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
600浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2335浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1896浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 09:35 , Processed in 1.203900 second(s), Total 80, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号