【Cortex-M3】中断处理时栈空间操作过程分析

前言

本文通过一个代码实例深度分析中断发生时,stm32做了哪些操作用于保护现场,中断退出时又做了哪些操作用于恢复现场。

基础知识

Cortex-M3寄存器组如下:
在这里插入图片描述

【Cortex-M3】C语言函数调用过程汇编层面分析 一文中介绍到,寄存器组中:
R0-R3R12LRxPSR被称为“调用者保护寄存器”,
R4-R11为“被调用者保护寄存器”
这一规则来自AAPCS(ARM架构过程调用标准)。
在函数调用过程中,子函数会保证执行前后R4-R11内容不变,而中断服务函数也是一种子函数,所以在中断发生时,处理器只负责保证在进入中断服务函数前和退出中断服务函数后,R0-R3R12LRxPSR的内容不变,另外处理器需要保证中断服务函数执行完可以跳转回中断发生时的代码位置继续执行。
所以,中断发生时,处理器会首先进行压栈操作,对一些寄存器的内容进行保存,压栈内容如下:
在这里插入图片描述
压栈顺序为:xPSR返回地址(中断产生时的PC值)、LRR12R3R2R1R0
退出中断时,将这些内容依次出栈,也就恢复到了中断发生前的状态。
接下来通过实验对这一过程进行详细分析。

实验

本实验在stm32单片机上实现了一个外部中断,正常情况下会在主函数中执行,当开发板上的按键被按下时,单片机会产生一个外部中断,并跳转到中断服务函数执行。
主函数:

void mymain()
{
	volatile int a = 1;
	volatile int b = 2;
	volatile int c = 3;
	volatile int d;

	while (1)
	{
		d = a + b + c;
	}
}

外部中断服务函数:

void EXTI0_IRQHandler(void)
{
	int a = 1;
	int b = 2;
	int c = 3;
	int d = 4;
	int e = 5;
	int f;
	
	e = a + b + c + d + e;
	EXTI_ClearITPendingBit(EXTI_Line0);
}

主函数对应汇编代码如下:
在这里插入图片描述

中断服务函数对应的汇编代码如下:
在这里插入图片描述
想查看汇编代码可以设置Keil编译后生成.dis文件。

接下来在中断服务函数开始位置打个断点:
在这里插入图片描述
按下按键触发中断后,显示当前寄存器的状态如下:
在这里插入图片描述
可以看到当前的栈指针SP0x20000714,接下来用Keil5找到RAM空间中这个位置,内容如下:
在这里插入图片描述
在中断服务函数汇编代码开始处有段如下代码:

0x0800067c:    e92d41f0    -..A    PUSH     {r4-r8,lr}

所以栈指针后面的6个32位数据是这5个寄存器,也就是:

寄存器Value
R40x00000000
R50x20000100
R60x00000000
R70x00000000
R80x00000000
LR0xFFFFFFF9

以上这些寄存器是中断服务函数压栈的,接下来就是处理器在执行中断时自动压栈的,也就是

寄存器Value
R00x00000003
R10x00000003
R20x00000001
R30x28010800
R120x00000001
LR0x08000217
返回地址0x080001E4
xPSR0xFFFFFFF9

查看mymain函数对应的汇编代码,0x080001E4处对应为:

0x080001e4:    4408        .D      ADD      r0,r0,r1

所以中断退出后,首先执行的是mymian函数中的这段代码。

接下来通过单步调试退出中断服务函数,返回到mymain,如下:
在这里插入图片描述
这时寄存器组的内容为:
在这里插入图片描述
可以看到R1-R8LRxPSR与中断发生时处理器自动压栈的、中断服务函数开始执行时压栈的内容一模一样,只有R0PC值不一样,这是因为Keil5是在C语言层面单步执行的,没有在汇编层面单步执行,mymain函数相关位置处的汇编代码如下:

0x080001dc:    e9dd1002    ....    LDRD     r1,r0,[sp,#8]
0x080001e0:    4408        .D      ADD      r0,r0,r1
0x080001e2:    9901        ..      LDR      r1,[sp,#4]
0x080001e4:    4408        .D      ADD      r0,r0,r1
0x080001e6:    9000        ..      STR      r0,[sp,#0]
0x080001e8:    e7f8        ..      B        0x80001dc ; mymain + 16

可以看到中断返回后到断点处,处理器执行了0x080001e40x080001e60x080001e8三个位置的机器码,在0x080001e4ADD r0,r0,r1这个操作,正好对应了
R0 = R0 + R1 = 3 + 3 = 6
B 0x80001dc 也就对应了将PC赋值为0x80001dc

总结

中断发生后,压栈行为分两步:
第一步:处理器自动压栈“调用者保护寄存器”,顺序为xPSRLRR12R3R2R1R0
第二步:中断服务函数首先压栈LR(这时的LR已经和进入中断前的LR不一样了),然后压栈一些用到的“被调用者保护寄存器”R4-R11

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值