分类: 嵌入式
2016-08-31 21:17:44
环境: keil 5.14
MCU: 一款M0的处理器
主要是解决以下问题: 函数嵌套调用过程中LR的处理,以及中断发生时LR的处理。
测试代码如下
uint8_t add(uint8_t a, uint8_t b){
uint8_t c;
c = a+b;
return c;
}
uint8_t test_fun(uint8_t a,uint8_t b, uint8_t c, uint8_t d,uint8_t e){
uint8_t ret;
ret = add(a,b);
return ret;
}
int main(void){
uint8_t i;
uint8_t k;
i = add(1,2);
k = test_fun(2,3,4,5,6);
while(1);
return 0;
}
Main函数汇编代码
从main汇编代码来看,参数1和2放入r0和r1中然后调用add函数,
BL指令在调用add函数时会同时将返回地址存入LR寄存器中
Add汇编代码如下:
计算了r0+r1的和 之后保存到r0,因为 bl调用add的时候就在lr中自动保存了返回地址,所以这里直接通过bx lr就可以让函数返回到main中
之后main执行test_fun函数
从main汇编中看到,参数 2 3 4 依次被保存到r0-r3中,参数6被保存到栈中,这也遵循ATPCS规定的函数传参规则
最后是test_fun的汇编代码,因为test_fun中用到了r4-r7所以 首先push保存了一下这几个寄存器的值,以方便后面恢复。 同时也将 LR的值压栈了。 前面add的汇编中我们看到是没有对LR压栈的,这里却有是因为test_fun里面还调用了add函数,通过BL函数调用时回改变LR的值,所以test_fun的第一句就 将这个LR保存了。
之后提取参数a ,e两个参数,然后调用add函数,最后通过pop来返回到man
对参数e的提取,因为 M0 栈向低地址增长,而函数入口将r4-r7,lr又入栈了,所以之前存的参数e,即数据6就SP+4*5地址处
综上:
对于LR的处理,主要是看 函数内部是否有其他函数的调用,如add函数,其内部并无其他函数调用,而 BL add时就已经将返回地址存入LR中了,所以整个过程中LR就没处理,最后函数返回直接 BX LR就可以了
而test_fun函数内部存在其他函数调用,既然存在其他函数调用就会破坏LR的值,所以函数第一句就先将 LR保存在栈中,而函数返回直接出栈将值放入PC中即可。
PS:对于中断,M0,M3等会自动将r0-r3 r12,LR, 返回地址PC,程序状态寄存器自动压栈。
即中断并不像普通的函数调用,中断的返回地址是在PC中并被入栈,那么LR保存的意义何在?
原因在于,M0,M3等处理器的中断返回机制,在将上面所说的中断发生后 自动将一些寄存器值入栈后,LR的会被更新为异常返回特殊值(EXC_RETURN),作用是中断返回时,LR将这个值放入PC便可以触发返回机制。具体的查阅M0,M3相关手册
也就是发生中断后,LR的值会被修改。
那么如果中断是 在test_fun这种内部有其他函数调用的函数 执行时发生的,并且第一句PUSH已经执行了,那么因为LR中的返回地址其实已经被push到栈中了,这个时候中断发生了,保不保存并不影响,即使中断触发会导致LR的值被破坏,但是毕竟我们已经保存了。
但是如果中断发生在add这类函数内部没有其他函数调用的 函数中时,因为add函数中并未自己在函数一开始就保存好自己的返回值,这个时候中断发生如果不保存LR的值,LR的值随后又会被修改,那么就会导致出错了。
综上,对于中断触发时保存LR的 目的在于 LR虽然不是中断自己的返回值,但是之前一个状态的返回值。