标题:
stm32 hard fault及堆栈探究(2)
[打印本页]
作者:
yuyang911220
时间:
2015-4-28 21:29
标题:
stm32 hard fault及堆栈探究(2)
在Time_Display()中通过用sprintf将地址赋值给变量(即代码中注视掉的sprintf语句),并在LCD上显示的办法观察到,栈内的变量分布情况为:
可以看出,栈从内存地址高位向低位生长,参数在栈底,变量按照定义的顺序依次往上摞。系统给buf多留了两字节的空间,其余变量(包括函数参数timevar和局部变量TXX)在内存中依次紧密排列,没有出现windows中将函数回退地址的入栈时间放于参数之后,使参数和变量之间有四字节空隙的情况。这说明函数的回退地址和一些寄存器的入栈保存另有其他时机。同时注意到,代码中有用sprintf取得变量地址的语句时,工作正常,不会进入hardfault。因此有必要比较两段代码对内存空间造成的影响。
1. 进入hard fault是在Time_Show()函数一个循环执行完毕时。因此有必要看一下汇编,了解具体对寄存器和内存的数据读写操作:
[cpp]
view plain
copy
208: if (TimeDisplay == 1)
209: {
0x08000E72 4C05 LDR r4,[pc,#20] ; @0x08000E88
210: uint32_t Counter = 0;
0x08000E74 2500 MOVS r5,#0x00
0x08000E76 6820 LDR r0,[r4,#0x00]
0x08000E78 2801 CMP r0,#0x01
0x08000E7A D1FC BNE 0x08000E76
211: Counter = RTC_GetCounter();
0x08000E7C F7FFFE2C BL.W RTC_GetCounter (0x08000AD8)
212: Time_Display(Counter);
0x08000E80 F7FFFF98 BL.W Time_Display (0x08000DB4)
213: TimeDisplay = 0;
0x08000E84 6025 STR r5,[r4,#0x00]
214: }
0x08000E86 E7F6 B 0x08000E76
在这一段中,R4存放变量TimeDisplay的地址,R0为TimeDisplay的值。循环的最后一步,寄存器R4中的地址加0作为新地址,R5从内存中的该新地址取值存入。如果R4指向的地址非法,则读取该地址很有可能产生hard fault。
2.查看Time_Display()的汇编
(1)添加了显示变量地址的代码,而无hard fault的情况。
主循环的起始部分汇编代码如下,每次进入循环只需将Time_Display()时入栈的回退地址弹出作为PC。
[cpp]
view plain
copy
200: void Time_Show(void)
0x08000E44 B009 ADD sp,sp,#0x24
0x08000E46 BD00 POP {pc}
0x08000E48 517F STR r7,[r7,r5]
刚进入Time_Display()时的汇编代码如下,进入时将R0和LR寄存器压入栈中。
[cpp]
view plain
copy
165: void Time_Display(uint32_t TimeVar)
0x08000DAC E8BD4010 POP {r4,lr}
0x08000DB0 F7FFBEEA B.W RTC_WaitForLastTask (0x08000B88)
166: {
0x08000DB4 B501 PUSH {r0,lr}
0x08000DB6 B088 SUB sp,sp,#0x20
167: uint32_t THH = 0, TMM = 0, TSS = 0;
168: char buf[10];
此时STM32芯片寄存器和内存的情况如下图所示。
根绝汇编中把R0和LR压入栈中的指令,对应LR和R0的值,在局部变量所在内存空间寻找,可以发现LR最先入栈,接着是函数参数和其余变量,这和最开始打印出的各变量地址也是吻合的。因此,如果buf越界不是太多,只是改写了其余局部变量的数据,不影响回退地址。另外,查看函数所有汇编代码,没有对R4的操作。至函数执行完成并返回,R4的值始终为0x20000000。综上,函数可以继续执行而不会出错。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0