15.1 ARM内核中SP PC LR的作用
增加一个空指针bug如下:

下载之后按键失效,RS485串口也死了,代码卡死在了HardFault:

先复习一下这个知识点:

然后这节课学习一下其他的,在ARM单片机的内核当中除了SysTick、NVIC、DWT这些模块的控制寄存器之外,还有和下图所示的这些32 位通用寄存器(General Purpose Register,GPR,通用目的寄存器):

- R0、R1、R2、R3 :通用寄存器,在函数调用约定里专门用来传递前 4 个函数参数;R0 同时存放函数返回结果 ,属于调用者保存寄存器(Caller-saved) ,函数内可以随便改写。
这也是为什么单片机C程序推荐函数形参不超过4个的核心原因。 - R4~R11 :通用寄存器,被调用者保存寄存器(Callee-saved),如果函数要使用它们,必须先入栈保存,退出前恢复原值。
- R12 :通用寄存器,也叫IP(Intra-Procedure Call Scratch Register,过程间临时寄存器),临时寄存器。
- R13 :SP(Stack Pointer,栈指针寄存器),虽然编号属于通用寄存器,但功能固定。
- R14 :LR(Link Register,链接寄存器),保存函数返回地址。
- R15 :PC(Program Counter,程序计数器),指向当前取指地址。
通过下面的例程进行实际讲解:
cpp
#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 |=(1<<4);
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的问题。