赞
踩
IRQ Interrupt Request 外部中断
整个IRQ中断服务函数的流程可以简洁地概括为以下几个关键步骤:
保存环境:
获取中断源:
模式切换:
执行中断处理:
system_irqhandler
,处理具体的中断逻辑。中断结束处理:
恢复环境并退出:
这些步骤确保了系统在响应中断时能够保持稳定和可预测,同时保护了任务的执行状态不受中断影响。
push {lr} /* 保存链接寄存器(lr)的值,即返回地址 */
push {r0-r3, r12} /* 保存通用寄存器r0到r3和r12 */
- R0-R3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
- R4-R11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
- R12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复
r12。- R13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
- R14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
- R15 是程序计数器 PC。它不能用于任何其它用途。
- 注意:在中断程序中,所有的寄存器都必须保护,编译器会自动保护R4~R11
mrs r0, spsr /* 读取保存程序状态寄存器(SPSR)到r0 */
push {r0} /* 将SPSR的值压栈保存 */
- CPSR: Current Program Status Register 当前程序状态寄存器
- SPSR: Saved Program Status Register 程序状态保存寄存器 当特定的异常中断发生时,SPSR用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后,可以用 SPSR 中保存的值来恢复 CPSR。
mrc p15, 4, r1, c15, c0, 0 /* 从CP15协处理器中获取GIC基地址 */
add r1, r1, #0x2000 /* 计算GIC的CPU接口端基地址 */
ldr r0, [r1, #0xC] /* 从GICC_IAR寄存器读取当前的中断号 */
push {r0, r1} /* 保存中断号和GIC基地址 */
Cortex-A7 Technical ReferenceManua.pdf
P68Cortex-A7 Technical ReferenceManua.pdf
P138Cortex-A7 Technical ReferenceManua.pdf
P138Cortex-A7 Technical ReferenceManua.pdf
P188cps #0x13 /* 切换到SVC(Supervisor Call)模式 */
push {lr} /* 保存SVC模式的链接寄存器 */
【正点原子】LMX6U嵌入式Linux驱动开发指南V1.81.pdf
P294ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf
P1139ldr r2, =system_irqhandler /* 将C语言编写的中断处理函数地址加载到r2 */
blx r2 /* 使用链接寄存器跳转到该函数执行 */
system_irqhandler
是实际处理中断的C语言函数,可以根据r0中的中断号执行相应的操作。pop {lr} /* 恢复SVC模式的lr寄存器 */
cps #0x12 /* 切回IRQ模式 */
pop {r0, r1} /* 恢复r0, r1寄存器值 */
str r0, [r1, #0x10] /* 向GICC_EOIR寄存器写入中断结束信号 */
pop {lr} 指令用于从堆栈中取出(pop)一个值并将其存入到链接寄存器(Link Register, 简称 LR)中
为什么中断发生时,要进入SVC模式后进行中断函数的执行,执行完成后又进入IRQ模式
写结束信号
Cortex-A7 Technical ReferenceManua.pdf
P189ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf
P214pop {r0} /* 恢复保存的SPSR */
msr spsr_cxsf, r0 /* 将SPSR的值恢复到处理器状态寄存器 */
pop {r0-r3, r12} /* 恢复通用寄存器 */
pop {lr} /* 恢复链接寄存器 */
subs pc, lr, #4 /* 通过链接寄存器设置程序计数器,完成中断返回 */
subs pc, lr, #4
指令安全地返回到中断点。核心原因: ARM 处理器采用流水线执行指令,当前执行的指令地址和下一条将要执行的指令地址并不一致。
具体解释:
lr - 4
赋值给 pc
: 为了确保程序正常执行,需要将 LR 寄存器的值减去 4,然后赋值给 PC 寄存器。这样,PC 指针就会指向中断发生时正在执行的指令的下一条指令,从而保证程序能够正常执行。示例:
0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC
MOV R2, R3
,导致程序执行错误。lr - 4
赋值给 PC,即 PC = 0X2004
,处理器会从 0X2004 处的指令 MOV R2, R3
开始执行,保证程序正常执行。总结:
将 lr - 4
赋值给 pc
是为了解决 ARM 处理器流水线执行指令带来的问题,确保中断处理完成后能够正常返回到中断发生的位置继续执行程序。
@ IRQ中断服务函数 IRQ_Handler: push {lr} /* 保存lr地址,Link Register, R14 寄存器*/ push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */ mrs r0, spsr /* 读取spsr寄存器 */ push {r0} /* 保存spsr寄存器 */ mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中 * 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49 * Cortex-A7 Technical ReferenceManua.pdf P68 P138 */ add r1, r1, #0X2000 /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */ ldr r0, [r1, #0XC] /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器, * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据 * 这个中断号来绝对调用哪个中断服务函数 */ push {r0, r1} /* 保存r0,r1 */ cps #0x13 /* 进入SVC模式,允许其他中断再次进去 */ push {lr} /* 保存SVC模式的lr寄存器 */ ldr r2, =system_irqhandler /* 加载C语言中断处理函数到r2寄存器中*/ blx r2 /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */ pop {lr} /* 执行完C语言中断服务函数,lr出栈 */ cps #0x12 /* 进入IRQ模式 */ pop {r0, r1} str r0, [r1, #0X10] /* 中断执行完成,写EOIR */ pop {r0} msr spsr_cxsf, r0 /* 恢复spsr */ pop {r0-r3, r12} /* r0-r3,r12出栈 */ pop {lr} /* lr出栈 */ subs pc, lr, #4 /* 将lr-4赋给pc */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。