01_异常与中断的概念引入与处理流程
1.1 使用生活实例引入中断
假设有个大房间里面有小房间,婴儿正在睡觉,他的妈妈在外面看书。 问:这个母亲怎么才能知道这个小孩醒?
-
过一会打开一次房门,看婴儿是否睡醒,然后接着看书
-
一直等到婴儿发出声音以后再过去查看,期间都在读书
第一种方法叫做查询方式:
-
优点:简单
-
缺点: 累
如何写程序?
while(1)
{
1 read book(读书)
2 open door(开门)
if(小孩还在睡)
return(继续读书)
else
照顾小孩
}
第二种方法叫中断方式:
-
优点:不累
-
缺点:复杂
如何写程序:
while(1)
{
read book
}
中断服务程序() //核心问题:如何被调用?
{
处理照顾小孩
}
1.2 母亲如何处理中断
我们还是看看母亲被小孩哭声打断如何照顾小孩?
母亲的处理过程
-
平时看书
-
发生了各种声音,如何处理这些声音
-
有远处的猫叫(听而不闻,忽略)
-
门铃声有快递(开门收快递)
-
小孩哭声(打开房门,照顾小孩)
-
-
母亲的处理
-
只会处理门铃声和小孩哭声
-
先在书中放入书签,合上书(保存现场)
-
去处理 (调用对应的中断服务程序)
-
继续看书(恢复现场)
-
-
不同情况,不同处理
-
对于门铃:开门取快件
-
对于哭声:照顾小孩
1.3 ARM系统中异常与中断处理流程
我们将母亲的处理过程抽象化:
-
母亲的头脑相当于CPU
-
耳朵听到声音会发送信号给脑袋
-
声音来源有很多种
-
有远处的猫叫,门铃声,小孩哭声
-
-
这些声音传入耳朵,再由耳朵传给大脑
-
除了这些可以中断母亲的看书,还有其他情况,比如:
-
身体不舒服
-
有只蜘蛛掉下来
-
对于特殊情况无法回避,必须立即处理
-
-
对于arm系统,异常与中断的硬件框图如下:
所有的中断源(按键、定时器等),它们发出的中断汇聚到中断控制器, 再由中断控制器发信号给CPU,告诉它发生了那些紧急情况。
除了这些中断,还有什么可以打断CPU的运行?
-
指令不对
-
数据访问有问题
-
reset信号
-
等等,这些都可以打断断CPU,这些被称为异常
-
中断属于一种异常
ARM系统中如何处理异常与中断?重点在于保存现场以及恢复现场, 处理过程如下:
-
保存现场(各种寄存器)
-
处理异常(中断属于一种异常)
-
恢复现场
细化一下,在ARM系统中如何使用异常(中断)?
-
初始化
-
设置中断源,让它可以产生中断
-
设置中断控制器(可以屏蔽某个中断,优先级)
-
设置CPU总开关,使能中断
-
-
执行其他程序:正常程序
-
产生中断,举例:按下按键--->中断控制器--->CPU
-
cpu每执行完一条指令都会检查有无中断/异常产生
-
发现有中断/异常产生,开始处理:
-
保存现场
-
分辨异常/中断,调用对于异常/中断的处理函数
-
恢复现场
-
不同的芯片,不同的架构,在这方面的处理稍有差别:
-
保存/恢复现场:cortex M3/M4是硬件实现的,cortex A7是软件实现的
-
CPU中止当前执行,跳转去执行处理异常的代码:也有差异
-
cortex M3/M4在向量表上放置的是函数地址
-
cortex A7在向量表上放置的是跳转指令
-
02_ARM架构中异常与中断的处理
1.1 处理流程是一样的
-
每执行完一条指令都会检查有无中断/异常产生
-
发现有中断/异常产生,开始处理:
-
保存现场
-
分辨异常/中断,调用对应的异常/中断处理函数
-
恢复现场
-
不同的芯片,不同的架构,在这方面的处理稍有差别:
-
CPU中止当前执行,跳转去执行处理异常的代码:也有差异
-
cortex M3/M4在向量表上放置的是函数地址
-
cortex A7在向量表上放置的是跳转指令
-
-
保存/恢复现场:cortex M3/M4是硬件实现的,cortex A7是软件实现的
1.2 cortex M3/M4
参考资料:DDI0403E_B_armv7m_arm.pdf、ARM Cortex-M3与Cortex-M4权威指南.pdf、PM0056.pdf
要想理解这个处理流程,需要从向量表说起。 向量,在数学定义里是有方向的量,在程序里可以认为向量就是一个数组,里面有多个项。 在ARM架构里,对于异常/中断,它们的处理入口会整齐地排放在一起。
1.2.1 M3/M4的向量表
M3/M4的向量表中,放置的是具体异常/中断的处理函数的地址。 比如发生Reset异常时,CPU就会从向量表里找到第1项,得到Reset_Handler函数的地址,跳转去执行。 比如发生EXTI Line 0中断时,CPU就会从向量表里找到第22项,得到EXTI0_IRQHandler函数的地址,跳转去执行。
-
跳转之前,硬件会保存现场
-
函数执行完毕,返回之后,硬件会恢复现场
; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors EXPORT __Vectors_End EXPORT __Vectors_Size __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler ; External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_IRQHandler ; PVD through EXTI Line detect DCD TAMPER_IRQHandler ; Tamper DCD RTC_IRQHandler ; RTC DCD FLASH_IRQHandler ; Flash DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line 0 DCD EXTI1_IRQHandler ; EXTI Line 1 DCD EXTI2_IRQHandler ; EXTI Line 2 DCD EXTI3_IRQHandler ; EXTI Line 3 DCD EXTI4_IRQHandler ; EXTI Line 4 DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 DCD ADC1_2_IRQHandler ; ADC1 & ADC2 DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 DCD CAN1_RX1_IRQHandler ; CAN1 RX1 DCD CAN1_SCE_IRQHandler ; CAN1 SCE DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 DCD TIM1_BRK_IRQHandler ; TIM1 Break DCD TIM1_UP_IRQHandler ; TIM1 Update DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD TIM4_IRQHandler ; TIM4 DCD I2C1_EV_IRQHandler ; I2C1 Event DCD I2C1_ER_IRQHandler ; I2C1 Error DCD I2C2_EV_IRQHandler ; I2C2 Event DCD I2C2_ER_IRQHandler ; I2C2 Error DCD SPI1_IRQHandler ; SPI1 DCD SPI2_IRQHandler ; SPI2 DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD USART3_IRQHandler ; USART3 DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend DCD TIM8_BRK_IRQHandler ; TIM8 Break DCD TIM8_UP_IRQHandler ; TIM8 Update DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare DCD ADC3_IRQHandler ; ADC3 DCD FSMC_IRQHandler ; FSMC DCD SDIO_IRQHandler ; SDIO DCD TIM5_IRQHandler ; TIM5 DCD SPI3_IRQHandler ; SPI3 DCD UART4_IRQHandler ; UART4 DCD UART5_IRQHandler ; UART5 DCD TIM6_IRQHandler ; TIM6 DCD TIM7_IRQHandler ; TIM7 DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 __Vectors_End
用“紧急电话簿”的比喻彻底理解这段代码:
1. 什么是向量表?
想象你的手机里有一个 “紧急电话簿”,里面记录了各种紧急情况对应的联系人。当发生地震、火灾或医疗急救时,你只需一键拨打对应的号码,无需临时查找。
ARM处理器的向量表就是这样的“紧急电话簿”,它告诉处理器:发生特定异常或中断时,该执行哪段代码。
2. 向量表的关键结构
这段代码定义了向量表的核心部分:
AREA RESET, DATA, READONLY ; 定义一块名为RESET的只读数据区
EXPORT __Vectors ; 导出符号,让其他文件能访问这个电话簿
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; 第一条:栈顶地址(急救指挥中心)
DCD Reset_Handler ; 第二条:复位处理函数(总指挥)
DCD NMI_Handler ; 第三条:不可屏蔽中断(最高优先级事件)
DCD HardFault_Handler ; 第四条:严重错误处理(灾难级故障)
... ; 其他条目...
__Vectors_End
3. 核心条目详解
| 条目 | 比喻 | 作用 |
|---|---|---|
__initial_sp | 急救指挥中心的位置 | 告诉处理器栈顶地址(急救时需要一个指挥台) |
Reset_Handler | 总指挥的电话 | 系统上电或复位时,第一个执行的代码(总指挥启动整个系统) |
NMI_Handler | 核按钮热线 | 不可屏蔽中断(如硬件故障),优先级最高,不能被其他电话打断 |
HardFault_Handler | 灾难应急小组 | 处理严重错误(如非法内存访问),系统最后的救命稻草 |
IRQ_Handler | 各部门专线(消防、医疗等) | 外部中断处理函数(如按键按下、定时器到期) |
4. 向量表的工作流程
-
系统上电:
- 处理器从内存地址0读取第一条条目
__initial_sp,设置栈顶指针。 - 读取第二条条目
Reset_Handler,跳转到复位处理函数开始执行。
- 处理器从内存地址0读取第一条条目
-
发生中断:
- 比如按下按键(外部中断),处理器自动查找向量表中对应的条目(如
EXTI0_IRQHandler)。 - 跳转到
EXTI0_IRQHandler函数执行按键处理代码。
- 比如按下按键(外部中断),处理器自动查找向量表中对应的条目(如
-
严重错误:
- 如果程序跑飞(如除零错误),触发
HardFault_Handler,执行错误恢复或日志记录。
- 如果程序跑飞(如除零错误),触发
5. 开发者需要做什么?
-
实现处理函数:
- 代码中所有以
_Handler结尾的符号(如Reset_Handler、NMI_Handler),都需要开发者编写具体实现。 - 例如:
void Reset_Handler(void) { // 初始化系统时钟、内存、外设... main(); // 最后跳转到main函数 }
- 代码中所有以
-
处理未使用的条目:
- 对于保留的条目(标记为
0或Reserved),需确保它们指向安全代码(如死循环):Default_Handler PROC B . ; 无限循环 ENDP
- 对于保留的条目(标记为
6. 技术细节补充
-
DCD指令:
相当于在内存中预留一个4字节的空间,并写入后续表达式的值(函数地址)。
例如:DCD Reset_Handler将Reset_Handler函数的地址写入向量表。 -
EXPORT指令:
将符号(如__Vectors)导出,使得链接器能正确引用这些地址。 -
地址对齐:
ARM要求向量表必须按特定地址对齐(如256字节对齐),但此代码未显式对齐,需依赖链接脚本配置。
7. 现实案例:STM32启动流程
- 芯片上电,从地址0读取栈顶指针(
__initial_sp)。 - 读取复位处理函数地址(
Reset_Handler),跳转执行。 Reset_Handler函数内初始化硬件,复制数据段,清零BSS段,最后调用main()。- 运行中发生中断(如定时器溢出),处理器查向量表,跳转至
TIM2_IRQHandler执行。
终极总结
- 向量表是处理器的“紧急电话簿”,定义了各种异常和中断的响应逻辑。
- 每个条目是一个4字节的函数地址,按固定顺序排列。
- 开发者必须实现所有处理函数,否则遇到未处理的中断会导致系统崩溃。
- 核心原则:先搭好应急框架(向量表),再填充具体应对措施(处理函数)!
1.2.2 M3/M4的异常/中断处理流程
发生异常/中断时,硬件上实现了这些事情:
-
保存现场:把被中断瞬间的寄存器的值保存进栈里
-
根据异常/中断号,从向量表中得到函数地址,跳转过去执行
-
函数执行完后,从栈中恢复现场
保存现场、分辨异常/中断、跳转执行,都是硬件实现的。 我们只需要在向量表中,把处理函数的地址填进去就可以了。
硬件承包了大部分的工作。
M3/M4的向量表中,存放的是函数地址。
1.3 cortex A7
参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf
实际上,以前的S3C2440属于ARM9处理器,它的异常/中断处理流程给cortex A7是一样的。
1.3.1 A7的向量表
A7的向量表中,放置的是某类异常的跳转指令。 比如发生Reset异常时,CPU就会从向量表里找到第0项,得到b reset指令,执行后就跳转到reset函数。 比如发生任何的中断时,CPU就会从向量表里找到第6项,得到ldr pc, _irq指令,执行后就跳转到_irq函数。
-
跳转之前,硬件只会保存CPSR寄存器
-
跳转之后,软件要保存现场
-
函数执行完毕,返回之前,软件恢复现场
_start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
用“急救中心分诊台”的比喻彻底理解这段代码:
1. 向量表 = 急救中心分诊台
想象医院的急诊室有一个分诊台,上面贴着各种紧急情况的处理指引:
- 第0号指引牌:突发停电 → 立即启动备用电源(
b reset) - 第1号指引牌:医生遇到未知病症 → 呼叫专家会诊(
ldr pc, _undefined_instruction) - 第6号指引牌:患者突发心梗 → 跳转到心脏急救室(
ldr pc, _irq)
ARM处理器的向量表就像这个分诊台,告诉CPU遇到不同异常时该跳转到哪里处理。
2. 关键代码解析
_start:
b reset ; 0号异常(复位):直接跳转到reset函数(备用电源)
ldr pc, _undefined_instruction ; 1号异常(未定义指令):加载处理函数地址
ldr pc, _software_interrupt ; 2号异常(软件中断)
ldr pc, _prefetch_abort ; 3号异常(取指错误)
ldr pc, _data_abort ; 4号异常(数据访问错误)
ldr pc, _not_used ; 5号异常(保留未用)
ldr pc, _irq ; 6号异常(普通中断)
ldr pc, _fiq ; 7号异常(快速中断)
3. 两种跳转方式的区别
| 指令 | 类比 | 适用场景 |
|---|---|---|
b reset | 直接跑向备用电源室 | 复位异常(处理函数在附近) |
ldr pc, _irq | 查分诊台地图后开车前往 | 普通中断(处理函数可能在远处) |
为什么复位用b,其他用ldr?
- 复位是系统启动后的第一个操作,
reset函数通常紧接向量表存放,用短跳转(b)足够。 - 其他异常处理函数可能分散在内存各处,需用长跳转(
ldr pc)加载绝对地址。
4. 硬件与软件的分工
(1) 硬件自动操作
- 保存CPSR:发生异常时,硬件自动将当前状态寄存器(CPSR)保存到备份寄存器(SPSR)。
类比:分诊台护士记录患者当前生命体征。
(2) 软件必须操作
- 保存现场:在异常处理函数中,手动保存其他寄存器(R0-R12, LR)。
类比:急救前把患者随身物品存到储物柜。irq_handler: push {r0-r12, lr} ; 保存寄存器 ... ; 处理中断 pop {r0-r12, pc}^ ; 恢复寄存器并返回(^表示恢复CPSR) - 恢复现场:处理完成后恢复寄存器和CPSR,用
pop {..., pc}^返回原程序。
类比:急救后取回物品,患者继续原行程。
5. 异常处理全流程
- 触发中断:按键按下(触发
EXTI0_IRQ)。 - 硬件响应:
- CPU暂停当前程序,切换到IRQ模式。
- 硬件保存CPSR到SPSR_irq,PC跳转到向量表第6项(
ldr pc, _irq)。
- 软件处理:
_irq函数保存寄存器,调用EXTI0_IRQHandler处理按键。- 处理完成后恢复寄存器和CPSR,返回原程序继续执行。
6. 常见问题解答
Q1: 为什么复位不用保存寄存器?
A1: 复位是系统从头开始运行,不需要恢复之前的现场(类似电脑重启后无需恢复之前的窗口)。
Q2: 中断处理函数末尾的^符号是什么?
A2: pop {..., pc}^中的^表示同时恢复CPSR寄存器(从SPSR_irq恢复回来)。
Q3: 如果忘记保存寄存器会怎样?
A3: 寄存器的值被覆盖,程序返回后行为不可预测(类似急救时弄丢患者钱包,导致后续行程无法继续)。
终极总结
- 向量表是异常处理的导航台:告诉CPU不同异常该跳转到哪里。
- 短跳转 vs 长跳转:复位用
b,其他用ldr pc。 - 保存现场是软件的责任:硬件只记录状态(CPSR),寄存器需手动保存恢复。
- 核心口诀:
异常来时先跳转,硬件存状态,软件保现场,处理完再复原! ✅
1.3.2 A7的异常/中断处理流程
发生异常/中断时,硬件上实现了这些事情:
-
CPU切换到对应的异常模式,比如IRQ模式、未定义模式、SVC模式
-
保存被中断时的CPSR到SPSR
-
CPSR:current program status register,当前程序状态寄存器
-
SRSR:saved program status register,保存的程序状态寄存器
-
-
跳到这个异常的入口地址去,执行指令,这通常是一条跳转指令
软件要做的事情就比较多了:
-
保存现场
-
分辨异常/中断
-
调用对应的处理函数
-
恢复现场
A7的向量表中,存放的是跳转指令。

03_异常处理深入分析_保存现场
1.1 回顾一下处理流程
CPU每执行完一条指令都会检查有无中断/异常产生,发现有中断/异常产生,开始处理:
-
保存现场
-
分辨异常/中断,调用对应的异常/中断处理函数
-
恢复现场
对于不用的处理器,具体的处理工作有差别:
-
保存现场:cortex M3/M4里是硬件完成,cortex A7等是软件实现
-
分辨异常/中断:cortex M3/M4里是硬件完成,cortex A7等是软件实现
-
调用处理函数:cortex M3/M4里是硬件来调用,cortex A7等是软件自己去调用
-
恢复现场:cortex M3/M4里是软件触发、硬件实现,cortex A7等是软件实现
不管是硬件还是软件实现,第一步都是保存现场。
1.2 为什么要保存现场
任何程序,最终都会转换为机器码,上述C代码可以转换为右边的汇编指令。 对于这4条指令,它们可能随时被异常打断,怎么保证异常处理完后,被打断的程序还能正确运行?
-
这4条指令涉及R0、R1寄存器,程序被打断时、恢复运行时,R0、R1要保持不变
-
执行完第3条指令时,比较结果保存在程序状态寄存器里,程序被打断时、恢复运行时,程序状态寄存器保持不变
-
这4条指令,读取a、b内存,程序被打断时、恢复运行时,a、b内存保持不变
内存保持不变,这很容易实现,程序不越界就可以。 所以,关键在于R0、R1、程序状态寄存器要保持不变(当然不止这些寄存器):
-
在处理异常前,把这些寄存器保存在栈中,这称为保存现场
-
在处理完异常后,从栈中恢复这些寄存器,这称为恢复现场
1.3 保存现场
ARM处理器中有这些寄存器: 
在arm中有个ATPCS规则(ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)。 约定R0-R15寄存器的用途:
-
R0-R3
调用者和被调用者之间传参数
-
R4-R11
函数可能被使用,所以在函数的入口保存它们,在函数的出口恢复它们。

还有一个程序状态寄存器,对于M3/M4它被称为XPSR,对于A7它被称为CPSR,我们简称为PSR。 R0-R15、PSR,就是所谓的现场。 发生异常/中断后,在处理异常/中断前,需要保存现场,难道需要保存所有这些寄存器吗? 不需要! 在C函数中,可以修改R0-R3、R12、R14(LR)以及PSR。如果C函数要用到这些寄存器,就要把它们保存到栈里,在函数结束前在从栈中恢复它们。 这些寄存器被拆分成2部分:调用者保存的寄存器(R0-R3,R12,LR,PSR)、被调用者保存的寄存器(R4-R11)。 比如函数A调用函数B,函数A应该知道:
-
R0-R3是用来传参数给函数B的
-
函数B可以肆意修改R0-R3
-
函数A不要指望函数B帮你保存R0-R3
-
保存R0-R3,是函数A的事情
-
对于LR、PSR也是同样的道理,保存它们是函数A的责任
对于函数B:
-
我用到R4-R11中的某一个,我都会在函数入口保存、在函数返回前恢复
-
保证在B函数调用前后,函数A看到的R4-R11保存不变
假设函数B就是异常/中断处理函数,函数B本身能保证R4-R11不变,那么保存现场时,只需要保存这些:
-
调用者保存的寄存器(R0-R3,R12,LR,PSR)
-
PC
场景设定
想象你要暂时离开家(正常程序执行),委托朋友(函数B)帮你照看房子(处理中断)。你需要明确告诉朋友:
- 哪些物品朋友可以随意使用(调用者不保护的寄存器)
- 哪些物品必须保持原样(被调用者必须保存的寄存器)
- 哪些物品你自己会提前收好(调用者负责保存的寄存器)
1. 寄存器的责任划分
(1) 调用者(函数A)的背包
| 寄存器 | 比喻 | 责任 |
|---|---|---|
| R0-R3 | 临时工具箱(用完即丢) | 调用者提前保存,传参数给函数B后不再保护 |
| R12 | 备用工具包 | 调用者提前保存 |
| LR(PC) | 回家的路线图 | 调用者必须保存返回地址 |
| PSR | 当前房间的温度计 | 调用者保存环境状态 |
(2) 被调用者(函数B)的保险柜
| 寄存器 | 比喻 | 责任 |
|---|---|---|
| R4-R11 | 传家宝古董 | 函数B若使用,必须自己存入保险柜并复原 |
2. 异常处理时的具体操作
(1) 硬件自动操作
- 保存PC和PSR:
发生异常时,硬件自动将 PC(下条指令地址) 和 PSR(程序状态) 存入栈中。
类比:地震时保险箱自动锁住贵重物品。
(2) 软件必须操作
-
保存调用者背包里的物品:
在异常处理函数中,手动保存 R0-R3, R12, LR(如果后续要调用其他函数)。irq_handler: push {r0-r3, r12, lr} ; 保存调用者的背包 ... ; 处理中断 pop {r0-r3, r12, lr} ; 取回背包 bx lr ; 返回(硬件自动恢复PC和PSR) -
按需保存保险柜物品:
如果异常处理函数使用了 R4-R11,必须自己保存和恢复:irq_handler: push {r4-r11} ; 保存保险柜里的传家宝 ... ; 处理中断 pop {r4-r11} ; 恢复传家宝 bx lr
3. 现实案例解析
(1) 场景:函数A调用函数B(普通函数)
// 函数A(调用者)
void A() {
int a = 1, b = 2; // 可能占用R0-R3
B(a, b); // 调用前不保存R0-R3,责任自负!
}
// 函数B(被调用者)
void B(int x, int y) {
int c = x + y; // 使用R0-R3(无需保存)
int d = 0; // 可能使用R4,必须保存!
}
- 函数A的失误:若在调用B后还要使用
a或b,但没有提前保存R0-R3,值会被覆盖!
(2) 场景:中断处理函数
; 中断处理函数(被调用者)
irq_handler:
push {r0-r3, r12, lr} ; 保存调用者的背包
push {r4} ; 保存保险柜的R4
bl handle_irq ; 调用C函数(可能修改R0-R3)
pop {r4} ; 恢复R4
pop {r0-r3, r12, lr} ; 恢复背包
bx lr ; 返回
4. 不同架构的差异
| 架构 | 自动保存的寄存器 | 软件需保存的寄存器 |
|---|---|---|
| ARM A7 | PC, CPSR | R0-R3, R12, LR, R4-R11(若使用) |
| Cortex-M3/M4 | PC, PSR, R0-R3, R12, LR | R4-R11(若使用) |
终极总结
| 角色 | 责任 | 操作口诀 |
|---|---|---|
| 调用者 | 保护临时工具(R0-R3, R12, LR, PSR) | “传参前收好背包” |
| 被调用者 | 保护传家宝(R4-R11) | “用前锁柜,用完复原” |
| 硬件 | 自动保护路线图和状态(PC, PSR) | “地震时自动锁保险箱” |
核心原则:
- 调用者的背包自己管:R0-R3传参后即可丢弃,重要数据提前存好。
- 被调用者的保险柜自己锁:若用R4-R11,必须保存恢复。
- 硬件是智能保险箱:自动守护最关键的路标(PC)和环境(PSR)。
1.4 对于M3/M4
参考资料:DDI0403E_B_armv7m_arm.pdf、ARM Cortex-M3与Cortex-M4权威指南.pdf、PM0056.pdf
1.4.1 硬件保存现场
1.4.2 然后调用C函数
C函数执行完后,它返回LR所指示的位置。 难道把LR设置为被中断的程序的地址就行了吗? 如果只是返回LR所指示的地方,硬件帮我们保存在栈里的寄存器,怎么恢复? M3/M4在调用异常处理函数前,把LR设置为一个特殊的值,转给特殊的值被称为EXC_RETURN。 当PC寄存器的值等于EXC_RETURN时,会触发异常返回机制,简单地说:会从栈里恢复R0-R3,R12,LR,PC,PSR等寄存器。 EXC_RETURN的值,请参考ARM Cortex-M3与Cortex-M4权威指南.pdf,截图如下:
补充2个知识点:
-
操作模式:M3/M4有两个操作模式
-
处理模式:执行中断服务程序等异常处理时,处于处理模式
-
线程模式:执行普通应用程序代码时,处于线程模式
-
-
M3/M4有连个SP寄存器:SP_process、SP_main
-
有些RTOS在运行用户程序时会使用SP_process,默认使用SP_main。
-
1.5 对于A7
它寄存器如下:
处理器有9中模式:User、Sys、FIQ、IRQ、ABT、SVC、UND、MON、HYP。 上图中深色的寄存器,表示该模式下的"Banked"寄存器,比如SPSR寄存器,在很多模式下都有自己的、单独的寄存器。 比如IRQ模式下访问SPSR时,访问到的是IRQ模式下自己的SPSR_irq,别的模式下无法访问SPSR_irq。
比较值得关注的是FIQ模式,名为"快中断",它有很多"Banked"寄存器:R8-R12,SP,LR。 在FIQ模式下,它既然能使用自己的R8-R12,SP,LR,自然不需要去保存被中断的程序的"R8-R12,SP,LR"了。 省去保存这几个寄存器的时间,处理中断时自然就快很多,所以被称为"FIQ"。
从上图也看到,几乎每个模式下都有自己是SP寄存器,意味着这些模式下有自己的栈。
当发生异常时,以IRQ为例:
-
CPU会自动切换进入对应的模式,比如进入IRQ模式
-
并且会把被中断是的CPSR保存到SPSR_irq里
所以发生异常/中断时,在保存现场时,只需要保存:
-
调用者保存的寄存器(R0-R3,R12,LR)
-
PC
用“智能快递柜”和“银行VIP通道”的比喻彻底理解两种架构的中断处理机制:
一、Cortex-M3/M4的中断处理(智能快递柜)
1. 硬件自动保存现场(快递柜自动存件)
当中断到来时(比如快递到达):
- 自动存件:硬件像智能快递柜一样,自动将 8个关键包裹(R0-R3, R12, LR, PC, PSR)存入指定格口(栈)。
- 生成取件码:硬件设置一个特殊取件码 EXC_RETURN(如
0xFFFFFFF1),用于后续取件。
; 硬件自动完成:
将 R0-R3, R12, LR, PC, PSR 压入栈
设置 LR = EXC_RETURN(类似生成快递取件码)
2. EXC_RETURN的作用(取件码触发自动出库)
当异常处理函数执行完毕(快递员取件完成):
- 扫码触发:将取件码(EXC_RETURN)加载到PC寄存器。
- 自动恢复:硬件从栈中弹出之前保存的8个寄存器,恢复现场。
; 中断处理函数返回时:
BX LR ; 当LR是EXC_RETURN时,触发自动恢复
3. 两种操作模式(快递柜的工作模式)
| 模式 | 比喻 | 用途 |
|---|---|---|
| 线程模式 | 普通用户模式 | 运行日常应用程序 |
| 处理模式 | 管理员模式 | 处理紧急中断任务 |
- 双栈设计:
SP_main:系统核心任务的专属柜(管理员专用)。SP_process:普通用户快递柜(RTOS用户程序使用)。
优势:防止用户程序搞乱系统核心数据。
二、ARM A7的中断处理(银行柜员窗口)
1. 九种工作模式(银行的不同服务窗口)
每个模式对应一个独立服务窗口,拥有专属柜员和保险箱:
- User模式:普通客户窗口(基础服务)。
- IRQ模式:普通业务加急窗口(处理一般中断)。
- FIQ模式:VIP专属窗口(快速处理紧急事务)。
2. FIQ为何更快?(VIP专属保险箱)
- 专属储物格:FIQ模式自带 R8-R12, SP, LR,无需操作客户的储物格。
- 免排队特权:处理中断时省去保存/恢复这些寄存器的步骤。
; 普通窗口(IRQ)处理流程:
保存客户物品(R0-R3, R12, LR) → 办理业务 → 归还物品 → 送走客户
; VIP窗口(FIQ)处理流程:
直接使用VIP保险箱 → 极速办理 → 送走客户(无需存取物品)
3. 异常处理流程(以IRQ为例)
- 切换窗口:硬件自动切换到IRQ窗口(使用专属柜员和保险箱)。
- 保管凭证:将客户的环境信息(CPSR)存入专属保险箱(SPSR_irq)。
- 手动存物:软件需保存客户的 R0-R3, R12, LR。
- 恢复现场:处理完成后手动归还物品和环境。
IRQ_Handler:
; 手动保存客户物品
STMFD SP!, {R0-R3, R12, LR}
; 处理中断...
; 归还物品并恢复环境
LDMFD SP!, {R0-R3, R12, LR}
SUBS PC, LR, #4 ; 从SPSR_irq恢复CPSR
三、对比总结:M系列 vs A系列
| 特性 | Cortex-M3/M4 | ARM A7 |
|---|---|---|
| 硬件支持 | 全自动存取(适合新手) | 半自动+手动操作(适合高手) |
| 中断速度 | 中等(自动操作需要时间) | FIQ极快(VIP通道免操作) |
| 寄存器管理 | 统一管理,双栈切换 | 多保险箱独立管理 |
| 适用场景 | 实时嵌入式设备(如智能手表) | 复杂系统(如手机、平板) |
四、现实案例
案例1:智能家居温控器(M4内核)
- 温度传感器触发中断:
- 硬件自动保存8个寄存器到栈。
- 执行温度读取函数。
BX LR触发自动恢复,继续运行主程序。
案例2:手机快速充电(A7内核)
- 充电电流监控(FIQ处理):
- 直接使用专属R8-R12计算电流。
- 无需保存寄存器,处理速度比普通中断快40%。
终极口诀
- M系列:“自动存取,傻瓜式操作”
- A系列:“VIP通道,高手专用提速”
- 核心差异:
- M系列硬件全包,适合实时性要求高的简单系统。
- A系列手动优化,适合需要极致性能的复杂场景。 ✅

-
寄存器 = 工作台上的工具格
- R0-R15就像16个工具格(R15是指挥棒位置,记录正在执行的任务)
- R13是工具箱(sp),R14是任务备忘录(lr),R15是当前工作进度(pc)
-
不同工作模式 = 不同工种的工作服
- 用户模式(User):日常状态
- 系统模式(Sys):7种特殊状态(FIQ快速抢修/IRQ普通维修/ABT事故处理等)
-
工具的特点:
- R0-R7是通用工具(所有模式共用)
- R8-R14是专用工具(每种工作服有自己的一套,比如穿FIQ工作服时会自动切换为R8_fiq等专属工具)
- 特殊工具:CPSR是工作状态显示屏(显示当前工作模式、是否完成任务等)
为什么这样设计?
当接到紧急任务(比如中断)时,CPU只需快速换上对应的工作服,就能直接使用该模式专用的工具,不需要先把原来的工具收起来,大大提高了响应速度。这就是为什么手机能流畅处理多任务、游戏能快速响应的硬件秘密之一!


04_CPU模式(Mode)_状态(State)与寄存器
这节课我们来讲CPU的工作模式(Mode) 状态(State)寄存器,主要是cortex A7。 参考:
-
《ARM体系结构与编程》作者:杜春雷 -
ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf -
S3C2440A_UserManual_Rev13.pdf:没错,这个手册里描述得更清晰
ARM9和cortex A7的CPU模式、状态、寄存器,以及发生异常时的处理细节,几乎是一模一样的。
1.1 CPU有9种Mode
跟ARM9相比,多了2中Mode:Monitor、Hyp。在本课程中不涉及这两种模式。 除usr模式外,其他模式是特权模式。 usr模式下,无法通过修改CPSR寄存器进入其他模式。 在其他模式下,可以通过修改CPSR寄存器进入其他模式。
-
usr:用户模式
-
sys:系统模式
-
undefined(und):未定义模式
-
Supervisor(svc):管理模式
-
Monitor
-
Abort(abt):中止模式 有两种情况会导致CPU今日中止模式:
-
指令预取中止(读取某条指令时发生错误)
-
数据访问终止 (读写某个地址上的数据时,发生错误)
-
-
Hyp
-
IRQ(irq):中断模式
-
FIQ(fiq):快中断模式

1.2 有2种State
以前的ARM9芯片,支持两种指令集:
-
ARM state:每条指令都是32位的,高效,但是占空间
-
Thumb state:每天指令都是16位的,节省空间,但是有时候效率不高
对于Cortex A7芯片,还有Thumb2指令集,支持16位、32位指令混合编程。
1.3 寄存器
有这些寄存器:
-
通用寄存器
-
备份寄存器(banked register)
-
当前程序状态寄存器(CPSR,Current Program Status Register)
-
CPSR的备份寄存器(SPSR,Save Program Status Register)
1.3.1 寄存器总图
一、核心设计理念
处理器像一栋多功能办公楼,不同工作模式对应不同部门,寄存器相当于各部门的专属办公桌:
-
部门分类(工作模式)
- 常规部门:User(普通员工办公室)
- 应急部门:FIQ(火警响应组)、IRQ(设备维修组)、Abort(事故处理组)
- 管理办公室:System(总务处)、Hyp(虚拟化调度中心)、Monitor(安全监控室)
-
办公桌规则
- 公共区域(R0-R12):所有部门共用
- 部门专属储物柜(R8-R14):每个应急部门有自己的储物空间
- 重要设备(CPSR/SPSR):全楼状态显示屏+部门独立备份屏
二、关键寄存器详解
1. 通用办公区(R0-R12)
所有模式共用,相当于公共会议室的白板,不同部门使用时需要自行记录/擦除内容
2. 应急部门装备(FIQ特权)
| 专属装备 | 优势说明 |
|---|---|
| R8_fiq-R12_fiq | 5个独立储物柜,处理紧急任务时无需腾空原有物品 |
| SP_fiq/LR_fiq | 专用电话和记事本,记录当前任务进度 |
示例:当手机触控屏被触摸时,FIQ模式可立即使用专属寄存器处理,比普通中断快50%
3. 管理核心设备
- PC寄存器:全楼唯一的总控台(记录当前执行位置)
- CPSR/SPSR:双屏监控系统(当前状态+部门备份状态)
- ELR_hyp:虚拟化部门的时光机(可回退错误操作)
三、模式切换流程图
[用户模式] → 遇到中断/异常 → 换工作服(切换模式)
↓
[自动领取装备] → 穿上FIQ/IRQ等专属马甲 → 使用专用寄存器
↓
[处理完毕] → 脱掉专属马甲 → 放回专用储物柜 → 返回原模式
四、安全设计亮点
- 隔离保护:Hyp模式(虚拟化扩展)和Monitor模式(安全扩展)各自独立储物柜
- 双重世界:安全状态(†标注)与非安全状态(+标注)物理隔离
- 故障隔离:Abort模式专用SP_abt/LR_abt,保证系统崩溃时核心数据不丢失
这种设计使得手机能做到:微信视频通话(User模式)与指纹识别(FIQ模式)同时运行互不干扰,游戏触控响应速度可达毫秒级。
我们不关心Monitor模式、Hyp模式,那么可以看ARM9手册上的这个图,更直观:
1.3.2 备份寄存器
上图中阴影部分,是该模式下自己专有的寄存器。 比如执行这样的指令:
mov R0, R8
在System 模式下访问的是R0和R8,在所有模式下访问R0都是同一个寄存器。 但是在FIQ模式下,访问R8时访问,它对应FIQ模式专属的R8寄存器,不是同一个物理上的寄存器,相当于:
mov R0,R8_fiq
在这各种异常模式下,都有自己专属的R13、R14寄存器:
-
R13用作SP(栈) :所以发生异常时,使用的是该异常模式下的栈
-
R14用作LR(返回地址):用来保存发生异常时的指令地址,处理完异常后,根据LR的值恢复被中断的程序
为什么快中断(FIQ)有那么多专属寄存器? 回顾一下中断的处理过程:
-
保存现场(保存被中断模式的寄存器)
-
处理异常
-
最后恢复这些寄存器
假设程序正在系统模式/用户模式下运行, 当发生中断时,需要把R0 ~ R14这些寄存器全部保存下来。 但如果是快中断,那么就不需要保存系统/用户模式下的R8 ~ R12这几个寄存器, 因为在FIQ模式下有自己专属的R8 ~ R12寄存器, 这样可以省略保存寄存器的时间,加快处理速度。
1.3.3 程序状态寄存器
CPSR,表示当前程序状态寄存器,这是一个特别重要的寄存器 SPSR,用来保存CPSR,它们格式如下
这张图展示了ARM架构中两个核心状态寄存器(CPSR和SPSR)的二进制位分配结构。你可以把它们想象成计算机的「控制面板」,通过32个开关(位)的排列组合,控制处理器的运行状态。以下用三个层次帮你理解:
一、核心功能定位
- CPSR(Current Program Status Register):实时显示CPU当前的工作状态(如是否在中断中、运算结果是否为零等)
- SPSR(Saved Program Status Register):当发生中断/异常时,自动备份CPSR的"现场快照",处理完毕后再还原
二、位域详解(从高位到低位)
1️⃣ 条件标志区(31-24位)—— CPU的"成绩单"
| 位域 | 缩写 | 功能说明 | 示例场景 |
|---|---|---|---|
| 31 | N | 负数标志(Negative) | 计算-5+3后N=1 |
| 30 | Z | 零标志(Zero) | 计算5-5后Z=1 |
| 29 | C | 进位标志(Carry) | 加法溢出时C=1 |
| 28 | V | 溢出标志(Overflow) | 符号位错误时V=1 |
| 27 | Q | 饱和标志(Saturation) | SIMD运算饱和时触发 |
2️⃣ 控制开关区(23-8位)—— CPU的"操作台"
| 位域 | 功能说明 | 关键作用 |
|---|---|---|
| 24-27 | GE[3:0] | SIMD指令比较结果存储 |
| 19-26 | IT[7:0] | 条件执行指令控制块(if-then) |
| 9 | E | 字节序控制(0=小端,1=大端) |
| 8/7 | A/I/F | 中断屏蔽开关: A=中止中断屏蔽 I=普通中断屏蔽 F=快速中断屏蔽 |
| 5 | T | 指令集模式(0=ARM,1=Thumb) |
3️⃣ 模式标识区(4-0位)—— CPU的"身份证"
| 位模式 | 值 | 对应模式 | 典型场景 |
|---|---|---|---|
| M[4:0] | 0b10000 | User(用户模式) | 运行普通APP |
| 0b10001 | FIQ(快速中断模式) | 处理触摸屏点击 | |
| 0b10010 | IRQ(普通中断模式) | 处理键盘输入 | |
| 0b10111 | Abort(异常终止模式) | 内存访问错误处理 |
三、设计精妙之处
-
快速响应机制:
当中断发生时,硬件自动将CPSR拷贝到SPSR,并立即修改CPSR中的模式位(M[4:0])和中断屏蔽位(I/F),实现微秒级切换。 -
双重保险设计:
RAZ/SBZP标注位(灰色区域)要求软件必须写0,避免未来架构升级时出现兼容性问题。 -
状态继承规则:
通过IT[7:0]位的设计,Thumb-2指令集能实现条件执行语句嵌套(类似if-else的层级判断)。
现实应用示例
当你在手机上玩游戏时:
- 触控中断触发FIQ模式 → CPSR的M[4:0]变为0b10001
- 系统自动屏蔽其他中断(F位=1)→ 保证触控响应优先
- 处理完毕后从SPSR恢复原模式 → 继续渲染游戏画面
这种精密位控制,正是手机能同时处理微信消息、音乐播放和游戏操作的底层秘密!
好的!我用一个「厨房做菜」的比喻来解释SIMD运算,保证你3分钟完全理解:
一、SIMD的本质:「批量烹饪法」
想象你是个厨师,要做100份宫保鸡丁:
-
传统方式(普通CPU指令)
你每次只能炒1份,需要重复100次:倒油→炒肉→加料→装盘 -
SIMD方式(单指令多数据)
你有个魔法炒锅(SIMD单元),能同时炒8份宫保鸡丁:
一次倒油→同时炒8份肉→同时加8份料→同时装8个盘子
这就是SIMD的核心优势:用一条指令同时处理多个数据!
二、关键技术拆解
1. 数据打包——食材预处理
假设每份宫保鸡丁需要:
鸡肉(8位) + 花生(8位) + 辣椒(8位) + 酱油(8位) = 32位数据包
SIMD寄存器就像一个分层食盒:
[鸡肉1|花生1|辣椒1|酱油1 | 鸡肉2|花生2|辣椒2|酱油2 | ... | 鸡肉8|花生8|辣椒8|酱油8]
128位SIMD寄存器能同时装8份32位调料包(实际中数据位宽可调整)
2. 并行计算——魔法炒锅工作
当执行SIMD加法指令时:
8份鸡肉同时翻炒 → 8份酱油同时喷洒 → 8份辣椒同步调味
所有操作在1个时钟周期内完成,效率是传统方式的8倍
三、什么是「运算饱和」?
典型场景:音频音量增强
假设用SIMD同时处理4个音频采样值(每个值范围0-255):
原始数据:[200, 150, 240, 180]
增强指令:所有数值+60
普通处理(溢出):
200+60=260 → 溢出变成4(二进制高位丢失) → 产生爆音
结果:[4, 210, 44, 240] ❌ 完全失真
饱和处理(Saturation):
200+60=260 → 超过255 → 自动锁定为最大值255
结果:[255, 210, 255, 240] ✅ 保持最大音量不爆音
此时CPSR中的Q(饱和标志)会亮起,告诉系统:"本次运算有数据触顶了!"
四、现实世界中的应用
-
手机拍照
HDR合成时,SIMD同时处理百万像素点的亮度/对比度,饱和标志防止过曝 -
游戏特效
粒子特效(雨雪、火焰)的位置计算,128位SIMD寄存器同时操控数百个坐标 -
视频解码
解码4K视频时,SIMD批量处理宏块数据,饱和机制确保颜色值不溢出
为什么这么设计?
传统溢出处理像「水杯接水漫出来」(数据丢失),而饱和处理像「自动水位控制器」(保持最大合理值)。这种设计在图形、音频等场景中,能避免突然的数值跳变,保证视觉/听觉的平滑体验!
-
M4 ~ M0 表示当前CPU处于哪一种模式(Mode) 我们可以读取这5位来判断CPU处于哪一种模式,也可以修改它进入其他模式。 注意:假如当前处于用户模式下,是没有权限修改这些位的。 M4 ~ M0对应模式,如下图所示:

-
其他位
-
Bit5 State bits:表示CPU工作于Thumb State还是ARM State,用的指令集是什么。
-
Bit6 FIQ disable:当bit6等于1时,FIQ被禁止。
-
Bit7 IRQ disable:当bit5等于1时,禁止所有的IRQ中断,这个位是IRQ的总开关
-
Bite28 ~ Bit31是状态位 什么是状态位,比如说执行这两条指令
cmp R0, R1 beq xxx
如果R0 等于 R1,第1条指令会导致CPSR中的Z位等于1。 后面的跳转指令,会根据Z位的值决定是否跳转:Z等于1就跳转,否则就不跳转。
-
每个模式下都有自己的SPSR寄存器,表示发生异常时,这个寄存器会用来保存被中断的模式下的CPSR。 就比如程序在系统模式下运行,当发生中断时会进入irq模式时,这个SPSR_irq就保存系统模式下的CPSR。
1.4 发生异常时处理流程
我们来看看发生异常时CPU是如何协同工作的。
1.4.1 异常向量表
在ARM9里,异常向量表基地址只有两个取值:0、0xFFFF0000。 对于cortex A7,它的异常向量表基地址是可以修改的。
一、公司安全区域划分
处理器像一家设有严格安全等级的公司:
-
监控中心(Secure Monitor模式)
- 最高安全级别,处理跨区域的安全事件
- 有一本专用《监控中心应急预案》(Monitor Vector Table)
-
保密办公室(Secure非Monitor模式)
- 处理机密业务问题
- 存放《机密应急预案》(Secure Vector Table)
-
普通办公区(Non-secure模式)
- 处理日常事务
- 使用《常规应急预案》(Non-secure Vector Table)
二、应急预案存放规则
1. 存放位置决定权
-
监控中心:必须使用保险柜里的专用本(地址由MVBAR寄存器指定)
-
保密办公室:有两种选择
✅ 若安全主管开启「统一存放」开关(Secure SCTLR.V=1)→ 使用公司统一保险柜(地址0xFFFF0000)
✅ 若关闭开关(V=0)→ 可自选保险柜位置(地址由Secure VBAR寄存器指定) -
普通办公区:同样有两种选择
✅ 统一存放开关(Non-secure SCTLR.V=1)→ 使用统一保险柜
✅ 关闭开关(V=0)→ 自选位置(由Non-secure VBAR指定)
三、核心设计亮点
-
物理隔离
保密区和普通区的应急预案存放在不同的保险柜(Secure/Non-secure地址空间),普通员工无法接触机密文件。 -
快速切换
当发生跨区域事件时(如普通区发现可疑操作),监控中心能立即调取对应区域的应急预案,无需现场翻找。 -
双重保险
统一地址0xFFFF0000相当于公司统一采购的标准保险柜,而VBAR自选地址则允许各部门使用定制保险柜。
现实场景示例
当手机同时运行银行APP(安全模式)和游戏(普通模式)时:
- 游戏突然崩溃 → 触发普通区应急预案(地址由Non-secure VBAR指定)
- 检测到可疑数据篡改 → 切换到监控中心应急预案(MVBAR指定地址)
- 银行APP密钥验证 → 调用保密区应急预案(Secure VBAR地址)
- 所有操作互不干扰,恶意程序无法读取安全区的应急流程
这种机制正是手机能同时保障支付安全和游戏流畅的底层秘密!
1.4.2 进入异常的处理流程(硬件)
我们来翻译一下: 发生异常时,我们的CPU会做什么事情
-
硬件确定要进入哪种异常模式
-
LR寄存器被更新,它表示处理完异常后要返回到哪里,这个值可能需要修改。
-
SPSR = 被中断时的CPSR
-
对于"Security Exceptions",……,本课程不涉及
-
更新异常模式下的CPSR:设置模式位、设置mask bit(屏蔽其他异常)、设置指令集状态位
-
PC = 异常入口地址
-
从PC所指示地方执行程序
一、整体概念
处理器处理异常就像医院的「急诊分诊系统」,当发生意外(如程序错误、硬件中断等),需要立即启动标准抢救流程:
核心角色
- 医生(CPU核心):正在处理普通任务(主程序)
- 护士站(异常处理模块):备有7种应急预案(对应7类异常)
- 病历本(CPSR/SPSR):记录当前健康状态
- 急救手册(异常向量表):明确每种病症对应科室位置
二、7步急救流程解析
1️⃣ 分诊台预判(确定异常模式)
- 护士首先判断病症类型:
- 是普通发烧(IRQ中断)?还是心脏骤停(HardFault错误)?
- 对应选择「急诊科室」(PL1普通模式/PL2监控模式)
技术对应:根据异常类型选择ARM模式(User/SVC/IRQ等)
2️⃣ 登记患者信息(保存链接值)
- 记录患者被送来时的位置(程序被中断的地址),就像:
- 在病历本写下「患者晕倒在第3诊室门口」(保存PC值到LR寄存器)
- 特别注意是否涉及隔离区患者(SCR.NS权限位判断)
3️⃣ 生命体征存档(保存CPSR)
- 用专用设备记录当前状态:
- 当前心率(程序运行状态)
- 正在服用的药物(中断屏蔽状态)
- 语言能力(指令集状态:ARM/Thumb)
技术实现:将CPSR复制到SPSR_寄存器
4️⃣ 清场准备手术(模式切换)
- 进入手术室前必须:
- 关闭手机(清除IT[7:0]条件执行标记)
- 换上无菌服(切换到特权模式)
- 挂起普通门诊(禁用同级中断)
技术细节:更新CPSR的M[4:0]位,设置中断屏蔽位
5️⃣ 启动急救预案(加载异常向量)
- 根据病症类型,自动调度:
- 心内科在A区3室(异常向量地址=基地址+偏移量)
- 神经外科在B区5室(不同异常对应不同向量地址)
技术实现:从VBAR(向量基地址寄存器)加载PC值
6️⃣ 开始实施抢救(继续执行)
- 主刀医生按标准流程操作:
- 心肺复苏(处理栈溢出错误)
- 电击除颤(修复内存访问错误)
- 完成后送回原诊室(恢复现场继续原程序)
三、关键技术亮点
-
双病历系统
- 当前状态(CPSR)和急救前状态(SPSR)分开保存,保证抢救后能精确恢复现场
-
智能分诊规则
- 普通医生(PL1模式)处理常见病症,遇到疑难杂症自动转交专家(PL2监控模式)
-
隔离防护机制
- 普通区(Non-secure)与隔离区(Secure)使用不同急救手册(向量表),防止交叉感染
现实场景示例
当你在手机上同时运行游戏和微信时:
- 游戏突然崩溃(触发HardFault异常)→ 进入急救模式
- 系统自动保存游戏进度(保存PC/LR)、画面设置(保存CPSR)
- 调用错误处理程序(加载0x00000008地址的急救方案)
- 错误处理后,微信仍能正常视频通话(其他程序不受影响)
这种精密机制,正是手机能「边玩游戏边接电话」还不卡顿的底层秘密!
1.4.3 退出异常
在ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf中,对异常的退出,描述得很复杂。但是很多情况,我们并不涉及。 所以我们参考S3C2440A_UserManual_Rev13.pdf,它描述得更清晰、简单。
从异常中退出,要做什么事情?
-
让LR减去某个值,然后赋值给PC(PC = 某个异常LR寄存器减去 offset) 减去什么值呢? 也就是我们怎么返回去继续执行原来的程序,根据下面这个表来取值:
如果发生的是SWI可以把 R14_svc复制给PC 如果发生的是IRQ可以把R14_irq的值减去4赋值给PC
-
把CPSR的值恢复(CPSR 值等于 某一个一场模式下的SPSR)
-
清中断(如果是中断的话,对于其他异常不用设置)
1️⃣ 调整“返回地址”
- 比喻:接电话前你正在切菜,电话挂断后需要决定是重新切刚才那一刀(回退一步)还是继续切下一刀。
- 技术解释:处理完异常时,处理器要根据异常类型调整程序计数器(PC)的值。不同异常发生时,处理器可能记录了不同的“返回地址”(比如是否要重新执行被中断的指令),所以需要减去一个偏移量来修正地址。
2️⃣ 恢复“工作状态”
- 比喻:接电话时你关掉了油烟机(为了听清电话),挂断后要重新打开油烟机,恢复原来的厨房状态。
- 技术解释:处理异常时,处理器会保存原来的工作状态(比如运行模式、权限等级)到SPSR寄存器,处理结束后需要把SPSR的内容复制回CPSR寄存器,确保系统回到原来的状态。
3️⃣ 解除“免打扰模式”
- 比喻:接电话时你开了“免打扰模式”(屏蔽其他来电),挂断后要关闭免打扰,否则会错过新电话。
- 技术解释:如果进入异常时关闭了中断响应(比如防止处理A异常时被B异常打断),处理结束后要重新开启中断,让系统能响应新的紧急事件。
一句话总结
“挂断电话后,把菜板放回原位、恢复厨房设置、关掉免打扰,接着切菜!”
这三个步骤保证了处理器处理完异常后,能精准回到原来的任务,同时恢复环境、解除限制,就像什么都没发生过一样。
1.4.4 确定异常返回地址
发生异常时,LR寄存器里保存的值是什么?
LR = Preferred return address + offset
"Preferred return address"是什么?请看下图:
这张图其实是在讲:当电脑的CPU遇到“突发状况”(比如程序出错、接到紧急任务)时,它要怎么记住自己“从哪里来”,并安全地回到原来的工作。就像你突然被老板叫去开会,回来后需要知道该继续做哪件事、用哪个笔记本记录一样。
一、核心概念比喻
-
异常类型(Exception)
➡️ 突发状况的类型:比如“程序写错了代码”(未定义指令)、“系统需要处理紧急任务”(管理调用)、“网络突然断线”(数据中断)等等。不同状况需要不同的处理方式。 -
首选返回地址(Preferred return address)
➡️ 回来继续工作的位置:就像你被电话打断后,要决定是重做刚才那一步(回到出错指令),还是跳过继续下一步(执行下一条指令)。 -
进入的模式(Taken to a mode at)
➡️ 切换权限或安全状态:比如普通用户模式(PL1)、管理员模式(PL2),或者区分“机密环境”(Secure)和“普通环境”(Non-secure)。就像你处理公司机密文件时要进加密办公室,处理完再回到普通工位。
二、具体例子解释
| 异常类型 | 通俗解释 | 返回地址 | 进入模式 |
|---|---|---|---|
| 未定义指令 | 程序写了CPU不认识的代码(比如打错字) | 回到错误指令的位置,重新检查哪里出问题 | 管理员模式(PL1/PL2) |
| 管理调用 | 系统需要执行高权限操作(比如打开摄像头) | 跳过当前指令,直接执行下一条 | 管理员模式(PL1/PL2) |
| 安全监控调用 | 处理涉及机密数据的任务 | 执行完机密任务后,继续下一条指令 | 仅限机密环境(Secure) |
| 虚拟中断 | 虚拟机遇到问题(比如模拟的网络断线) | 直接下一步,让虚拟机自己处理 | 普通用户模式(仅限非机密环境) |
三、关键注释总结
-
安全与权限
- Secure vs Non-secure:就像公司有“机密实验室”和“普通办公室”,某些异常只能在机密环境下处理(例如指纹解锁失败)。
- PL2权限:类似“超级管理员权限”,但只能在普通办公室(Non-secure)使用,不能带进机密实验室。
-
异常处理规则
➡️ 原则:处理完异常后,CPU必须回到正确的位置、切换回原来的权限,并且不能泄露机密信息(比如在普通环境下不暴露Secure状态的数据)。
一句话总结
“电脑遇到突发状况时,就像你突然被叫去灭火,处理完要记得:回哪继续干活?用哪个权限?是否涉及机密?”
这张表就是CPU的“应急手册”,确保它处理完异常后能精准恢复现场,既不卡死也不泄密。
offset是什么?请看下图:
这张图讲的是ARM处理器在PL1特权模式下处理异常时的“返回地址修正规则”,可以比喻成:电脑突然遇到紧急任务(比如程序崩溃、权限请求)时,它要如何准确记住“回来继续干活的位置”。
一、核心机制
-
异常发生时,处理器会保存“返回地址”到LR寄存器
- LR = 理想返回地址 + 偏移量
- 偏移量的作用:根据异常类型和当时的指令集状态(ARM/Thumb/Jazelle)修正返回地址,确保处理器回来时能正确继续执行。
通俗比喻:
你正在看书时被电话打断,挂断后要翻回某一页继续读。但不同电话(异常类型)和不同阅读姿势(指令集状态)会导致翻页的位置需要微调(偏移量)。- 例如:如果是“书上有错别字”(未定义指令),可能需要回退几页重新读;如果是“快递到了”(中断请求),可能直接翻到下一页。
二、偏移量的作用解析
| 异常类型 | 典型偏移量(ARM模式) | 通俗解释 |
|---|---|---|
| 未定义指令 | +4 | 遇到不认识的指令(如代码写错),需回到错误指令的位置重新处理(ARM指令长度4字节)。 |
| 管理调用 | 无偏移(None) | 系统调用(如申请权限)后直接执行下一条指令,无需回退。 |
| 数据中止 | +8 | 访问内存出错(如地址无效),处理完异常后跳过当前指令,继续执行后续指令。 |
| 中断请求(IRQ) | +4 | 处理完中断(如收到网络数据)后,回到被中断指令的下一条继续执行。 |
三、指令集状态的影响
-
ARM模式(32位指令)
- 大部分异常偏移量是4或8(按4字节对齐)。
举例:未定义指令异常偏移+4,相当于回退到当前指令的起始位置重新执行。
- 大部分异常偏移量是4或8(按4字节对齐)。
-
Thumb模式(16位指令)
- 偏移量减半(如未定义指令偏移+2,因为Thumb指令长度2字节)。
举例:同样处理未定义指令,Thumb模式下回退2字节即可定位错误指令。
- 偏移量减半(如未定义指令偏移+2,因为Thumb指令长度2字节)。
-
Jazelle模式(特殊指令集)
- 偏移量规则更复杂,需参考具体文档(如注释中的“-b”或“-c”)。
注意:Jazelle状态现已逐步淘汰,实际应用中较少涉及。
- 偏移量规则更复杂,需参考具体文档(如注释中的“-b”或“-c”)。
四、关键总结
-
为什么需要偏移量?
- 不同异常的处理逻辑不同:有的需要重新执行当前指令(如未定义指令),有的需要跳过当前指令(如数据中止)。偏移量帮助处理器精准定位返回位置。
-
为什么分不同指令集状态?
- ARM和Thumb指令长度不同(4字节 vs 2字节),修正返回地址时需按指令长度调整偏移量。
-
PL1模式是什么?
- PL1是ARM处理器的特权模式(如操作系统内核态),用于处理高权限操作(如系统调用、内存管理)。异常处理时需进入PL1模式保存现场。
一句话总结
“电脑处理异常就像你接电话:挂断后要翻回书里正确的位置继续读。不同电话类型(异常)和读书姿势(指令集)决定了翻几页(偏移量)!”
这张表就是处理器的“电话接听手册”,确保它处理完紧急任务后能精准回到原来的工作流程。
从异常中返回时,LR可能需要调整,再赋给PC。 ARM9的手册讲得比较清楚,返回指令如下:
通俗解析:电脑遇到突发状况时,如何精准“回到案发现场”?
这张图是ARM处理器处理各种异常(如程序崩溃、系统调用、硬件中断等)的“返回地址操作手册”。可以理解为:电脑遇到紧急事件后,必须记录“从哪里被中断”,处理完还要精准回到原来的位置继续工作。以下用生活场景类比:
一、核心概念
-
R14寄存器(LR:Link Register)
➡️ “书签”:当电脑被异常打断时,R14会记录一个“书签位置”,告诉CPU处理完异常后应该翻到哪一页继续执行程序。- 不同异常类型对应不同的“书签夹”(如
R14_svc、R14_abt),避免书签混乱。
- 不同异常类型对应不同的“书签夹”(如
-
PC(Program Counter)
➡️ “当前读到的页码”:PC指向下一条要执行的指令地址。异常发生时,PC的地址会被保存到R14,但需要根据异常类型调整偏移量(比如回退或跳过当前指令)。
二、表格逐行解析(以生活场景类比)
| 异常类型 | 返回指令 | ARM模式书签位置 | Thumb模式书签位置 | 通俗解释 |
|---|---|---|---|---|
| BL | MOV PC, R14 | PC + 4 | PC + 4 | 正常函数调用,处理完直接翻到下一页(ARM和Thumb都+4)。 |
| SWI | MOVS PC, R14_svc | PC + 4 | PC + 2 | 系统调用(如请求权限),ARM模式翻下一页(4字节),Thumb模式翻下一页的一半(2字节)。 |
| UDEF | MOVS PC, R14_und | PC + 4 | PC + 2 | 遇到不认识的代码(比如写错指令),处理完需回到错误指令位置重新执行。 |
| FIQ/IRQ | SUBS PC, R14_fiq, #4 | PC + 4 | PC + 4 | 处理硬件中断(如收到网络数据),需回退4字节,重新执行被中断的指令。 |
| PABT | SUBS PC, R14_abt, #4 | PC + 4 | PC + 4 | 预取指令失败(比如内存访问错误),回退4字节重新取指令。 |
| DABT | SUBS PC, R14_abt, #8 | PC + 8 | PC + 8 | 数据访问失败(如无效地址),需跳过更多步骤(回退8字节),避免重复错误。 |
| RESET | NA | - | - | 电脑重启,没有“书签”可言,直接从头开始运行。 |
三、关键细节
-
为什么ARM和Thumb模式的书签位置不同?
- ARM指令长度4字节:好比一本书每页有4行字,翻页要+4。
- Thumb指令长度2字节:每页只有2行字,翻页只需+2。
- 示例:
SWI异常时,Thumb模式的书签位置是PC+2,因为下一条指令距离更近。
-
返回指令中的
SUBS PC, R14_xx, #N是什么操作?
➡️ “书签修正”:从R14保存的书签位置减去N(如4或8),让PC指向正确的返回地址。- 例子:FIQ异常用
SUBS PC, R14_fiq, #4,表示“书签位置减4字节”再赋值给PC,确保重新执行被中断的指令。
- 例子:FIQ异常用
-
RESET为什么没有返回地址?
➡️ “彻底重启”:复位相当于电脑关机再开机,所有任务从头开始,不需要回到之前的执行位置。
四、一句话总结
“电脑处理异常就像你突然被电话打断:挂断后要根据电话类型(异常)和读书进度(指令模式),翻回书里正确的位置(修正PC),然后继续读下去!”
这张表就是CPU的“书签管理指南”,确保它处理完紧急事件后能精准恢复现场,既不跳页也不卡死。
05_ARM_Thumb指令集程序示例
对于IMX6ULL、STM32MP157,默认使用ARM指令集。 在汇编文件里,可以使用这样的语法告诉编译器:
1.1.1 新语法
.arm // 表示后续的指令使用ARM指令集 .thumb // 表示后续的指令使用thumb指令集
1.1.2 以前的语法
.code 32 // 表示后续的指令使用ARM指令集 .code 16 // 表示后续的指令使用thumb指令集
1.2 编译C文件时指定指令集
使用GCC编译时:
-marm // 使用ARM指令集编译 -mthumb // 使用Thumb指令集编译 比如: arm-linux-gcc -marm -c -o main.o main.c
1.3 汇编里切换状态
要切换CPU的State,比如从ARM切换到Thumb,或者从Thumb切换到ARM,可以使用BX、BLX指令:
// 如果R0的bit0为0,表示切换到ARM State; 如果R0的bit0位1,表示切换到Thumb State BX R0 BLX R0
汇编里调用C函数时,可以直接如此调用:
LDR PC, =main // 如果main函数时用Thumb指令集编译的,最终的指令如下: // 注意到下面的c020051b,它的bit0为1 c0200010: e59ff004 ldr pc, [pc, #4] ; c020001c <AB+0x14> c0200014: c0100000 andsgt r0, r0, r0 c0200018: c02004a3 eorgt r0, r0, r3, lsr #9 c020001c: c020051b eorgt r0, r0, fp, lsl r5
一、核心概念比喻
把ARM指令集和Thumb指令集想象成两种“语言模式”:
- ARM模式:像用完整的句子说话(32位指令),表达清晰但占篇幅(代码体积大)。
- Thumb模式:像用缩略词说话(16位指令),简短但可能不够详细(功能受限),但更省纸(节省内存空间)。
IMX6ULL、STM32MP157等芯片默认用ARM模式,但有些场景(如嵌入式设备)需要切换到Thumb模式来节省内存。
二、切换指令集的具体方法
1. 在汇编文件中声明指令集
- 新语法:
.arm // 告诉编译器:后面用ARM指令集(完整句子)[7,8](@ref) .thumb // 告诉编译器:后面用Thumb指令集(缩略词)[7,8](@ref) - 旧语法(兼容早期代码):
.code 32 // ARM指令集(类似32位寄存器的操作)[7,8](@ref) .code 16 // Thumb指令集(16位指令)[7,8](@ref)
为什么需要声明?
编译器需要知道当前代码的“语言模式”,才能正确翻译指令。比如Thumb模式下,ADD R0, R1会被编译成16位指令。
2. 编译C文件时指定指令集
用GCC编译时:
arm-linux-gcc -marm -c -o main.o main.c # 强制用ARM模式编译
arm-linux-gcc -mthumb -c -o main.o main.c # 强制用Thumb模式编译
类比:
-marm相当于让C语言翻译成“完整句子”(ARM指令)。-mthumb相当于让C语言翻译成“缩略词”(Thumb指令)。
注意事项:
- 如果C函数用Thumb模式编译,在汇编中调用时,必须切换到Thumb状态(后文解释)。
3. 在汇编中动态切换状态
使用BX或BLX指令,根据目标地址的最低位(bit0)切换模式:
BX R0 // 如果R0的bit0=1 → 切换到Thumb模式;bit0=0 → 切回ARM模式
BLX R0 // 切换状态并跳转(类似打电话时先切换语言再说话)
原理:
- ARM芯片通过目标地址的最低位判断模式:
- bit0=0:ARM模式(地址对齐到4字节,如
0x8000)。 - bit0=1:Thumb模式(地址对齐到2字节,如
0x8001)。
- bit0=0:ARM模式(地址对齐到4字节,如
示例:
LDR PC, =main // 加载main函数的地址到PC(程序计数器)
假设main函数用Thumb编译,其地址会是0xC020051B(bit0=1),执行时会自动切换到Thumb模式。
三、为什么需要手动切换?
- 节省内存:Thumb指令占空间更小(适合内存紧张的嵌入式设备)。
- 性能平衡:Thumb代码密度高,但复杂操作需切回ARM模式(如浮点运算)。
- 兼容性:混合编程时,C和汇编可能用不同指令集,需统一状态。
四、实际应用场景
- 嵌入式开发:用Thumb模式编译大部分代码节省空间,关键性能代码切回ARM模式。
- 异常处理:ARM芯片默认在异常(如中断)时切到ARM模式,处理完需手动切回Thumb。
- 调用C函数:若C函数用Thumb编译,汇编中调用前需确保状态匹配(否则崩溃)。
五、一句话总结
“ARM和Thumb像两种输入法:ARM是‘全拼’,Thumb是‘简拼’。切换时需告诉编译器用哪种,跳转时靠地址最后一位(类似输入法快捷键)自动切换!”
掌握这些规则,就能在代码中灵活平衡性能和内存需求。
06_实战_未定义指令异常
要想深入理解异常处理,需要写程序来验证。 本节课程故意执行一条未定义的指令,让它触发异常。
参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf
1.1 A7的异常向量表
从向量表可以看出,A7支持哪些异常:未定义指令、软中断(SVC)、预取指令中止、数据中止、IRQ、FIQ。
_start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
1.2 什么是未定义指令?
未定义指令,即使"还没有定义的指令",也就是CPU不认识的指令。 很多时候,我们故意在代码里插入一些伪造的指令,故意让CPU执行到它时触发错误。 这在调试时很有用,比如想打断点:怎么实现呢? 有很多种方法:硬件监视点(watch point,数量有限)、软件断点(数量无限)。 软件断点就是使用未定义指令来实现的,比如想让程序执行到某个地址A时停下来,可以这样做:
-
地址A上原来的指令是
xxx -
我们故意把它改成
yyy,改成一条CPU无法识别的指令 -
当CPU执行到地址A上的
yyy指令时,触发异常 -
在异常处理函数里,打印更多调试信息
-
调试完毕后,恢复地址A上的指令为
xxx -
从地址A重新执行程序
本节教程并不打算制作调试器,这里只是讲述一下未定义指令的作用,使用它来深入理解异常处理流程。
1.3 在汇编代码里插入未定义指令
在代码中插入:
.word 0xffffffff
看看会发生什么事情。
1.4 编程
1.4.1 设置异常向量表基地址
MRC p15, 0, <Rt>, c12, c0, 0 ; Read VBAR into Rt MCR p15, 0, <Rt>, c12, c0, 0 ; Write Rt to VBAR
1.4.2 设置未定义模式的栈
1.4.3 保存现场
1.4.4 处理异常
1.4.5 恢复现场
通俗解析:如何用未定义指令异常“玩坏”CPU?
一、核心原理
未定义指令异常可以理解为CPU的“知识盲区”——当它遇到一条不认识的指令时,就会触发这个异常,就像你读书时遇到一个不认识的生词,必须查字典才能继续读下去。我们可以故意在代码里插入一条“生词”(未定义指令),让CPU触发异常,从而观察整个异常处理流程。
二、关键步骤解析
1️⃣ 设置异常向量表
- 代码示例:
_start: b reset ldr pc, _undefined_instruction // 未定义指令异常入口 ldr pc, _software_interrupt // 软中断入口 ... // 其他异常入口 - 通俗解释:
异常向量表是CPU的“应急手册”,当异常发生时,CPU会根据类型跳转到对应入口。比如未定义指令异常对应第二个条目(0x00000004)。- VBAR寄存器:通过
MRC/MCR指令设置向量表基地址(如0x46000000),告诉CPU“应急手册”放在哪里。
- VBAR寄存器:通过
2️⃣ 插入未定义指令
- 代码示例:
.word 0xffffffff // 插入一条CPU不认识的指令 - 通俗解释:
这相当于在书上写一个“乱码单词”,CPU读到这条指令时发现不认识,立刻触发未定义指令异常。
3️⃣ 处理异常流程
(1) 切换模式
- 触发异常后,CPU自动切换为未定义指令模式(CPSR的M位变为
0b11011),此时使用专用的未定义模式寄存器组。
(2) 保存现场
- 保存关键寄存器:将当前状态(CPSR)保存到
SPSR_und,返回地址(下一条指令地址)保存到LR_und。 - 设置栈指针:在未定义模式下初始化栈(如
ldr sp, =0x48000000),用于保存被打断时的寄存器状态(R0-R12)。
(3) 处理异常
- 打印调试信息:在异常处理函数中,可以读取触发异常的指令地址(
LR_und - 4),打印错误信息(如“发现未知指令!”)。 - 模拟指令:如果是故意触发的调试断点,可以替换回原指令并继续执行(类似软件断点功能)。
(4) 恢复现场
- 恢复寄存器:从栈中恢复R0-R12的原始值,将
LR_und的值减去偏移量后赋值给PC,并恢复CPSR状态。 - 返回原程序:CPU继续执行触发异常指令的下一条指令。
三、实战场景类比
1. 设置“应急手册”
就像在手机里设置“紧急联系人”,告诉CPU遇到未定义指令时该联系谁(跳转到undef_handler函数)。
2. 触发异常
故意在代码里写一个“乱码单词”(.word 0xffffffff),相当于在程序中埋一个地雷,CPU踩到后立刻“爆炸”(触发异常)。
3. 处理异常
CPU的异常处理程序就像拆弹专家:
- 第一步:记录爆炸位置(保存PC和CPSR)。
- 第二步:分析炸弹类型(读取错误指令)。
- 第三步:拆除炸弹(处理异常)。
- 第四步:修复现场(恢复寄存器),继续执行后续任务。
四、技术细节补充
1. 偏移量修正
- ARM模式下,未定义指令异常的返回地址需要-4字节(
LR_und - 4),因为触发异常的指令长度为4字节。
2. 调试应用
- 软件断点:将目标指令替换为未定义指令(如UDF指令),触发异常后暂停程序,方便调试器检查内存和寄存器。
3. 安全机制
- 未定义指令异常可用于检测恶意代码:攻击者若插入非法指令,系统可通过异常处理程序拦截并终止其执行。
五、一句话总结
“CPU遇到不认识的指令就像你看不懂生词——触发异常后,它会查‘应急手册’(向量表)、保存当前进度(现场)、调用‘翻译’(处理函数),最后回到原位置继续执行!”
通过故意触发未定义指令异常,可以深入理解ARM处理器的异常处理机制,为调试和系统安全设计打下基础。
通俗解析:ldr pc, _fiq的作用与实现原理
一、核心概念
ldr pc, _fiq 是 ARM 汇编中用于处理 快速中断(FIQ) 的关键指令,它的功能是 将 FIQ 异常处理函数的地址加载到程序计数器(PC),从而实现跳转到快速中断服务程序。
二、指令拆解与类比
1️⃣ 指令结构
-
ldr pc, _fiq 由三部分组成:-
ldr:加载指令,将内存中的数据(地址)加载到目标寄存器。 -
pc:目标寄存器(程序计数器),用于控制程序执行流程。 -
_fiq:标签(Label),指向存储 FIQ 处理函数地址的内存位置。
-
类比:
-
_fiq标签 像快递柜的取件码,存储了快递(FIQ 处理函数地址)的位置。 -
ldr pc, _fiq 就像快递员(CPU)根据取件码(_fiq)找到快递柜中的包裹(地址),送到你手中(PC 寄存器)。
2️⃣ 实际执行过程
-
_fiq标签的定义:
通常在汇编中通过.word或DCD指令定义,例如:_fiq: .word FIQ_Handler ; 将 FIQ_Handler 的地址存入 _fiq 对应的内存- 作用:在内存中预留一个位置(如
0x0000001C,FIQ 异常向量地址),存储 FIQ 处理函数的实际地址。
- 作用:在内存中预留一个位置(如
-
指令执行时:
- CPU 根据
_fiq标签的地址(如0x0000001C),从内存中读取存储的值(如0x8000FF00,即FIQ_Handler的地址)。 - 将该值赋值给 PC,程序跳转到
FIQ_Handler执行。
- CPU 根据
关键点:
- 地址无关性:
ldr pc, _fiq是 绝对地址跳转,跳转目标由链接器在编译时确定,因此需确保代码已加载到正确内存位置。 - 异常向量表:FIQ 异常向量位于
0x0000001C,该指令必须放置在此地址,确保 CPU 触发 FIQ 时能自动执行此跳转。
三、与 B 指令的区别
在异常向量表中,复位异常(Reset) 通常使用 B reset,而 其他异常(如 FIQ) 使用 ldr pc, _xx,原因如下:
- 复位异常的特殊性:
- 复位时可能尚未初始化内存(如 MMU 未启用),
B指令的 相对跳转 更可靠(不依赖绝对地址)。
- 复位时可能尚未初始化内存(如 MMU 未启用),
- 其他异常的需求:
- FIQ 处理函数可能位于较远地址(如链接脚本指定的高端内存),
ldr pc, _fiq的 绝对跳转 能力更灵活。
- FIQ 处理函数可能位于较远地址(如链接脚本指定的高端内存),
四、技术细节
1️⃣ 内存访问与流水线
- ARM 采用 三级流水线(取指→译码→执行),执行
ldr pc, _fiq时:- PC 实际值为当前指令地址 + 8(流水线补偿)。
- 例如,若
_fiq标签位于0x0000001C,则实际加载的地址为[0x1C + 8]处的值。
2️⃣ 伪指令与真实指令
-
ldr pc, =label 是伪指令,编译后会转换为ldr pc, [pc, #offset],例如:ldr pc, =FIQ_Handler ; 伪指令 ; 编译后变为: ldr pc, [pc, #804] ; 真实指令,从 PC+804 处读取地址[1,4](@ref)
五、应用场景
- 嵌入式系统启动:
- 在 Bootloader 的异常向量表中,通过
ldr pc, _fiq指定 FIQ 处理函数,确保中断触发时能正确响应。
- 在 Bootloader 的异常向量表中,通过
- 动态重定位:
- 若代码从 Flash 复制到 RAM 运行,需在重定位后使用
ldr pc, _fiq,使跳转地址指向 RAM 中的处理函数。
- 若代码从 Flash 复制到 RAM 运行,需在重定位后使用
一句话总结
“ldr pc, _fiq 是 CPU 处理快速中断的‘导航指令’:根据内存中预设的地址(_fiq),跳转到中断服务程序,确保紧急任务优先执行。”
理解其原理,能帮助开发者设计可靠的中断处理机制,尤其在内存管理和启动流程中至关重要。
通俗解析:LDR是什么?为什么说它像“快递员”和“代购”?
一、核心概念
LDR(Load Register) 是 ARM 架构中的一条指令,主要作用是从内存中搬运数据到寄存器,相当于一个“数据快递员”或“内存代购”。它的核心功能可以拆解为两种场景:
- 作为指令:直接搬运内存中的数据到寄存器(像快递员取快递)。
- 作为伪指令:直接给寄存器赋值大数字或地址(像代购直接送货上门)。
二、LDR的两种身份
1. 当“快递员”:搬运内存数据
- 功能:从内存地址读取数据(如数字、代码、变量)到寄存器。
- 语法:
LDR R0, [R1]- 含义:从内存地址
R1处读取数据,存入R0寄存器。
- 含义:从内存地址
- 变种:
- LDRB:搬运一个字节(如读一个字符)。
- LDRH:搬运半个字(如读一个短整数)。
生活比喻:
假设寄存器是你的手,内存是仓库,LDR 就像从仓库货架(内存地址)上取一个包裹(数据)放到手里(寄存器)。
2. 当“代购”:直接赋值大数字
- 功能:给寄存器赋一个很大的立即数(如
0x12345678)或地址。 - 语法:
LDR R0, =0x12345678- 含义:直接把
0x12345678这个值存入R0,编译器会帮你处理内存分配。
- 含义:直接把
生活比喻:
如果你想买一个超大包裹(比如 32 位数字),但快递员(MOV 指令)只能带小包裹(12 位以内),LDR 伪指令就像代购,先帮你把包裹存到附近的寄存点(临时内存),再取回来给你。
三、为什么需要 LDR?
- 解决 MOV 的局限:
MOV只能赋值小数字(如0xFF),而 LDR 伪指令可以赋值任意大数字(如0xABCD1234)。
- 灵活访问内存:
- 支持多种寻址方式,如基址+偏移(
LDR R0, [R1, #8])、自动更新地址(LDR R0, [R1], #8)等。
- 支持多种寻址方式,如基址+偏移(
- 跳转功能:
- 当
PC(程序计数器)作为目标寄存器时,LDR 可以实现绝对地址跳转(如LDR PC, =main)。
- 当
四、常见误区
- 带不带等号的区别:
LDR R0, [R1]:搬运内存数据(快递员模式)。LDR R0, =0x123:直接赋值(代购模式)。
- 地址对齐问题:
- ARM 要求某些指令的地址必须是 4 字节对齐(如字操作),否则会触发异常。
- 跳转范围限制:
- LDR 作为跳转指令时,地址范围受限于当前 PC 的 4KB 范围(需配合其他指令长距离跳转)。
五、一句话总结
“LDR 像快递员和代购的结合体:既能从内存仓库取货到寄存器,也能直接给寄存器送大包裹。关键在于有没有等号(=)!”
理解它的双重身份,就能在 ARM 编程中灵活处理数据和地址操作。
07_实战_SVC异常
实战_SVC异常
要想深入理解异常处理,需要写程序来验证。 本节课程故意执行一条SVC指令,让它触发异常。
参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf
1.1 A7的异常向量表
从向量表可以看出,A7支持哪些异常:未定义指令、软中断(SVC)、预取指令中止、数据中止、IRQ、FIQ。
_start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
1.2 什么是SVC指令?
在ARM指令中,有一条指令:
SVC #VAL
它会触发一个异常。 在操作系统中,比如各类RTOS或者Linux,都会使用SVC指令故意触发异常,从而导致内核的异常处理函数被调用,进而去使用内核的服务。 比如Linux中,各类文件操作的函数open、read、write,它的实质都是SVC指令。 本节课程不讲解SVC在内核中的使用,我们只是看看如何处理SVC触发的异常。
1.3 在汇编代码里插入SVC指令
在代码中插入:
SVC #1
看看会发生什么事情。
1.4 编程
1.4.1 设置SVC模式的栈
1.4.2 保存现场
1.4.3 处理异常
1.4.4 恢复现场
一、核心概念比喻
SVC指令可以理解为用户程序向操作系统“按紧急按钮”。当用户程序需要调用内核功能(如读写文件、创建进程)时,通过执行SVC #VAL指令触发异常,CPU会暂停当前任务,切换到特权模式(如SVC模式),调用内核的异常处理程序完成请求,再返回用户程序继续执行。
二、实战步骤解析
1️⃣ 设置异常向量表
- 代码示例:
_start: b reset ldr pc, _software_interrupt // SVC异常入口(地址0x00000008) ... // 其他异常入口 - 通俗解释:
异常向量表是CPU的“应急通讯录”,SVC异常对应第二项(地址0x00000008)。当执行SVC指令时,CPU会跳转到_software_interrupt标签指向的处理函数。
2️⃣ 插入SVC指令
- 代码示例:
SVC #1 // 故意触发异常,VAL=1表示请求类型(如系统调用编号) - 通俗解释:
这相当于在程序中埋一个“求救信号弹”,#1是传递给内核的暗号(如请求打印日志)。CPU执行到此会立即触发异常,进入SVC模式。
3️⃣ 处理SVC异常的流程
(1) 硬件自动操作(四大步三小步)
- 保存现场:
- 将当前模式(如用户模式)的
CPSR保存到SPSR_svc(SVC模式的备份状态寄存器)。 - 将下一条指令地址(
PC-4)保存到LR_svc(SVC模式的返回地址寄存器)。
- 将当前模式(如用户模式)的
- 切换模式:
- 切换到SVC模式(特权模式),关闭IRQ中断(防止干扰)。
- 跳转入口:
- CPU从向量表
0x00000008处读取ldr pc, _software_interrupt,跳转到SVC处理函数。
- CPU从向量表
(2) 软件处理(手动操作)
-
设置SVC模式的栈:
ldr sp, =0x48000000 // 初始化SVC模式的栈指针作用:SVC模式有独立栈空间,用于保存寄存器等临时数据。
-
保存寄存器:
stmfd sp!, {r0-r12, lr} // 将用户程序寄存器压栈(保护现场)类比:像暂停游戏时保存进度,防止处理异常时覆盖原数据。
-
解析SVC指令:
ldr r0, [lr, #-4] // 从触发异常的指令地址(LR-4)读取SVC #1 and r0, r0, #0xFF // 提取VAL=1(低8位为请求编号)原理:通过
LR_svc - 4找到触发异常的SVC指令,解析参数。 -
处理请求:
根据VAL的值执行对应操作(如打印调试信息),完成后恢复现场。 -
恢复寄存器并返回:
ldmfd sp!, {r0-r12, pc}^ // 出栈恢复寄存器,^表示恢复CPSR关键细节:
SUBS PC, LR, #4会修正返回地址,让程序回到SVC指令的下一条继续执行。
三、技术细节补充
1. 为什么需要SVC #VAL?
- 权限隔离:用户程序无权直接操作硬件(如内存管理),需通过SVC指令请求内核代劳。
- 统一入口:所有系统调用通过
VAL参数区分功能,内核只需一个处理函数即可分发请求。
2. 与普通函数调用的区别
- 模式切换:SVC会触发CPU模式切换(用户模式→SVC模式),普通调用无此过程。
- 硬件支持:异常处理由硬件自动保存关键寄存器,软件只需处理业务逻辑。
3. 实际应用场景
- 操作系统内核:Linux的系统调用(如
open())最终编译为SVC指令。 - 调试工具:通过
SVC插入断点,触发异常后暂停程序并检查内存。
四、一句话总结
“SVC指令是用户程序与内核的‘加密对讲机’:发送暗号(VAL)触发异常,内核接听后处理请求,再让程序无缝回到原位置继续执行。”
通过实战理解SVC异常处理,能掌握操作系统的底层交互机制,为系统级开发打下基础。
08_中断的硬件框架
中断的硬件框架
1.1 中断路径上的3个部件
-
中断源 中断源多种多样,比如GPIO、定时器、UART、DMA等等。 它们都有自己的寄存器,可以进行相关设置:使能中断、中断状态、中断类型等等。
-
中断控制器 各种中断源发出的中断信号,汇聚到中断控制器。 可以在中断控制器中设置各个中断的优先级。 中断控制器会向CPU发出中断信号,CPU可以读取中断控制器的寄存器,判断当前处理的是哪个中断。 中断控制器有多种实现,比如:
-
STM32F103中被称为NVIC:Nested vectored interrupt controller(嵌套向量中断控制器)
-
ARM9中一般是芯片厂家自己实现的,没有统一标准
-
Cortex A7中使用GIC(Generic Interrupt Controller)
-
-
CPU CPU每执行完一条指令,都会判断一下是否有中断发生了。 CPU也有自己的寄存器,可以设置它来使能/禁止中断,这是中断处理的总开关。

1.2 STM32F103的GPIO中断
如果搞忘了硬件是怎么判断上升沿和下降沿的可以去看看D触发器
D触发器构成及原理 - 知乎
参考资料:STM32F103数据手册.pdf、ARM Cortex-M3与Cortex-M4权威指南.pdf、PM0056.pdf
对于GPIO中断,STM32F103又引入了External interrupt/event controller (EXTI)。 用来设置GPIO的中断类型,如下图:
EXTI可以给NVIC提供16个中断信号:EXTI0~EXTI15。 那么某个EXTIx,它来自哪些GPIO呢?这需要设置GPIO控制器。
1.2.1 GPIO控制器
STM32F103的GPIO控制器中有AFIO_EXTICR1~AFIO_EXTICR4一共4个寄存器 名为:External interrupt configuration register,外部中断配置寄存器。 用来选择某个外部中断EXTIx的中断源,示例如下:
注意:从上图可知,EXTI0只能从PA0、……、PG0中选择一个,这也意味着PA0、……、PG0中只有一个引脚可以用于中断。这跟其他芯片不一样,很多芯片的任一GPIO引脚都可以同时用于中断。 
1.2.2 EXTI
在GPIO控制器中,可以设置某个GPIO引脚作为中断源,给EXTI提供中断信号。 但是,这个中断的触发方式是怎么的?高电平触发、低电平触发、上升沿触发、下降沿触发? 这需要进一步设置。 EXTI框图如下: 
沿着上面框图中的红线,我们要设置:
-
Falling trigger selection register:是否选择下降沿触发
-
Rising trigger selection register:是否选择上升沿触发
-
Interrupt mask register:是否屏蔽中断
当发生中断时,可以读取下列寄存器判断是否发生了中断、发生了哪个中断:
-
Pending reqeust register
这张图是外部中断/事件控制器(External Interrupt/Event Controller)的“接线图”,可以把它想象成一个公司前台,负责接收各种外部设备(如按键、传感器)的“紧急来电”(中断请求),并转接给老板(CPU)处理。以下是核心模块的比喻解析:
一、核心模块与生活类比
| 模块名称 | 生活比喻 | 功能详解 |
|---|---|---|
| AMBA APB总线 | 公司内部的电话线路 | 连接外设和控制器,传输时钟(PCLK2)和中断信号。 |
| 外设接口 | 前台电话总机 | 接收外部设备的“来电”(中断请求),如按键按下、传感器报警。 |
| 待处理请求寄存器 | 来电登记簿 | 记录哪些设备发起了中断请求(如“3号设备来电未接”)。 |
| 中断屏蔽寄存器 | 免打扰开关 | 决定是否忽略某些设备的来电(如“今天不接3号设备的电话”)。 |
| 软件中断事件寄存器 | 手动拨号键 | 允许软件主动触发中断(如前台自己拨电话给老板)。 |
| 上升沿/下降沿触发选择寄存器 | 来电触发条件 | 定义何时算作有效来电(如“只在电话铃声响起的瞬间触发”或“挂断瞬间触发”)。 |
| 脉冲发生器 & 边沿检测电路 | 电话铃声识别器 | 检测外设信号的变化(如电压从低到高跳变),生成一个“铃声脉冲”,表示有中断需要处理。 |
| 输入线 | 外部设备的电话线(共19条) | 每个设备(如键盘、鼠标)独占一条线,避免抢线冲突。 |
| NVIC中断控制器 | 老板的秘书 | 根据优先级将中断转给CPU处理(如“先接客户电话,再处理打印机问题”)。 |
二、中断处理流程(以按键触发为例)
-
来电触发:
- 按键按下 → 输入线(如线3)电压从高变低(下降沿)。
- 边沿检测电路识别到变化,生成脉冲信号。
-
登记来电:
- 若中断屏蔽寄存器未屏蔽线3 → 脉冲信号写入待处理请求寄存器(标记线3有请求)。
-
转接优先级:
- NVIC检查所有待处理请求,根据优先级决定先处理哪个(如线3优先级高于线5)。
-
老板接电话:
- CPU暂停当前任务,跳转到中断处理函数(如执行按键扫描程序)。
-
挂断恢复:
- 处理完成后,清除待处理请求,CPU继续原来的工作。
三、关键细节
-
为什么需要“上升沿/下降沿触发”?
- 避免误触发:比如按键抖动会产生多次电平变化,只有指定边沿(如下降沿)才会触发中断。
- 应用场景:
- 上升沿触发:适合检测信号从低到高的跳变(如温度超过阈值)。
- 下降沿触发:适合检测信号从高到低的跳变(如按键松开)。
-
软件中断事件的作用
- 模拟外部中断:测试时无需真实外设,软件写寄存器即可触发中断。
- 紧急任务调度:操作系统可主动触发中断,强制CPU处理高优先级任务。
-
“19”的含义
- 图中多次出现的“19”代表19条独立的中断输入线,每条线可连接一个外设(如STM32的EXTI0-EXTI18)。
四、一句话总结
“外部中断控制器像公司前台:登记来电、过滤骚扰电话、按优先级转接给老板,确保紧急事件优先处理!”
理解这张图,就能掌握CPU如何高效响应外部设备的实时请求,为嵌入式开发中的中断编程打下基础。
要使用EXTI,流程如下:
这张图讲的是如何让单片机的外设(比如按键、传感器)通过 中断 的方式高效通知 CPU 处理紧急任务。整个过程可以类比为 公司前台接电话的规则设置,分三步完成:
一、设置“允许谁打电话”(EXTI_IMR 屏蔽位)
- 硬件行为:
每个外设连接一条“电话线”(共20条中断线路)。通过 EXTI_IMR 寄存器 的屏蔽位,决定哪些线路能接通。 - 操作示例:
EXTI->IMR |= (1 << 3); // 允许线3的中断(如按键按下) EXTI->IMR &= ~(1 << 5); // 屏蔽线5的中断(如不需要响应的传感器) - 生活比喻:
前台有一部总机,但只有白名单上的员工(如线3)能拨通电话,其他人(如线5)的电话会被直接挂断。
二、设置“电话响铃的条件”(EXTI_RTSR 和 EXTI_FTSR 触发选择位)
- 硬件行为:
每条线路可独立设置触发条件:- 上升沿触发(EXTI_RTSR):电压从低变高时触发(如按键按下瞬间)。
- 下降沿触发(EXTI_FTSR):电压从高变低时触发(如按键松开瞬间)。
- 操作示例:
EXTI->RTSR |= (1 << 3); // 线3设置为上升沿触发(按键按下时触发) EXTI->FTSR |= (1 << 5); // 线5设置为下降沿触发(传感器信号消失时触发) - 生活比喻:
前台规定:- 员工A(线3)只有说“紧急”(上升沿)才会转接电话。
- 员工B(线5)只有说“故障”(下降沿)才会转接电话。
三、设置“电话如何转接”(NVIC 中断控制器配置)
- 硬件行为:
NVIC(嵌套向量中断控制器)是中断的“总调度员”,负责将不同线路的中断请求转发给 CPU,并设置优先级。 - 操作示例:
NVIC_EnableIRQ(EXTI3_IRQn); // 启用线3的中断通道 NVIC_SetPriority(EXTI3_IRQn, 0); // 设置线3为最高优先级 - 生活比喻:
前台给每个员工分配专属电话线(IRQ 通道),并设置优先级:- 消防通道(线3)的电话直接转接总经理(高优先级)。
- 普通快递(线15)的电话转接后勤部(低优先级)。
四、总结流程
- 允许谁打电话 → EXTI_IMR 寄存器(白名单过滤)。
- 电话响铃的条件 → EXTI_RTSR/EXTI_FTSR 寄存器(边沿触发规则)。
- 电话如何转接 → NVIC 配置(通道使能、优先级排序)。
注意事项:
- 配置顺序建议:先设置中断线路(步骤1-2),再开启 NVIC 总闸(步骤3)。
- 未正确配置时,可能导致中断无法触发或优先级混乱(如低优先级任务阻塞高优先级)。
一句话总结
“配置硬件中断就像安排公司前台接电话:先确定哪些员工能打电话(EXTI_IMR),再规定什么情况算紧急来电(EXTI_RTSR/FTSR),最后分配专属线路和优先级(NVIC)!”
掌握这三步,就能让单片机高效响应外设的实时请求。
翻译如下:
-
配置EXTI_IMR:允许EXTI发出中断
-
配置EXTI_RTSR、EXTI_FTSR,选择中断触发方式
-
配置NVIC中的寄存器,允许NVIC把中断发给CPU
1.2.3 NVIC
多个中断源汇聚到NVIC,NVIC的职责就是从多个中断源中取出优先级最高的中断,向CPU发出中断信号。 处理中断时,程序可以写NVIC的寄存器,清除中断。 涉及的寄存器: 
这张图是 中断控制器(NVIC)的“电话转接规则手册”,它定义了如何设置中断的开关、优先级和状态监控。可以把整个系统想象成 公司前台的总机电话系统,每个寄存器对应不同的管理功能:
一、核心寄存器与生活类比
| 寄存器名称 | 生活比喻 | 功能详解 |
|---|---|---|
| 中断设置使能寄存器(ISER) | 接电话的白名单 | 决定哪些设备可以打电话进来(写1开启某个中断通道)。例:开启按键中断 → 允许按键拨通电话。 |
| 中断清除使能寄存器(ICER) | 拉黑名单 | 禁止某些设备打电话(写1关闭某个中断通道)。例:关闭传感器中断 → 忽略传感器的来电。 |
| 中断设置挂起寄存器(ISPR) | 手动拨号键 | 软件主动触发中断(写1模拟设备来电)。例:测试时手动触发按键中断,无需真的按下按键。 |
| 中断清除挂起寄存器(ICPR) | 取消手动拨号 | 撤销软件触发的假来电(写1清除挂起状态)。例:测试结束后取消模拟中断。 |
| 中断活跃状态寄存器(IABR) | 电话接听状态屏 | 显示当前正在处理哪些中断(只读)。例:屏幕显示“正在处理3号电话(按键中断)”。 |
| 中断优先级寄存器(IP) | VIP客户分级 | 设置不同中断的优先级(数值越小越优先)。例:火灾报警(优先级0) > 打印机缺纸(优先级5)。 |
| 软件触发中断寄存器(STIR) | 内部紧急呼叫按钮 | 软件直接触发中断(如系统调用)。例:程序主动按下按钮,强制前台转接电话给老板。 |
二、关键操作示例
-
开启按键中断(允许接电话)
NVIC->ISER[0] |= (1 << 3); // 开启中断编号3(按键中断)- 地址:0xE000E100(ISER[0]对应中断0~31)。
- 效果:允许按键触发中断,类似将按键加入白名单。
-
设置火灾报警最高优先级
NVIC->IP[5] = 0x00; // 中断编号5(火灾报警)优先级设为0(最高) NVIC->IP[6] = 0x80; // 中断编号6(打印机缺纸)优先级设为8(较低)- 地址:0xE000E400~0xE000E4EF(每个中断占1字节)。
- 规则:优先级数值越小越优先(0 > 8)。
-
检查当前处理的中断
if (NVIC->IABR[0] & (1 << 3)) { printf("正在处理按键中断!"); }- 地址:0xE000E300(IABR[0]对应中断0~31)。
- 特性:只读寄存器,实时显示活跃中断。
三、实际应用场景
-
按键中断处理流程
- 步骤1:通过
ISER开启按键中断。 - 步骤2:通过
IP设置按键优先级。 - 步骤3:按键按下触发中断 → CPU处理中断函数。
- 步骤4:处理完成后,通过
ICPR清除挂起状态。
- 步骤1:通过
-
软件调试技巧
- 模拟中断:通过
ISPR手动触发中断,测试处理逻辑。 - 强制中断:通过
STIR触发系统调用(如Linux的svc指令)。
- 模拟中断:通过
四、注意事项
-
寄存器分组:
- 多个寄存器(如ISER[0]~ISER[7])管理256个中断,每组32位控制32个中断。
- 例:中断编号35 → 属于
ISER[1]的第3位(35=32×1 + 3)。
-
优先级抢占规则:
- 高优先级中断可打断低优先级中断(类似VIP客户插队)。
- 相同优先级时,按硬件中断编号顺序处理。
-
清除挂起状态:
- 处理完中断后必须清除挂起位(
ICPR),否则会重复触发。
- 处理完中断后必须清除挂起位(
我们暂时只需要关注:ISER(中断设置使能寄存器)、ICPR(中断清除挂起寄存器)。 要注意的是,这些寄存器有很多个,比如ISER0、ISER1等等。里面的每一位对应一个中断。 ISER0中的bit0对应异常向量表中的第16项(向量表从第0项开始),如下图:
这张图是 单片机中断系统的“开关面板说明书”,它描述了如何通过一组寄存器(可以理解为开关)来 允许或禁止某些中断的触发。用生活中的例子类比,就像 管理一个酒店的电话总机,决定哪些房间的电话能接通前台,哪些直接挂断。
一、核心概念与生活类比
| 寄存器名称 | 生活比喻 | 功能详解 |
|---|---|---|
| ISER(Interrupt Set Enable Register) | 接听白名单:允许特定房间的电话接入前台。 | 每个开关(Bit位)对应一个中断(如火灾报警、客房服务),打开开关(写1)即允许该中断触发。 |
| ICER(Interrupt Clear Enable Register) | 挂断黑名单:禁止特定房间的电话接入前台。 | 关闭开关(写1)即禁止该中断触发,即使房间拨打前台电话也会被忽略。 |
| Bit位与中断号 | 电话分机号:每个开关对应一个房间的分机号(如101房对应中断0,201房对应中断1)。 | 表格中的“中断#0(异常#16)”表示:中断0对应ARM架构中的异常编号16。 |
| 寄存器分组 | 楼层总控:每层楼有一个总开关箱(ISER[0]控制1楼的0~31号房间,ISER[1]控制2楼的32~63号房间)。 | 通过多个寄存器分组管理大量中断(如ISER[0]-[2]共管理96个中断)。 |
二、操作规则与示例
-
允许火灾报警(中断0)触发
// 地址0xE000E100对应ISER[0],将Bit0置1(二进制:00000001) NVIC->ISER[0] |= (1 << 0); // 允许中断0(火灾报警)- 效果:当火灾传感器触发时,CPU会立即响应。
-
禁止打印机缺纸中断(中断31)
// 地址0xE000E180对应ICER[0],将Bit31置1 NVIC->ICER[0] |= (1 << 31); // 禁止中断31(打印机缺纸)- 效果:即使打印机缺纸,也不会打扰CPU处理其他任务。
-
查看当前允许的中断
if (NVIC->ISER[0] & (1 << 3)) { printf("3号中断(如门禁报警)已允许!"); }- 原理:读取寄存器的值,判断对应Bit位是否为1。
三、技术细节与注意事项
-
地址范围与分组逻辑
- ISER组:地址
0xE000E100~0xE000E11C,每组寄存器管理32个中断。ISER[0]:管理中断0~31ISER[1]:管理中断32~63ISER[2]:管理中断64~95
- ICER组:地址
0xE000E180~0xE000E19C,分组规则与ISER相同。
- ISER组:地址
-
读写规则
- 写1生效:向ISER的Bit位写1 → 允许中断;向ICER的Bit位写1 → 禁止中断。
- 写0无效:向任意寄存器写0均不改变状态。
- 读出值:直接反映当前中断的使能状态(1=允许,0=禁止)。
-
复位值
- 所有寄存器复位值均为0 → 默认禁止所有中断,需手动开启所需中断。
四、实际应用场景
- 系统初始化:
- 启动时仅开启关键中断(如系统定时器),其他中断按需动态开启。
- 节能模式:
- 休眠前通过ICER关闭非必要中断(如按键检测),降低功耗。
- 调试阶段:
- 逐步开启中断,定位故障源(如仅开启串口中断,排除其他干扰)。
一句话总结
“这张图是单片机中断系统的总控开关面板:ISER是接听白名单,ICER是挂断黑名单,每个开关对应一个紧急电话(中断),写1生效,写0无效!”
掌握这些寄存器的操作,就能精准控制哪些中断能打断CPU,哪些被静默忽略。
一句话总结
“NVIC寄存器像公司电话总控台:用白名单(ISER)接电话,用黑名单(ICER)挂电话,用优先级(IP)决定谁先插队,用状态屏(IABR)看谁在通话!”
掌握这些寄存器的操作,就能让单片机精准管理各类中断请求。
1.2.4 CPU
cortex M3/M4处理器内部有这几个寄存器:
1. PRIMASK
把PRIMASK的bit0设置为1,就可以屏蔽所有优先级可配置的中断。 可以使用这些指令来设置它:
CPSIE I ; 清除PRIMASK,使能中断 CPSID I ; 设置PRIMASK,禁止中断 或者: MOV R0, #1 MSR PRIMASK R0 ; 将1写入PRIMASK禁止所有中断 MOV R0, #0 MSR PRIMASK, R0 ; 将0写入PRIMASK使能所有中断
这张图讲的是ARM处理器中一个名为 PRIMASK(优先级掩码寄存器) 的“紧急开关”,它的核心功能可以概括为:一键静音所有非紧急的中断和异常,确保CPU专注处理当前任务。以下用生活场景类比:
一、核心功能——什么时候需要“免打扰”?
- 场景类比:
当你需要专注写代码或开会时,打开手机“免打扰模式”——此时所有短信、来电(包括普通优先级)都会被静音,只有特定紧急电话(如老板来电)能打断你。 - 技术对应:
PRIMASK寄存器的作用类似这个“免打扰开关”:- 第0位 = 1:开启免打扰 → 屏蔽所有可配置优先级的中断(如定时器中断、外设中断)。
- 第0位 = 0:关闭免打扰 → 恢复正常中断响应。
二、寄存器结构解析(图5和表7)
| 寄存器结构 | 生活比喻 | 技术细节 |
|---|---|---|
| Bit 0(PRIMASK位) | 免打扰开关:唯一有效的控制位。 | - 0:正常模式(允许所有中断)。 - 1:免打扰模式(屏蔽所有可配置优先级的中断)。 |
| Bits 1~31 | 备用开关:目前没有实际功能,为未来扩展预留。 | 这些位必须保持默认值(通常写0),随意修改可能导致未知错误。 |
| 版本号(MSv39638V1) | 开关型号:不同版本处理器的寄存器功能可能微调,需参考对应手册。 | 确保代码与硬件版本兼容,避免功能异常。 |
三、适用场景与注意事项
-
什么时候用PRIMASK?
- 执行关键代码:如操作系统的任务切换、内存管理,需避免被中断打断。
- 实时性要求极高:如电机控制中,必须确保一段代码完整执行,否则可能导致硬件故障。
-
注意事项
- 及时关闭:免打扰模式结束后需恢复Bit 0 = 0,否则系统将无法响应后续中断。
- 不影响不可屏蔽中断:某些异常(如复位、硬件错误)优先级不可配置,即使开启PRIMASK也会触发。
- 嵌套使用:在复杂代码中,需记录PRIMASK的原始状态,处理完毕后恢复原值(避免破坏其他模块逻辑)。
四、操作示例(汇编指令)
CPSID I ; 开启免打扰(Bit 0 = 1)
; 此处执行关键代码(如修改系统配置)
CPSIE I ; 关闭免打扰(Bit 0 = 0)
一句话总结
“PRIMASK像手机的免打扰模式:一键屏蔽所有‘不重要的电话’(中断),让CPU专注处理当前任务。但切记处理完要关闭,否则可能错过老板来电(系统崩溃)!”
理解其原理,能帮助开发者在关键代码段避免意外中断干扰,提升系统稳定性。
2. FAULTMASK
FAULTMASK和PRIMASK很像,它更进一步,出来一般的中断外,把HardFault都禁止了。 只有NMI可以发生。 可以使用这些指令来设置它:
CPSIE F ; 清除FAULTMASK CPSID F ; 设置FAULTMASK 或者: MOV R0, #1 MSR FAULTMASK R0 ; 将1写入FAULTMASK禁止中断 MOV R0, #0 MSR FAULTMASK, R0 ; 将0写入FAULTMASK使能中断
这张图讲的是ARM处理器中一个名为 FAULTMASK(故障掩码寄存器) 的“终极防护开关”。它的核心功能是 在系统遇到严重问题时,强制屏蔽绝大多数异常和中断,只保留最底层的不可屏蔽中断(NMI)。以下用生活场景类比:
一、核心功能——什么时候需要“紧急隔离”?
- 场景类比:
医院手术室遇到地震时,会启动“紧急隔离模式”——关闭所有非必要通道(普通异常),仅保留生命维持系统(如氧气供应)和地震警报(NMI),确保核心功能不受干扰。 - 技术对应:
- FAULTMASK = 1:开启紧急隔离 → 屏蔽所有可屏蔽的异常(如硬件中断、普通错误)。
- FAULTMASK = 0:恢复正常模式 → 允许所有异常触发。
二、寄存器结构解析(图6和表8)
| 寄存器结构 | 生活比喻 | 技术细节 |
|---|---|---|
| Bit 0(FAULTMASK位) | 紧急隔离开关:唯一有效的控制位。 | - 0:正常模式(允许所有异常)。 - 1:隔离模式(仅允许NMI,如电源故障)。 |
| Bits 1~31 | 备用开关:当前无功能,禁止随意操作。 | 这些位必须保持默认值(全0),修改可能导致系统崩溃。 |
| NMI(不可屏蔽中断) | 终极警报:即使隔离模式开启也必须响应(如地震警报、核电站紧急停机)。 | NMI的优先级高于FAULTMASK,确保关键事件不被忽略。 |
三、适用场景与注意事项
-
什么时候用FAULTMASK?
- 处理致命错误:如内存访问越界、硬件总线错误,需暂停其他任务专心修复。
- 系统崩溃前自救:在触发硬错误(HardFault)前,强制进入隔离状态,尝试保存关键数据。
-
注意事项
- 慎用!:开启后系统几乎“冻结”,只有NMI能响应(如看门狗复位)。
- 及时恢复:处理完必须关闭(Bit 0 = 0),否则系统无法恢复。
- 与PRIMASK的区别:
- PRIMASK:屏蔽普通中断(如按键、定时器),但允许错误异常(如内存错误)。
- FAULTMASK:更严格,屏蔽几乎所有异常(包括普通错误),仅保留NMI。
四、操作示例(汇编指令)
CPSID F ; 开启紧急隔离(Bit 0 = 1)
; 此处执行关键恢复操作(如备份数据)
CPSIE F ; 关闭紧急隔离(Bit 0 = 0)
一句话总结
“FAULTMASK像核电站的紧急封锁按钮:按下后屏蔽所有非致命操作,只保留最关键的生命线(NMI),用于系统崩溃前的最后挣扎!”
理解其原理,能在系统严重故障时争取宝贵的修复时间,但需谨慎操作避免雪上加霜。
3. BASEPRI
BASEPRI用来屏蔽这些中断:它们的优先级,其值大于或等于BASEPRI。 可以使用这些指令来设置它:
MOVS R0, #0x60 MSR BASEPRI, R0 ; 禁止优先级在0x60~0xFF间的中断 MRS R0, BASEPRI ; 读取BASEPRI MOVS R0, #0 MSR BASEPRI, R0 ; 取消BASEPRI屏蔽
这张图讲的是ARM处理器中一个名为 BASEPRI(基优先级掩码寄存器) 的“优先级过滤器”,它的核心功能是 设置一个最低门槛,让CPU只处理优先级高于这个门槛的中断或异常。用生活场景类比,就像 公司前台规定“只接待VIP客户”:
一、核心功能——如何“过滤”中断?
-
设置门槛值:
- BASEPRI = 0 → 所有中断都能触发(前台接待所有访客)。
- BASEPRI = 非零值(如0xA0) → 优先级≤0xA0的中断被屏蔽(前台只接待优先级高于0xA0的VIP客户)。
-
优先级规则:
- 数值越小,优先级越高(如0x00 > 0x80)。
- 举例:
- 火灾报警(优先级0x10) > 打印机缺纸(优先级0xA0)。
- 若设置BASEPRI=0xA0 → 只处理火灾报警,忽略打印机缺纸。
二、寄存器结构解析(图7和表9)
| 寄存器结构 | 生活比喻 | 技术细节 |
|---|---|---|
| Bits 7:4(优先级掩码位) | VIP客户门槛值:用4位二进制数定义优先级门槛(范围0x00~0xF0)。 | - 0x00:关闭过滤,允许所有中断。 - 非零值:仅允许优先级更高的中断触发。 |
| Bits 31:8 和 3:0 | 备用开关:禁止随意操作(可能导致系统异常)。 | 必须保持默认值(全0),否则可能引发未知错误。 |
三、适用场景与操作示例
-
何时使用?
- 关键任务保护:执行重要代码时屏蔽低优先级中断(如电机控制时忽略按键检测)。
- 动态调整优先级:在不同场景下灵活调整门槛值(如开机时允许更多中断,运行中收紧限制)。
-
操作示例
// 设置优先级门槛为0xA0(二进制1010 0000) __set_BASEPRI(0xA0); // 优先级≤0xA0的中断全部屏蔽 // 执行关键代码(如传感器校准) __set_BASEPRI(0x00); // 恢复允许所有中断
四、注意事项
-
数值越大,优先级越低:
- 设置
BASEPRI=0xA0时,实际屏蔽的是优先级值≥0xA0的中断(数值越大优先级越低)。 - 容易误解点:这里的“优先级”是硬件层面的逻辑值,与应用层定义的优先级可能相反。
- 设置
-
与PRIMASK的区别:
- PRIMASK:一键屏蔽所有中断(类似“闭门谢客”)。
- BASEPRI:选择性屏蔽低优先级中断(类似“仅接待VIP客户”)。
一句话总结
“BASEPRI像公司前台的VIP过滤器:设置一个最低门槛,只放行优先级高的‘客户’(中断),让CPU专注处理重要任务!”
掌握这一机制,可以精细控制中断响应,在实时系统中平衡效率与关键任务安全。
1.3 STM32MP157的GPIO中断
STM32MP157的GPIO中断在硬件上的框架,跟STM32F103是类似的。 它们的中断控制器不一样,STM32MP157中使用的是GIC: 
1.3.1 GPIO控制器
对于STM32MP157,除了把GPIO引脚配置为输入功能外,GPIO控制器里没有中断相关的寄存器。 请参考前面的课程《01_使用按键控制LED(STM32MP157)》。
1.3.2 EXTI
GPIO引脚可以向CPU发出中断信号,所有的GPIO引脚都可以吗? 不是的,需要在EXTI控制器中设置、选择。 GPIO引脚触发中断的方式是怎样的?高电平触发、低电平触发、上升沿触发、下降沿触发? 这需要进一步设置。 这些,都是在EXTI中配置,EXTI框图如下:
这张图是单片机中 EXTI(外部中断/事件控制器) 的“工作流程图”,你可以把它想象成 公司前台的接线总机系统,专门负责接收和转接各种外部设备的“紧急来电”(比如按键按下、传感器报警)。以下是核心模块和流程的比喻解释:
一、核心模块与功能
| 模块名称 | 生活比喻 | 技术功能 |
|---|---|---|
| GPIO/外设/PWR | 打电话的部门:不同部门(如按键、传感器、电源)通过电话线(引脚)联系前台。 | 外部设备通过GPIO引脚或内部模块(如电源管理PWR)发送中断请求。 |
| EXTIMUX | 电话转接台:前台根据规则将不同号码(引脚)的来电转接到指定线路(中断线)。 | 多路复用器(MUX)选择某个引脚连接到对应的中断线(如EXTI0~EXTI15)。 |
| Trigger & Masking | 接听规则: 1. Trigger:来电触发条件(如响铃一声还是两声接听)。 2. Masking:暂时挂断某些部门的电话(屏蔽中断)。 | 配置中断触发方式(上升沿/下降沿)和屏蔽不需要的中断请求。 |
| EVG(事件生成器) | 紧急广播:将重要来电(如火灾警报)直接转给负责人,无需等待前台处理。 | 生成中断信号或事件信号(事件不触发中断,但可唤醒CPU)。 |
| NVIC(CPU中断控制器) | 部门负责人:前台将电话转接给对应负责人(如A7或M4核),由他们处理具体问题。 | 中断信号通过NVIC发送给CPU内核,触发中断服务函数(如执行按键处理程序)。 |
二、工作流程(以按键触发中断为例)
-
来电接入:
- 按键按下 → GPIO引脚(如PA0)电平变化(如从高变低)。
- EXTIMUX将PA0的来电转接到EXTI0线(类似前台将电话转接至“投诉专线”)。
-
规则检查:
- Trigger:检查是否满足接听条件(如“只接听下降沿来电”)。
- Masking:若未屏蔽EXTI0线(允许接听),则继续处理。
-
生成事件/中断:
- 如果是普通事件(如唤醒CPU但不触发中断),直接通过 EVG 发送唤醒信号。
- 如果是紧急中断(如按键需要立即响应),生成脉冲信号发送给 NVIC。
-
转接处理:
- NVIC根据优先级将中断转给指定CPU核(如M4核),CPU暂停当前任务,执行按键处理程序。
-
挂断恢复:
- 处理完成后,CPU继续原来的工作(如继续播放音乐)。
三、关键细节
-
Configurable vs Direct Event
- Configurable(可配置事件):需要前台手动设置规则(如仅允许PA0触发EXTI0),灵活性高。
- Direct Event(直接事件):某些特殊来电(如电源故障)无需配置,直接触发(类似火警电话自动转接)。
-
多核协作(A7 & M4)
- 前台(EXTI)可以同时连接多个负责人(CPU核),例如:
- A7核:处理复杂任务(如系统调度)。
- M4核:处理实时任务(如电机控制)。
- 不同中断可分配给不同核,实现分工协作(类似“技术问题找M4,系统问题找A7”)。
- 前台(EXTI)可以同时连接多个负责人(CPU核),例如:
-
脉冲生成(Pulse Generation)
- 前台将连续的来电(如长按按键)转换为单次脉冲信号,避免重复打扰负责人(类似“长按铃铛只响一次”)。
一句话总结
“EXTI模块像公司的全能前台:接线、筛选、转接一气呵成,确保按键、传感器等‘紧急来电’精准送达CPU负责人,既不漏接也不乱接!”
通过这张图,可以理解单片机如何高效响应外部实时事件,为嵌入式开发中的中断编程提供底层逻辑支持。
沿着红线走:
1. 设置EXTImux
选择哪些GPIO可以发出中断。 只有16个EXTI中断,从EXTI0~EXTI15;每个EXTIx中断只能从PAx、PBx、……中选择某个引脚,如下图所示: 
注意:从上图可知,EXTI0只能从PA0、……中选择一个,这也意味着PA0、……中只有一个引脚可以用于中断。这跟其他芯片不一样,很多芯片的任一GPIO引脚都可以同时用于中断。
通过EXTI_EXTICR1等寄存器来设置EXTIx的中断源是哪个GPIO引脚,入下图所示:
通俗解析:EXTI_EXTICR1寄存器——单片机的“电话转接台”
这张图讲的是如何用 EXTI_EXTICR1寄存器 把单片机的某个物理引脚(如按键、传感器)和对应的“中断热线”(EXTI中断线)连接起来。可以类比为 公司前台有一部总机电话,需要把不同部门的电话分机(引脚)转接到对应热线(中断线)上。
一、核心功能——“分机转接热线”
-
EXTI中断线的作用
- 单片机有多个“中断热线”(如EXTI0~EXTI15),每条热线专门处理一类紧急事件(如按键按下、温度超标)。
- 例如:EXTI0热线专门接听“PA0、PB0、PC0等引脚”的来电(类似公司总机把PA0分机的电话转接给“投诉专线”)。
-
寄存器的作用
- EXTI_EXTICR1寄存器就像前台的“转接台”,通过设置不同的“分机号”(寄存器的值),把某个引脚(如PB0)绑定到对应的热线(如EXTI0)。
- 操作示例:
// 将PB0引脚接到EXTI0热线(分机号0x01) EXTI_EXTICR1 |= (0x01 << 0); // 类似前台把PB0分机转接至EXTI0热线
二、寄存器的关键细节
| 字段 | 通俗比喻 | 技术解释 |
|---|---|---|
| 地址偏移0x060 | 转接台的位置:在单片机的内存地图中,这个寄存器的“门牌号”是基地址+0x060。 | 程序员通过这个地址找到并操作寄存器。 |
| Bits 7:0(EXTI0[7:0]) | 分机号拨盘:用8位二进制数选择引脚(如0x01对应PB0)。 | 设置不同的值,将不同GPIO端口的0号引脚(PA0/PB0/PC0等)连接到EXTI0中断线。 |
| 复位值0x00000000 | 默认状态:所有热线未连接任何分机(相当于总机电话线未插上)。 | 上电后必须手动配置,否则引脚电平变化不会触发中断。 |
| TZENO安全模式 | 权限锁:当启用安全模式(TZENO=1)时,只有“管理员”(安全程序)能操作转接台。 | 防止普通程序(非安全程序)乱改接线规则,导致系统异常。 |
三、操作步骤与示例
-
绑定PB0到EXTI0热线
// 步骤1:找到寄存器位置(基地址 + 0x060) uint32_t *EXTI_EXTICR1 = (uint32_t*)(0x40000000 + 0x060); // 步骤2:写入分机号0x01(PB0) *EXTI_EXTICR1 = (*EXTI_EXTICR1 & ~0xFF) | 0x01;- 效果:当PB0引脚电平变化(如按键按下)时,触发EXTI0中断,CPU立即响应。
-
安全模式下的权限控制
- TZENO=0(普通模式):任何程序都能修改接线规则(风险:恶意程序可能篡改)。
- TZENO=1(安全模式):只有“管理员程序”(安全代码)能操作寄存器,普通程序写入无效。
四、引脚编号对应表(EXTI0)
| 寄存器值 | 连接的引脚 | 生活场景类比 |
|---|---|---|
| 0x00 | PA0 | 把“PA0号工位”的电话转接到EXTI0投诉专线。 |
| 0x01 | PB0 | 把“PB0号工位”的电话转接到EXTI0投诉专线。 |
| 0x02 | PC0 | 把“PC0号工位”的电话转接到EXTI0投诉专线。 |
| ... | ... | ... |
| 0x05 | PF0 | 把“PF0号工位”的电话转接到EXTI0投诉专线。 |
一句话总结
“EXTI_EXTICR1像公司的电话转接台:程序员通过它把某个工位(引脚)的‘电话线’插到指定的热线(中断线)上,让CPU能第一时间响应按键、传感器等紧急事件!”
掌握这个寄存器的配置,就能让单片机精准识别外部设备的实时请求。
2. 设置Event Trigger
设置中断触发方式:
这张图讲的是单片机如何通过两个“开关面板”(寄存器)设置 外部中断的触发条件,可以理解为 给门铃设置“响铃规则”:是按下门铃时响(上升沿触发),还是松开时响(下降沿触发),或者两种都响。以下是具体解释:
一、核心功能——两种“响铃规则”
| 寄存器名称 | 生活比喻 | 技术功能 |
|---|---|---|
| EXTI_RTSR1 | 按下响铃开关:当电压从低变高(比如按键按下瞬间)时触发中断。 | 设置某个引脚(如PA0)的电平上升沿是否触发中断。 |
| EXTI_FTSR1 | 松开响铃开关:当电压从高变低(比如按键松开瞬间)时触发中断。 | 设置某个引脚(如PA0)的电平下降沿是否触发中断。 |
二、寄存器结构详解
| 寄存器字段 | 通俗比喻 | 技术细节 |
|---|---|---|
| 地址偏移 | 开关面板的位置: - EXTI_RTSR1在0x0000(门铃的“按下响铃”开关箱)。 - EXTI_FTSR1在0x0004(门铃的“松开响铃”开关箱)。 | 通过基地址+偏移量访问寄存器。 |
| Bits 16:0 | 16个门铃开关:每个开关对应一个引脚(如RT0对应PA0,RT1对应PA1)。 | - 0:关闭该引脚的响铃规则。 - 1:启用该引脚的响铃规则。 |
| Bits 31:17 | 封条区域:禁止触碰的开关(保持默认值0)。 | 随意改动可能导致门铃系统故障。 |
| 复位值0x00000000 | 默认状态:所有门铃开关关闭(按下或松开均不响铃)。 | 需要手动配置才能触发中断。 |
| TZEN权限控制 | 管理员锁:安全模式下,只有管理员(安全程序)能修改开关规则。 | 防止熊孩子(恶意程序)乱调门铃设置。 |
三、操作示例与场景
-
按键按下时触发中断(上升沿触发)
// 打开PA0引脚的“按下响铃”开关(RT0=1) EXTI_RTSR1 |= (1 << 0); // 类似设置门铃“按下时响” EXTI_FTSR1 &= ~(1 << 0); // 关闭PA0的“松开响铃”开关- 效果:只有按键按下瞬间触发中断,松开不触发。
-
按键松开时触发中断(下降沿触发)
EXTI_RTSR1 &= ~(1 << 0); // 关闭PA0的“按下响铃”开关 EXTI_FTSR1 |= (1 << 0); // 打开PA0的“松开响铃”开关- 效果:只有按键松开瞬间触发中断。
-
按下和松开都触发(双边沿触发)
EXTI_RTSR1 |= (1 << 0); // 按下时响 EXTI_FTSR1 |= (1 << 0); // 松开时也响- 应用场景:需要检测按键的完整动作(如长按计时)。
四、注意事项
-
避免毛刺干扰:
- 修改寄存器时,要确保操作原子化(如关闭中断后再修改),防止电平突变导致误触发。
- 类比:装修时调整门铃线路,先关掉电源,避免触电。
-
权限控制(TZEN):
- 普通模式:所有程序都能改门铃规则(风险:恶意程序让门铃乱响)。
- 安全模式:只有管理员能改规则,普通程序操作无效(类似密码锁保护)。
一句话总结
“EXTI_RTSR1和EXTI_FTSR1像门铃的响铃规则开关:设置按下响、松开响,或者都响,让CPU精准识别外设的‘敲门声’!”
通过这两个寄存器,开发者可以灵活控制外部事件何时触发中断,为实时响应外设动作提供基础。
3. 设置Masking
允许某个EXTI中断:
这张图讲的是单片机中一个名为 EXTI_IMR1 的“值班表”,它的核心功能是 决定哪些外部设备的“紧急呼叫”(中断)能叫醒正在休息的CPU。可以类比为 公司门卫的值班规则,规定哪些部门的员工有权在深夜拨打紧急电话给老板。
一、核心功能——“谁的电话能叫醒老板?”
-
门卫的值班表(EXTI_IMR1)
- 每个外部设备(如按键、传感器)对应一条“电话线”(中断线)。
- IM0~IM31位:每个开关(Bit位)对应一条中断线,控制是否允许这条线的中断叫醒CPU。
- Bit = 1:允许这条线的中断(门卫放行)。
- Bit = 0:禁止这条线的中断(门卫挂断)。
-
默认规则(复位值0xFFFE 0000)
- 复位后,大部分中断默认被屏蔽(类似门卫深夜默认不接普通电话)。
- 举例:复位值
0xFFFE 0000对应的二进制中,Bit16=0(屏蔽中断16),其他高位部分为1或0,需具体分析。
二、寄存器结构详解
| 寄存器部分 | 通俗比喻 | 技术细节 |
|---|---|---|
| 地址偏移0x080 | 值班表的位置:在单片机内存中的固定位置(如公司文件柜第0x080号抽屉)。 | 程序员通过这个地址找到并修改值班规则。 |
| IM0~IM31位 | 电话线开关:每个开关对应一个外部设备(如IM0对应PA0引脚的中断)。 | - 写1:允许这条线触发中断。 - 写0:禁止这条线触发中断。 |
| TZEN权限控制 | 值班表加密锁:当开启安全模式(TZEN=1)时,只有“安全程序”能修改值班规则。 | 防止普通程序(如恶意代码)乱改中断权限(如屏蔽火灾报警)。 |
| 复位值0xFFFE 0000 | 默认值班规则:大部分高位中断(如IM16~IM31)默认被屏蔽,低位中断部分开放。 | 需根据实际需求手动开启所需中断(如允许按键中断)。 |
三、操作示例与场景
-
允许按键中断(如开启IM0)
// 地址计算:基地址(如0x40000000) + 偏移0x080 uint32_t *EXTI_IMR1 = (uint32_t*)(0x40000000 + 0x080); // 将IM0位置1(允许PA0引脚中断) *EXTI_IMR1 |= (1 << 0);- 效果:当PA0引脚电平变化(如按键按下),CPU会被叫醒处理。
-
禁止传感器中断(如关闭IM5)
// 将IM5位清0(屏蔽传感器中断) *EXTI_IMR1 &= ~(1 << 5);- 效果:即使传感器报警,CPU也不会被中断(类似门卫不接仓库来电)。
四、关键细节
-
权限控制(TZEN)
- 普通模式:所有程序都能修改值班表(风险:恶意程序屏蔽重要中断)。
- 安全模式:只有受信任的程序(安全代码)能修改,普通程序操作无效。
-
复位值的意义
- 默认屏蔽部分中断是为了系统稳定,避免上电时无关外设频繁打扰CPU。
-
“直接事件”与“可配置事件”
- 直接事件:某些特殊中断(如电源故障)无需配置值班表,强制叫醒CPU(类似火警电话直通老板)。
- 可配置事件:需通过IMR1手动开启(如按键、传感器)。
一句话总结
“EXTI_IMR1像公司门卫的值班表:设置哪些‘电话线’(中断)能深夜叫醒CPU老板,哪些被静默挂断。复位值决定了默认规则,TZEN锁保护它不被篡改!”
掌握这个寄存器,就能精准控制单片机对外部事件的响应,平衡实时性与低功耗需求。
4. 查看中断状态、清中断
这张图讲的是单片机中两个重要的寄存器:EXTI_RPR1(上升沿挂起寄存器)和EXTI_FPR1(下降沿挂起寄存器)。它们的作用可以类比为 公司前台的“未接来电记录本”,专门记录哪些外部设备(如按键、传感器)的“来电”(中断请求)还没被处理。以下是通俗解释:
一、核心功能——“记录谁打过电话还没接”
-
上升沿挂起寄存器(EXTI_RPR1)
- 专门记录 电压从低跳变到高时触发的中断(比如按键按下的瞬间)。
- 例如:PA0引脚的电平突然变高(上升沿)→ EXTI_RPR1的第0位会被标记为1(类似前台在记录本上写“PA0来电未接”)。
-
下降沿挂起寄存器(EXTI_FPR1)
- 专门记录 电压从高跳变到低时触发的中断(比如按键松开的瞬间)。
- 例如:PA1引脚的电平突然变低(下降沿)→ EXTI_FPR1的第1位会被标记为1。
二、寄存器的“记录规则”
| 寄存器细节 | 生活比喻 | 技术解释 |
|---|---|---|
| 地址偏移0x00C和0x010 | 两个记录本的位置:在单片机的内存中,这两个寄存器像两个相邻的抽屉。 | 通过基地址+偏移量(如0x40000000 + 0x00C)访问寄存器。 |
| Bit0~Bit31 | 32条电话线记录:每个Bit对应一个引脚的中断线(如Bit0对应PA0,Bit1对应PA1)。 | - 0:该引脚没有未处理的“来电”。 - 1:该引脚有未处理的“来电”。 |
| 复位值0x00000000 | 默认状态:所有记录位初始为0(相当于刚上班时记录本是空的)。 | 系统启动后,所有中断事件需要重新检测。 |
| 操作方式 | 前台用铅笔记录:检测到中断时自动标记,处理后需手动擦除(写1清零)。 | 向对应Bit位写1,可以清除挂起状态(类似用橡皮擦掉记录)。 |
三、典型操作流程(以按键中断为例)
- 按键按下(上升沿触发)
- PA0电平从低变高 → EXTI_RPR1的Bit0被自动置1(前台记录“PA0来电”)。
- CPU处理中断
- 程序检测到EXTI_RPR1的Bit0=1 → 执行按键处理函数(如点亮LED)。
- 清除挂起标志
EXTI_RPR1 |= (1 << 0); // 向Bit0写1,清除挂起状态(类似擦除记录)- 若不手动清除,下次检测时仍会认为有未处理中断(类似前台重复提醒同一个来电)。
四、注意事项
-
为什么需要手动清除?
- 防止重复响应:如果中断处理函数执行期间再次触发相同中断,挂起位会再次置1,确保不漏事件。
- 类比:前台擦掉记录后,如果同一号码再次来电,会重新记录。
-
与触发寄存器的区别
- EXTI_RTSR/FTSR:决定“什么条件下算来电”(如按下或松开触发)。
- EXTI_RPR1/FPR1:记录“哪些来电还没处理”,需手动清理。
-
保留位的意义
- 寄存器中部分Bit位标注为“Reserved”(如高位Bit16~31),这些是预留位,禁止操作(类似记录本的空白页,不能乱写)。
一句话总结
“EXTI_RPR1和EXTI_FPR1像前台的未接来电记录本:自动记下按键、传感器等‘来电’(中断),处理完必须手动擦掉(写1清零),否则CPU会反复提醒你接电话!”
掌握这两个寄存器,就能精准管理中断事件,避免重复响应或遗漏关键请求。
1.3.3 GIC
ARM体系结构定义了通用中断控制器(GIC),该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。GIC提供了内存映射寄存器,可用于管理中断源和行为,以及(在多核系统中)用于将中断路由到各个CPU核。它使软件能够屏蔽,启用和禁用来自各个中断源的中断,以(在硬件中)对各个中断源进行优先级排序和生成软件触发中断。它还提供对TrustZone安全性扩展的支持。GIC接受系统级别中断的产生,并可以发信号通知给它所连接的每个内核,从而有可能导致IRQ或FIQ异常发生。
GIC比较复杂,下一个视频再详细讲解。
你可以把 ARM的通用中断控制器(GIC) 想象成 公司的前台接线员,它负责接收各种设备(如按键、传感器、定时器等)的“紧急电话”(中断请求),然后根据优先级、安全性等规则转接给对应的“部门负责人”(CPU核心)。以下是通俗易懂的解释:
一、GIC的核心功能
-
接电话:接收所有设备的“来电”
- 公司有很多部门(外设),每个部门有事时都会拨打电话(产生中断)。GIC像前台接线员一样,统一接听所有电话,避免每个部门直接打给老板(CPU核心)导致混乱。
- 例如:按键按下、传感器报警、定时器超时,都会通过硬件信号“拨号”给GIC。
-
分优先级:谁的“来电”更重要?
- 前台接线员会根据紧急程度排序电话。GIC同样会给中断分优先级,比如火警(高优先级)比打印机缺纸(低优先级)更早转接给老板处理。
-
转接规则:电话该转给哪个负责人?
- 如果是公司内部会议通知(软件中断),前台会直接转给指定员工(特定CPU核心);如果是客户投诉(公共外设中断),可能转给所有部门负责人(多核共享处理)。
二、GIC的“电话分机系统”
GIC将中断分为4种类型,类似不同的电话线路:
| 中断类型 | 通俗比喻 | 技术作用 |
|---|---|---|
| SGI | 内部电话:员工用座机拨分机号(软件触发),用于同事间沟通(多核协作)。 | 由软件生成,用于CPU核间通信(如通知其他核处理任务)。 |
| PPI | 私人专线:每个部门经理的私人电话(如财务部的报销专线),只能转接给对应负责人。 | 每个CPU核心独有的中断(如本地定时器),不与其他核共享。 |
| SPI | 公共热线:客户服务电话(如400热线),所有部门都能接听。 | 共享外设中断(如网卡、硬盘),可路由到任意CPU核心处理。 |
| LPI | VIP直拨:重要客户的专属电话(如PCIe设备的中断),直接通过消息传递。 | 基于消息的优先级中断,配置存储在内存表中(GICv3及以上支持)。 |
三、GIC的“接线流程”
以按键触发中断为例,流程如下:
-
来电接入:
- 按键按下 → 电平变化(如从高变低)→ GIC检测到“电话铃响”(中断信号)。
-
分类登记:
- GIC查看来电类型(SPI/PPI/SGI),记录在“未接来电本”(挂起寄存器)中。
-
优先级排序:
- 如果同时有多个电话(如火灾警报和打印机缺纸),GIC会优先处理更紧急的。
-
转接处理:
- 根据预设规则(如目标CPU核、安全权限),将电话转给指定负责人(CPU核心)。
- 例如:安全相关的中断(如指纹识别)只能转给“安保主管”(安全模式下的CPU核)。
-
挂断确认:
- CPU处理完中断后,必须告诉GIC“电话已接听”(清除挂起标志),否则GIC会反复提醒。
四、高级功能:安全与多核协作
-
安全模式(TrustZone)
- 普通电话(非安全中断)只能转接给普通员工,机密电话(安全中断)必须转给“保密部门”(安全模式CPU核)。
-
多核分工
- 前台接线员(GIC)可以同时连接多个老板(多核CPU),比如:
- 大核A7:处理复杂任务(如系统调度)。
- 小核M4:处理实时任务(如电机控制)。
- 前台接线员(GIC)可以同时连接多个老板(多核CPU),比如:
-
动态调整规则
- 软件可以随时修改GIC的配置,比如临时屏蔽某个部门的电话(禁用中断),或调整优先级(升/降级)。
一句话总结
“GIC就像公司的全能前台:统一接听所有设备的中断‘来电’,按优先级和规则精准转接给CPU核心,既保证紧急事件优先处理,又支持多核协作与安全隔离!”
通过这种机制,ARM系统既能高效响应实时事件,又能平衡性能与功耗,是嵌入式开发和多核处理的核心基础。
1.3.4 CPU
CPU的CPSR寄存器中有一位:I位,用来使能/禁止中断。 
可以使用以下汇编指令修改I位:
CPSIE I ; 清除I位,使能中断 CPSID I ; 设置I位,禁止中断
1.4 IMX6ULL的GPIO中断
IMX6ULL的GPIO中断在硬件上的框架,跟STM32MP157是类似的。 IMX6ULL中没有EXTI控制器,对GPIO的中断配置、控制,都在GPIO模块内部实现:
这张图讲的是ARM处理器中两个关键寄存器 CPSR(当前程序状态寄存器) 和 SPSR(保存的程序状态寄存器),它们就像CPU的“工作日志本”,记录当前运行状态和异常发生前的备份状态。以下是通俗解释:
一、CPSR和SPSR的作用
- CPSR:记录CPU现在在干什么(如当前模式、是否允许中断、最近一次计算的结果是正数还是负数等)。
- SPSR:异常发生时的“存档”(如接电话前保存当前状态,挂断后恢复现场)。
生活比喻:
- 你正在写作业(正常模式),突然接电话(触发异常)→ 接电话前,你把作业进度记在SPSR笔记本上;
- 接完电话后,根据SPSR笔记本恢复作业进度。
二、CPSR/SPSR的“笔记本字段”分解
图中将寄存器的32位分成多个功能区,以下是核心部分:
| 位字段 | 通俗解释 | 技术作用 |
|---|---|---|
| N、Z、C、V(31~28位) | 考试分数:记录最近一次计算的结果。例如: - N=1 → 结果是负数。 - Z=1 → 结果是零。 | 条件标志位,用于程序跳转(比如 if (a > 0) 的判断依据)。 |
| I、F、A(8~6位) | 免打扰开关:控制是否响应外部事件。 - I=1 → 屏蔽普通中断(如按键)。 - F=1 → 屏蔽快速中断(如硬件故障)。 | 中断屏蔽位,优先级:FIQ(快速中断)> IRQ(普通中断)。 |
| T(5位) | 工作语言: - T=1 → 用Thumb指令集(省电模式)。 - T=0 → 用ARM指令集(高性能模式)。 | 指令集状态切换,Thumb指令更紧凑(适合资源受限场景)。 |
| M[4:0](4~0位) | 当前模式: - 10000 → 用户模式(普通程序)。 - 10011 → 管理模式(系统内核)。 | 处理器模式控制,不同模式权限不同(如内核模式可操作硬件)。 |
| IT[7:2]和IT[1:0] | 待办事项清单:记录条件执行指令的后续步骤(如 if...else 的执行分支)。 | Thumb-2指令集中的条件执行标记(IT块)。 |
三、关键细节
-
为什么需要SPSR?
- 发生异常(如中断、系统调用)时,CPU自动将当前状态(CPSR)保存到SPSR,处理完异常后再从SPSR恢复。
- 类比:接电话前先保存作业进度(CPSR→SPSR),挂断后继续写作业(SPSR→CPSR)。
-
模式切换(M[4:0])的意义
- 用户模式:普通APP运行,不能直接操作硬件。
- 管理模式:操作系统内核运行,可访问所有资源。
- 其他模式:如中断模式(处理紧急事件)、未定义模式(处理未知指令)等,共7种模式。
-
条件标志位的应用
CMP R0, R1 ; 比较R0和R1的值 BGT label ; 如果R0 > R1(N=0且Z=0),跳转到label- CPU根据N、Z、C、V的值决定是否跳转,类似根据考试分数决定奖励。
四、总结:CPSR/SPSR的三大核心功能
- 记录状态:最近的计算结果、当前运行模式、是否允许中断。
- 控制行为:切换指令集(ARM/Thumb)、屏蔽中断、管理权限。
- 异常恢复:通过SPSR保存和恢复现场,确保程序不被异常打断破坏。
一句话总结
“CPSR是CPU的实时状态记录本,SPSR是异常时的存档备份——它们决定了CPU能做什么、怎么做,以及如何从‘突发事件’中恢复现场。”
理解这两个寄存器,是掌握ARM处理器状态管理和异常处理机制的关键!
1.4.1 GPIO控制器
1. 配置GPIO中断
每组GPIO中都有对应的GPIOx_ICR1、GPIOx_ICR2寄存器(interrupt configuration register )。 每个引脚都可以配置为中断引脚,并配置它的触发方式: 
这张图讲的是单片机如何通过 GPIOx_ICR1寄存器 给每个引脚(如按键、传感器)设置“门铃响的规则”,比如按一下响铃(按下触发)、松开才响(松开触发),或者两种都响。以下是通俗解释:
一、核心功能——给每个引脚设置“触发规则”
-
每个引脚对应一个“门铃开关”
- 单片机有多个引脚(如PA0、PB1等),每个引脚可以接一个外设(比如按键)。
- GPIOx_ICR1寄存器 就像一个“门铃设置面板”,每个引脚对应面板上的一个开关(2个比特位),用来定义这个引脚的触发规则。
-
四种触发模式
每2个比特位有4种组合,对应四种触发方式(以按键为例):比特值 触发规则 生活比喻 00 禁止中断 门铃坏了,按了也不响。 01 上升沿触发(按下瞬间) 按门铃的瞬间响铃。 10 下降沿触发(松开瞬间) 松开门铃的瞬间响铃。 11 双边沿触发(按下和松开都响) 无论是按下还是松开都响铃。
二、寄存器结构详解
-
地址计算
- 寄存器的位置 = 基地址(如0x40000000) + 通道偏移(类似门铃面板装在某个固定房间)。
- 程序员通过这个地址找到并操作寄存器。
-
比特位分组
- 寄存器共有32位,分成16组(每组2位),对应16个引脚:
- ICR0:控制第0号引脚(如PA0)的触发规则。
- ICR1:控制第1号引脚(如PA1)的触发规则。
- ...
- ICR15:控制第15号引脚(如PA15)的触发规则。
- 寄存器共有32位,分成16组(每组2位),对应16个引脚:
-
复位值全0
- 单片机上电时,所有引脚的中断触发规则默认是 00(禁止中断),需要手动配置才能生效。
三、操作示例(配置PA0按键按下触发中断)
-
确定引脚对应的比特位
- PA0是第0号引脚 → 对应 ICR0(比特位1:0)。
-
写入触发规则(01)
// 基地址 + 通道偏移(假设基地址为0x40000000) uint32_t *GPIOx_ICR1 = (uint32_t*)(0x40000000 + 通道偏移); // 将ICR0设置为01(上升沿触发) *GPIOx_ICR1 = (*GPIOx_ICR1 & ~0x03) | 0x01;- 效果:当PA0引脚电平从低变高(按键按下瞬间),触发中断通知CPU处理。
四、注意事项
-
先允许中断,再设置规则
- 除了配置GPIOx_ICR1,还需通过其他寄存器(如EXTI_IMR)允许该引脚的中断,否则规则无效。
-
防误触发
- 如果引脚信号抖动(如按键接触不良),双边沿触发(11)可能导致多次中断。此时需硬件消抖或软件过滤。
-
多引脚独立配置
- 不同引脚可以设置不同规则(如PA0用上升沿,PA1用下降沿),互不影响。
一句话总结
“GPIOx_ICR1像门铃的触发规则面板:每个引脚对应一个开关,设置按下响、松开响,或者都响,让CPU知道什么时候该处理按键、传感器等外设的‘敲门声’!”
掌握这个寄存器,就能精准控制外部事件何时触发中断,避免漏报或误报。
2. 使能GPIO中断
这张图讲的是单片机中的 GPIOx_IMR寄存器,它的核心功能是 决定哪些GPIO引脚的“门铃”(中断)能叫醒CPU。可以类比为 小区保安的值班表,规定哪些住户的门禁卡(引脚)能触发警报(中断),哪些直接忽略。
一、核心功能——“谁的门禁卡能触发警报?”
-
保安的值班表(GPIOx_IMR)
- 每个GPIO引脚(如PA0、PB1)对应一个“门禁卡”(中断线)。
- IMR的32个Bit位:每个开关(Bit位)对应一个引脚,控制是否允许这个引脚的中断触发。
- Bit = 1:允许中断(保安放行)。
- Bit = 0:禁止中断(保安挂断)。
-
默认规则(复位值全0)
- 单片机上电时,所有引脚的中断默认被屏蔽(类似保安默认不接任何住户的门禁警报)。
- 需要手动开启所需引脚的中断功能(比如允许PA0按键触发中断)。
二、寄存器的“值班表结构”
| 寄存器部分 | 通俗比喻 | 技术细节 |
|---|---|---|
| 地址:基地址+0x14 | 值班表的位置:在单片机的内存中,这个寄存器的“门牌号”是基地址+0x14。 | 程序员通过这个地址找到并修改值班规则(比如 0x40000000 + 0x14)。 |
| Bit0~Bit31 | 32个门禁卡开关:每个开关对应一个GPIO引脚(如Bit0对应PA0,Bit1对应PA1)。 | - 0:屏蔽该引脚的中断(保安忽略这个门禁卡的警报)。 - 1:允许该引脚的中断(保安立即上报警报)。 |
| 复位值全0 | 默认状态:所有引脚的中断被屏蔽(保安不响应任何门禁警报)。 | 必须手动开启需要的中断(比如允许PA0按键触发)。 |
三、操作示例与场景
-
允许PA0按键触发中断(Bit0=1)
// 找到寄存器地址(假设基地址是0x40000000) uint32_t *GPIOx_IMR = (uint32_t*)(0x40000000 + 0x14); // 将Bit0置1(允许PA0中断) *GPIOx_IMR |= (1 << 0);- 效果:当PA0引脚电平变化(如按键按下),CPU会立即响应。
-
禁止PB1传感器中断(Bit1=0)
// 将Bit1清0(屏蔽PB1中断) *GPIOx_IMR &= ~(1 << 1);- 效果:即使传感器报警,CPU也不会被打断(类似保安忽略PB1住户的门禁警报)。
四、注意事项
-
先设置触发条件,再开启中断
- 除了配置IMR寄存器,还需设置引脚触发方式(如上升沿/下降沿触发),否则中断可能无法正确触发。
-
复位后必须手动配置
- 默认所有中断被屏蔽,需根据需求逐个开启(比如只允许按键和温度传感器触发中断)。
-
防止误操作
- 修改寄存器时,建议关闭全局中断(类似让保安暂时休息),避免配置过程中被意外中断打断。
一句话总结
“GPIOx_IMR像小区的保安值班表:设置哪些住户的门禁卡(引脚)能触发警报(中断),哪些被静默忽略。复位后默认全关,需手动开启关键中断!”
掌握这个寄存器,就能精准控制单片机对外部事件的响应,避免无关干扰,提升系统稳定性。
3. 判断中断状态、清中断
1.4.2 GIC
ARM体系结构定义了通用中断控制器(GIC),该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。GIC提供了内存映射寄存器,可用于管理中断源和行为,以及(在多核系统中)用于将中断路由到各个CPU核。它使软件能够屏蔽,启用和禁用来自各个中断源的中断,以(在硬件中)对各个中断源进行优先级排序和生成软件触发中断。它还提供对TrustZone安全性扩展的支持。GIC接受系统级别中断的产生,并可以发信号通知给它所连接的每个内核,从而有可能导致IRQ或FIQ异常发生。
GIC比较复杂,下一个视频再详细讲解。
1.4.3 CPU
CPU的CPSR寄存器中有一位:I位,用来使能/禁止中断。 
这张图讲的是单片机中的 GPIOx_ISR寄存器,它的核心功能可以比喻为 家门口的“门铃状态灯”:当有人按门铃(触发中断)时,对应的灯会亮起,直到你处理完并手动关灯(清除标志)。以下是通俗解释:
一、核心功能——“谁的门铃被按了?”
-
状态指示灯(ISR寄存器)
- 每个GPIO引脚(如PA0、PB1)对应一个“门铃灯”(Bit位)。
- Bit = 1:这个门铃被按过且还没处理(灯亮)。
- Bit = 0:门铃未被按或已处理(灯灭)。
-
自动亮灯规则
- 当引脚的电平满足触发条件(如按键按下或松开)时,对应Bit位自动置1(灯亮),即使CPU没空处理,灯也会一直亮着(状态保持)。
-
手动关灯规则
- 处理完中断后,必须向对应Bit位写1才能关灯(类似按下灯的开关)。
二、寄存器的“门铃灯面板”结构
| 寄存器部分 | 通俗比喻 | 技术细节 |
|---|---|---|
| 地址:基地址+0x18 | 门铃灯面板的位置:在单片机的内存中固定(如小区物业的监控室第0x18号面板)。 | 程序员通过这个地址(如 0x40000000 + 0x18)查看和操作状态灯。 |
| Bit0~Bit31 | 32个门铃灯:每个灯对应一个GPIO引脚(如Bit0对应PA0按键,Bit1对应PB1传感器)。 | - 读操作:查看哪些灯亮(哪些中断未处理)。 - 写操作:向Bit位写1关灯(清除状态)。 |
| 复位值全0 | 默认状态:所有灯初始熄灭(上电时无中断触发)。 | 无需手动初始化,但需及时处理亮起的灯(防止漏掉中断)。 |
三、操作流程(以按键触发中断为例)
-
按键按下(触发条件满足)
- PA0电平变化(如从高到低)→ Bit0自动置1(PA0的灯亮起)。
-
CPU查看状态灯
// 读取寄存器状态 uint32_t status = *(uint32_t*)(0x40000000 + 0x18); if (status & (1 << 0)) { // 处理PA0按键中断(如点亮LED) } -
手动关灯(清除标志)
// 向Bit0写1,熄灭PA0的灯 *(uint32_t*)(0x40000000 + 0x18) = (1 << 0);
四、关键细节
-
为什么要手动关灯?
- 如果不清除标志,灯会一直亮着,CPU会认为这个中断未被处理(反复提醒)。
- 类比:快递员按门铃后,你收完快递但没关灯,物业会反复通知你有快递。
-
等待状态的作用
- 读操作需两个等待状态:查看灯的状态时,需要短暂确认(防止误判瞬态干扰)。
- 复位需一个等待状态:关灯操作需要一点时间生效(类似开关延迟)。
-
与IMR寄存器的区别
- GPIOx_IMR:决定哪些门铃能响(允许/禁止中断)。
- GPIOx_ISR:记录哪些门铃已经响过(中断触发状态)。
一句话总结
“GPIOx_ISR像家门口的32个门铃状态灯:有人按铃(中断触发)灯就亮,处理完必须手动关灯(写1清标志),否则系统会一直提醒你有快递(未处理中断)!”
掌握这个寄存器,就能精准追踪哪些外部事件触发了中断,避免遗漏或重复处理。
可以使用以下汇编指令修改I位:
CPSIE I ; 清除I位,使能中断 CPSID I ; 设置I位,禁止中断





09_GIC介绍与编程
10_实战_GPIO中断编程(IMX6ULL)
//字数太多了后一节讲

1551

被折叠的 条评论
为什么被折叠?



