首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

stm32 hard fault及堆栈探究(2)

stm32 hard fault及堆栈探究(2)

在Time_Display()中通过用sprintf将地址赋值给变量(即代码中注视掉的sprintf语句),并在LCD上显示的办法观察到,栈内的变量分布情况为:

可以看出,栈从内存地址高位向低位生长,参数在栈底,变量按照定义的顺序依次往上摞。系统给buf多留了两字节的空间,其余变量(包括函数参数timevar和局部变量TXX)在内存中依次紧密排列,没有出现windows中将函数回退地址的入栈时间放于参数之后,使参数和变量之间有四字节空隙的情况。这说明函数的回退地址和一些寄存器的入栈保存另有其他时机。同时注意到,代码中有用sprintf取得变量地址的语句时,工作正常,不会进入hardfault。因此有必要比较两段代码对内存空间造成的影响。
1. 进入hard fault是在Time_Show()函数一个循环执行完毕时。因此有必要看一下汇编,了解具体对寄存器和内存的数据读写操作:
[cpp] view plaincopy

  •    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 plaincopy

  •    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 plaincopy

  •    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。综上,函数可以继续执行而不会出错。
继承事业,薪火相传
返回列表