ARM架构与编程学习(六)(异常与中断(概念与处理,处理,分析_保存现场,CPU Mode State与寄存器,ARM_Thumb指令集程序,未定义指令异常,SVC异常,中断硬件框架,GIC介绍))

01_异常与中断的概念引入与处理流程

1.1 使用生活实例引入中断

假设有个大房间里面有小房间,婴儿正在睡觉,他的妈妈在外面看书。 问:这个母亲怎么才能知道这个小孩醒?

  1. 过一会打开一次房门,看婴儿是否睡醒,然后接着看书

  2. 一直等到婴儿发出声音以后再过去查看,期间都在读书

第一种方法叫做查询方式

  • 优点:简单

  • 缺点: 累

如何写程序?

 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.pdfARM Cortex-M3与Cortex-M4权威指南.pdfPM0056.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. 向量表的工作流程​
  1. ​系统上电​​:

    • 处理器从内存地址0读取第一条条目__initial_sp,设置栈顶指针。
    • 读取第二条条目Reset_Handler,跳转到复位处理函数开始执行。
  2. ​发生中断​​:

    • 比如按下按键(外部中断),处理器自动查找向量表中对应的条目(如EXTI0_IRQHandler)。
    • 跳转到EXTI0_IRQHandler函数执行按键处理代码。
  3. ​严重错误​​:

    • 如果程序跑飞(如除零错误),触发HardFault_Handler,执行错误恢复或日志记录。

​5. 开发者需要做什么?​
  1. ​实现处理函数​​:

    • 代码中所有以_Handler结尾的符号(如Reset_HandlerNMI_Handler),都需要开发者编写具体实现。
    • 例如:
      void Reset_Handler(void) {
          // 初始化系统时钟、内存、外设...
          main();  // 最后跳转到main函数
      }
  2. ​处理未使用的条目​​:

    • 对于保留的条目(标记为0Reserved),需确保它们指向安全代码(如死循环):
      Default_Handler PROC
          B .  ; 无限循环
      ENDP

​6. 技术细节补充​
  • DCD指令​​:
    相当于在内存中预留一个4字节的空间,并写入后续表达式的值(函数地址)。
    例如:DCD Reset_Handler 将Reset_Handler函数的地址写入向量表。

  • EXPORT指令​​:
    将符号(如__Vectors)导出,使得链接器能正确引用这些地址。

  • ​地址对齐​​:
    ARM要求向量表必须按特定地址对齐(如256字节对齐),但此代码未显式对齐,需依赖链接脚本配置。


​7. 现实案例:STM32启动流程​
  1. 芯片上电,从地址0读取栈顶指针(__initial_sp)。
  2. 读取复位处理函数地址(Reset_Handler),跳转执行。
  3. Reset_Handler函数内初始化硬件,复制数据段,清零BSS段,最后调用main()
  4. 运行中发生中断(如定时器溢出),处理器查向量表,跳转至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. 异常处理全流程​
  1. ​触发中断​​:按键按下(触发EXTI0_IRQ)。
  2. ​硬件响应​​:
    • CPU暂停当前程序,切换到IRQ模式。
    • 硬件保存CPSR到SPSR_irq,PC跳转到向量表第6项(ldr pc, _irq)。
  3. ​软件处理​​:
    • _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. ​哪些物品朋友可以随意使用​​(调用者不保护的寄存器)
  2. ​哪些物品必须保持原样​​(被调用者必须保存的寄存器)
  3. ​哪些物品你自己会提前收好​​(调用者负责保存的寄存器)

​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后还要使用ab,但没有提前保存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, CPSRR0-R3, R12, LR, R4-R11(若使用)
​Cortex-M3/M4​PC, PSR, R0-R3, R12, LRR4-R11(若使用)

​终极总结​

角色责任操作口诀
​调用者​保护临时工具(R0-R3, R12, LR, PSR)“传参前收好背包”
​被调用者​保护传家宝(R4-R11)“用前锁柜,用完复原”
​硬件​自动保护路线图和状态(PC, PSR)“地震时自动锁保险箱”

​核心原则​​:

  • ​调用者的背包自己管​​:R0-R3传参后即可丢弃,重要数据提前存好。
  • ​被调用者的保险柜自己锁​​:若用R4-R11,必须保存恢复。
  • ​硬件是智能保险箱​​:自动守护最关键的路标(PC)和环境(PSR)。

1.4 对于M3/M4

参考资料:DDI0403E_B_armv7m_arm.pdfARM Cortex-M3与Cortex-M4权威指南.pdfPM0056.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为例)​
  1. ​切换窗口​​:硬件自动切换到IRQ窗口(使用专属柜员和保险箱)。
  2. ​保管凭证​​:将客户的环境信息(CPSR)存入专属保险箱(SPSR_irq)。
  3. ​手动存物​​:软件需保存客户的 ​​R0-R3, R12, LR​​。
  4. ​恢复现场​​:处理完成后手动归还物品和环境。
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内核)​
  • ​温度传感器触发中断​​:
    1. 硬件自动保存8个寄存器到栈。
    2. 执行温度读取函数。
    3. BX LR触发自动恢复,继续运行主程序。
​案例2:手机快速充电(A7内核)​
  • ​充电电流监控(FIQ处理)​​:
    1. 直接使用专属R8-R12计算电流。
    2. 无需保存寄存器,处理速度比普通中断快40%。

​终极口诀​

  • ​M系列​​:“自动存取,傻瓜式操作”
  • ​A系列​​:“VIP通道,高手专用提速”
  • ​核心差异​​:
    • ​M系列​​硬件全包,适合实时性要求高的简单系统。
    • ​A系列​​手动优化,适合需要极致性能的复杂场景。 ✅

  1. ​寄存器 = 工作台上的工具格​

    • R0-R15就像16个工具格(R15是指挥棒位置,记录正在执行的任务)
    • R13是工具箱(sp),R14是任务备忘录(lr),R15是当前工作进度(pc)
  2. ​不同工作模式 = 不同工种的工作服​

    • 用户模式(User):日常状态
    • 系统模式(Sys):7种特殊状态(FIQ快速抢修/IRQ普通维修/ABT事故处理等)
  3. ​工具的特点:​

    • 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 寄存器总图

一、核心设计理念

处理器像一栋​​多功能办公楼​​,不同工作模式对应不同部门,寄存器相当于各部门的专属办公桌:

  1. ​部门分类(工作模式)​

    • 常规部门:User(普通员工办公室)
    • 应急部门:FIQ(火警响应组)、IRQ(设备维修组)、Abort(事故处理组)
    • 管理办公室:System(总务处)、Hyp(虚拟化调度中心)、Monitor(安全监控室)
  2. ​办公桌规则​

    • 公共区域(R0-R12):所有部门共用
    • 部门专属储物柜(R8-R14):每个应急部门有自己的储物空间
    • 重要设备(CPSR/SPSR):全楼状态显示屏+部门独立备份屏

二、关键寄存器详解

1. 通用办公区(R0-R12)

所有模式共用,相当于公共会议室的白板,不同部门使用时需要自行记录/擦除内容

2. 应急部门装备(FIQ特权)
专属装备优势说明
R8_fiq-R12_fiq5个独立储物柜,处理紧急任务时无需腾空原有物品
SP_fiq/LR_fiq专用电话和记事本,记录当前任务进度

示例:当手机触控屏被触摸时,FIQ模式可立即使用专属寄存器处理,比普通中断快50%

3. 管理核心设备
  • ​PC寄存器​​:全楼唯一的总控台(记录当前执行位置)
  • ​CPSR/SPSR​​:双屏监控系统(当前状态+部门备份状态)
  • ​ELR_hyp​​:虚拟化部门的时光机(可回退错误操作)

三、模式切换流程图

[用户模式] → 遇到中断/异常 → 换工作服(切换模式)
      ↓
[自动领取装备] → 穿上FIQ/IRQ等专属马甲 → 使用专用寄存器
      ↓
[处理完毕] → 脱掉专属马甲 → 放回专用储物柜 → 返回原模式

四、安全设计亮点

  1. ​隔离保护​​:Hyp模式(虚拟化扩展)和Monitor模式(安全扩展)各自独立储物柜
  2. ​双重世界​​:安全状态(†标注)与非安全状态(+标注)物理隔离
  3. ​故障隔离​​: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的"成绩单"
位域缩写功能说明示例场景
31N负数标志(Negative)计算-5+3后N=1
30Z零标志(Zero)计算5-5后Z=1
29C进位标志(Carry)加法溢出时C=1
28V溢出标志(Overflow)符号位错误时V=1
27Q饱和标志(Saturation)SIMD运算饱和时触发
2️⃣ ​​控制开关区​​(23-8位)—— CPU的"操作台"
位域功能说明关键作用
24-27GE[3:0]SIMD指令比较结果存储
19-26IT[7:0]条件执行指令控制块(if-then)
9E字节序控制(0=小端,1=大端)
8/7A/I/F中断屏蔽开关:
A=中止中断屏蔽
I=普通中断屏蔽
F=快速中断屏蔽
5T指令集模式(0=ARM,1=Thumb)
3️⃣ ​​模式标识区​​(4-0位)—— CPU的"身份证"
位模式对应模式典型场景
M[4:0]0b10000User(用户模式)运行普通APP
0b10001FIQ(快速中断模式)处理触摸屏点击
0b10010IRQ(普通中断模式)处理键盘输入
0b10111Abort(异常终止模式)内存访问错误处理

三、设计精妙之处

  1. ​快速响应机制​​:
    当中断发生时,硬件自动将CPSR拷贝到SPSR,并立即修改CPSR中的模式位(M[4:0])和中断屏蔽位(I/F),实现​​微秒级切换​​。

  2. ​双重保险设计​​:
    ​RAZ/SBZP标注位​​(灰色区域)要求软件必须写0,避免未来架构升级时出现兼容性问题。

  3. ​状态继承规则​​:
    通过IT[7:0]位的设计,Thumb-2指令集能实现​​条件执行语句嵌套​​(类似if-else的层级判断)。


现实应用示例

当你在手机上玩游戏时:

  1. 触控中断触发FIQ模式 → CPSR的M[4:0]变为0b10001
  2. 系统自动屏蔽其他中断(F位=1)→ 保证触控响应优先
  3. 处理完毕后从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(饱和标志)会亮起​​,告诉系统:"本次运算有数据触顶了!"


四、现实世界中的应用

  1. ​手机拍照​
    HDR合成时,SIMD同时处理百万像素点的亮度/对比度,饱和标志防止过曝

  2. ​游戏特效​
    粒子特效(雨雪、火焰)的位置计算,128位SIMD寄存器同时操控数百个坐标

  3. ​视频解码​
    解码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,它的异常向量表基地址是可以修改的。

一、公司安全区域划分

处理器像一家设有严格安全等级的公司:

  1. ​监控中心(Secure Monitor模式)​

    • 最高安全级别,处理跨区域的安全事件
    • 有一本专用《监控中心应急预案》(Monitor Vector Table)
  2. ​保密办公室(Secure非Monitor模式)​

    • 处理机密业务问题
    • 存放《机密应急预案》(Secure Vector Table)
  3. ​普通办公区(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指定)


三、核心设计亮点

  1. ​物理隔离​
    保密区和普通区的应急预案存放在不同的保险柜(Secure/Non-secure地址空间),普通员工无法接触机密文件。

  2. ​快速切换​
    当发生跨区域事件时(如普通区发现可疑操作),监控中心能立即调取对应区域的应急预案,无需现场翻找。

  3. ​双重保险​
    统一地址0xFFFF0000相当于公司统一采购的标准保险柜,而VBAR自选地址则允许各部门使用定制保险柜。


现实场景示例

当手机同时运行银行APP(安全模式)和游戏(普通模式)时:

  1. 游戏突然崩溃 → 触发普通区应急预案(地址由Non-secure VBAR指定)
  2. 检测到可疑数据篡改 → 切换到监控中心应急预案(MVBAR指定地址)
  3. 银行APP密钥验证 → 调用保密区应急预案(Secure VBAR地址)
  4. 所有操作互不干扰,恶意程序无法读取安全区的应急流程

这种机制正是手机能同时保障支付安全和游戏流畅的底层秘密!

1.4.2 进入异常的处理流程(硬件)

我们来翻译一下: 发生异常时,我们的CPU会做什么事情

  1. 硬件确定要进入哪种异常模式

  2. LR寄存器被更新,它表示处理完异常后要返回到哪里,这个值可能需要修改。

  3. SPSR = 被中断时的CPSR

  4. 对于"Security Exceptions",……,本课程不涉及

  5. 更新异常模式下的CPSR:设置模式位、设置mask bit(屏蔽其他异常)、设置指令集状态位

  6. PC = 异常入口地址

  7. 从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️⃣ ​​开始实施抢救​​(继续执行)
  • 主刀医生按标准流程操作:
    • 心肺复苏(处理栈溢出错误)
    • 电击除颤(修复内存访问错误)
    • 完成后送回原诊室(恢复现场继续原程序)

三、关键技术亮点

  1. ​双病历系统​

    • 当前状态(CPSR)和急救前状态(SPSR)分开保存,保证抢救后能精确恢复现场
  2. ​智能分诊规则​

    • 普通医生(PL1模式)处理常见病症,遇到疑难杂症自动转交专家(PL2监控模式)
  3. ​隔离防护机制​

    • 普通区(Non-secure)与隔离区(Secure)使用不同急救手册(向量表),防止交叉感染

现实场景示例

当你在手机上同时运行游戏和微信时:

  1. 游戏突然崩溃(触发HardFault异常)→ 进入急救模式
  2. 系统自动保存游戏进度(保存PC/LR)、画面设置(保存CPSR)
  3. 调用错误处理程序(加载0x00000008地址的急救方案)
  4. 错误处理后,微信仍能正常视频通话(其他程序不受影响)

这种精密机制,正是手机能「边玩游戏边接电话」还不卡顿的底层秘密!

1.4.3 退出异常

ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf中,对异常的退出,描述得很复杂。但是很多情况,我们并不涉及。 所以我们参考S3C2440A_UserManual_Rev13.pdf,它描述得更清晰、简单。

从异常中退出,要做什么事情?

  1. 让LR减去某个值,然后赋值给PC(PC = 某个异常LR寄存器减去 offset) 减去什么值呢? 也就是我们怎么返回去继续执行原来的程序,根据下面这个表来取值:

    如果发生的是SWI可以把 R14_svc复制给PC 如果发生的是IRQ可以把R14_irq的值减去4赋值给PC

  2. 把CPSR的值恢复(CPSR 值等于 某一个一场模式下的SPSR)

  3. 清中断(如果是中断的话,对于其他异常不用设置)

1️⃣ ​​调整“返回地址”​

  • ​比喻​​:接电话前你正在切菜,电话挂断后需要决定是​​重新切刚才那一刀​​(回退一步)还是​​继续切下一刀​​。
  • ​技术解释​​:处理完异常时,处理器要根据异常类型调整​​程序计数器(PC)​​的值。不同异常发生时,处理器可能记录了不同的“返回地址”(比如是否要重新执行被中断的指令),所以需要​​减去一个偏移量​​来修正地址。

2️⃣ ​​恢复“工作状态”​

  • ​比喻​​:接电话时你关掉了油烟机(为了听清电话),挂断后要​​重新打开油烟机​​,恢复原来的厨房状态。
  • ​技术解释​​:处理异常时,处理器会保存原来的​​工作状态​​(比如运行模式、权限等级)到​​SPSR寄存器​​,处理结束后需要把SPSR的内容复制回​​CPSR寄存器​​,确保系统回到原来的状态。

3️⃣ ​​解除“免打扰模式”​

  • ​比喻​​:接电话时你开了“免打扰模式”(屏蔽其他来电),挂断后要​​关闭免打扰​​,否则会错过新电话。
  • ​技术解释​​:如果进入异常时​​关闭了中断响应​​(比如防止处理A异常时被B异常打断),处理结束后要​​重新开启中断​​,让系统能响应新的紧急事件。

一句话总结

​“挂断电话后,把菜板放回原位、恢复厨房设置、关掉免打扰,接着切菜!”​
这三个步骤保证了处理器处理完异常后,能精准回到原来的任务,同时恢复环境、解除限制,就像什么都没发生过一样。

1.4.4 确定异常返回地址

发生异常时,LR寄存器里保存的值是什么?

 LR = Preferred return address + offset

"Preferred return address"是什么?请看下图:

这张图其实是在讲:​​当电脑的CPU遇到“突发状况”(比如程序出错、接到紧急任务)时,它要怎么记住自己“从哪里来”,并安全地回到原来的工作​​。就像你突然被老板叫去开会,回来后需要知道该继续做哪件事、用哪个笔记本记录一样。


一、核心概念比喻
  1. ​异常类型(Exception)​
    ➡️ ​​突发状况的类型​​:比如“程序写错了代码”(未定义指令)、“系统需要处理紧急任务”(管理调用)、“网络突然断线”(数据中断)等等。不同状况需要不同的处理方式。

  2. ​首选返回地址(Preferred return address)​
    ➡️ ​​回来继续工作的位置​​:就像你被电话打断后,要决定是​​重做刚才那一步​​(回到出错指令),还是​​跳过继续下一步​​(执行下一条指令)。

  3. ​进入的模式(Taken to a mode at)​
    ➡️ ​​切换权限或安全状态​​:比如普通用户模式(PL1)、管理员模式(PL2),或者区分“机密环境”(Secure)和“普通环境”(Non-secure)。就像你处理公司机密文件时要进加密办公室,处理完再回到普通工位。


二、具体例子解释

​异常类型​​通俗解释​​返回地址​​进入模式​
​未定义指令​程序写了CPU不认识的代码(比如打错字)回到错误指令的位置,重新检查哪里出问题管理员模式(PL1/PL2)
​管理调用​系统需要执行高权限操作(比如打开摄像头)跳过当前指令,直接执行下一条管理员模式(PL1/PL2)
​安全监控调用​处理涉及机密数据的任务执行完机密任务后,继续下一条指令仅限机密环境(Secure)
​虚拟中断​虚拟机遇到问题(比如模拟的网络断线)直接下一步,让虚拟机自己处理普通用户模式(仅限非机密环境)

三、关键注释总结

  1. ​安全与权限​

    • ​Secure vs Non-secure​​:就像公司有“机密实验室”和“普通办公室”,某些异常只能在机密环境下处理(例如指纹解锁失败)。
    • ​PL2权限​​:类似“超级管理员权限”,但只能在普通办公室(Non-secure)使用,不能带进机密实验室。
  2. ​异常处理规则​
    ➡️ ​​原则​​:处理完异常后,CPU必须​​回到正确的位置​​、​​切换回原来的权限​​,并且​​不能泄露机密信息​​(比如在普通环境下不暴露Secure状态的数据)。


一句话总结

​“电脑遇到突发状况时,就像你突然被叫去灭火,处理完要记得:回哪继续干活?用哪个权限?是否涉及机密?”​
这张表就是CPU的“应急手册”,确保它处理完异常后能精准恢复现场,既不卡死也不泄密。

offset是什么?请看下图:

这张图讲的是​​ARM处理器在PL1特权模式下处理异常时的“返回地址修正规则”​​,可以比喻成:​​电脑突然遇到紧急任务(比如程序崩溃、权限请求)时,它要如何准确记住“回来继续干活的位置”​​。


一、核心机制

  1. ​异常发生时,处理器会保存“返回地址”到LR寄存器​

    • ​LR = 理想返回地址 + 偏移量​
    • ​偏移量​​的作用:根据​​异常类型​​和​​当时的指令集状态​​(ARM/Thumb/Jazelle)修正返回地址,确保处理器回来时能正确继续执行。

    ​通俗比喻​​:
    你正在看书时被电话打断,挂断后要翻回某一页继续读。但不同电话(异常类型)和不同阅读姿势(指令集状态)会导致翻页的位置需要微调(偏移量)。

    • 例如:如果是“书上有错别字”(未定义指令),可能需要回退几页重新读;如果是“快递到了”(中断请求),可能直接翻到下一页。

二、偏移量的作用解析

​异常类型​​典型偏移量(ARM模式)​​通俗解释​
​未定义指令​+4遇到不认识的指令(如代码写错),需回到错误指令的位置重新处理(ARM指令长度4字节)。
​管理调用​无偏移(None)系统调用(如申请权限)后直接执行下一条指令,无需回退。
​数据中止​+8访问内存出错(如地址无效),处理完异常后跳过当前指令,继续执行后续指令。
​中断请求(IRQ)​+4处理完中断(如收到网络数据)后,回到被中断指令的下一条继续执行。

三、指令集状态的影响

  1. ​ARM模式(32位指令)​

    • 大部分异常偏移量是4或8(按4字节对齐)。
      ​举例​​:未定义指令异常偏移+4,相当于回退到当前指令的起始位置重新执行。
  2. ​Thumb模式(16位指令)​

    • 偏移量减半(如未定义指令偏移+2,因为Thumb指令长度2字节)。
      ​举例​​:同样处理未定义指令,Thumb模式下回退2字节即可定位错误指令。
  3. ​Jazelle模式(特殊指令集)​

    • 偏移量规则更复杂,需参考具体文档(如注释中的“-b”或“-c”)。
      ​注意​​:Jazelle状态现已逐步淘汰,实际应用中较少涉及。

四、关键总结

  1. ​为什么需要偏移量?​

    • 不同异常的处理逻辑不同:有的需要​​重新执行当前指令​​(如未定义指令),有的需要​​跳过当前指令​​(如数据中止)。偏移量帮助处理器精准定位返回位置。
  2. ​为什么分不同指令集状态?​

    • ARM和Thumb指令长度不同(4字节 vs 2字节),修正返回地址时需按指令长度调整偏移量。
  3. ​PL1模式是什么?​

    • PL1是ARM处理器的特权模式(如操作系统内核态),用于处理高权限操作(如系统调用、内存管理)。异常处理时需进入PL1模式保存现场。

一句话总结

​“电脑处理异常就像你接电话:挂断后要翻回书里正确的位置继续读。不同电话类型(异常)和读书姿势(指令集)决定了翻几页(偏移量)!”​
这张表就是处理器的“电话接听手册”,确保它处理完紧急任务后能精准回到原来的工作流程。

从异常中返回时,LR可能需要调整,再赋给PC。 ARM9的手册讲得比较清楚,返回指令如下:

通俗解析:​​电脑遇到突发状况时,如何精准“回到案发现场”?​

这张图是​​ARM处理器处理各种异常(如程序崩溃、系统调用、硬件中断等)的“返回地址操作手册”​​。可以理解为:电脑遇到紧急事件后,必须记录“从哪里被中断”,处理完还要精准回到原来的位置继续工作。以下用生活场景类比:


一、核心概念
  1. ​R14寄存器(LR:Link Register)​
    ➡️ ​​“书签”​​:当电脑被异常打断时,R14会记录一个“书签位置”,告诉CPU处理完异常后应该翻到哪一页继续执行程序。

    • 不同异常类型对应不同的“书签夹”(如 R14_svcR14_abt),避免书签混乱。
  2. ​PC(Program Counter)​
    ➡️ ​​“当前读到的页码”​​:PC指向下一条要执行的指令地址。异常发生时,PC的地址会被保存到R14,但需要根据异常类型调整偏移量(比如回退或跳过当前指令)。


二、表格逐行解析(以生活场景类比)

​异常类型​​返回指令​​ARM模式书签位置​​Thumb模式书签位置​​通俗解释​
​BL​MOV PC, R14PC + 4PC + 4正常函数调用,处理完直接翻到下一页(ARM和Thumb都+4)。
​SWI​MOVS PC, R14_svcPC + 4PC + 2系统调用(如请求权限),ARM模式翻下一页(4字节),Thumb模式翻下一页的一半(2字节)。
​UDEF​MOVS PC, R14_undPC + 4PC + 2遇到不认识的代码(比如写错指令),处理完需回到错误指令位置重新执行。
​FIQ/IRQ​SUBS PC, R14_fiq, #4PC + 4PC + 4处理硬件中断(如收到网络数据),需回退4字节,重新执行被中断的指令。
​PABT​SUBS PC, R14_abt, #4PC + 4PC + 4预取指令失败(比如内存访问错误),回退4字节重新取指令。
​DABT​SUBS PC, R14_abt, #8PC + 8PC + 8数据访问失败(如无效地址),需跳过更多步骤(回退8字节),避免重复错误。
​RESET​NA--电脑重启,没有“书签”可言,直接从头开始运行。

三、关键细节
  1. ​为什么ARM和Thumb模式的书签位置不同?​

    • ​ARM指令长度4字节​​:好比一本书每页有4行字,翻页要+4。
    • ​Thumb指令长度2字节​​:每页只有2行字,翻页只需+2。
    • 示例SWI异常时,Thumb模式的书签位置是PC+2,因为下一条指令距离更近。
  2. ​返回指令中的 SUBS PC, R14_xx, #N 是什么操作?​
    ➡️ ​​“书签修正”​​:从R14保存的书签位置减去N(如4或8),让PC指向正确的返回地址。

    • 例子:FIQ异常用 SUBS PC, R14_fiq, #4,表示“书签位置减4字节”再赋值给PC,确保重新执行被中断的指令。
  3. ​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. ​​在汇编中动态切换状态​

使用BXBLX指令,根据目标地址的​​最低位​​(bit0)切换模式:

BX R0   // 如果R0的bit0=1 → 切换到Thumb模式;bit0=0 → 切回ARM模式
BLX R0  // 切换状态并跳转(类似打电话时先切换语言再说话)

​原理​​:

  • ARM芯片通过目标地址的​​最低位​​判断模式:
    • ​bit0=0​​:ARM模式(地址对齐到4字节,如0x8000)。
    • ​bit0=1​​:Thumb模式(地址对齐到2字节,如0x8001)。

​示例​​:

LDR PC, =main  // 加载main函数的地址到PC(程序计数器)

假设main函数用Thumb编译,其地址会是0xC020051B(bit0=1),执行时会自动切换到Thumb模式。


三、为什么需要手动切换?
  1. ​节省内存​​:Thumb指令占空间更小(适合内存紧张的嵌入式设备)。
  2. ​性能平衡​​:Thumb代码密度高,但复杂操作需切回ARM模式(如浮点运算)。
  3. ​兼容性​​:混合编程时,C和汇编可能用不同指令集,需统一状态。

四、实际应用场景
  1. ​嵌入式开发​​:用Thumb模式编译大部分代码节省空间,关键性能代码切回ARM模式。
  2. ​异常处理​​:ARM芯片默认在异常(如中断)时切到ARM模式,处理完需手动切回Thumb。
  3. ​调用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“应急手册”放在哪里。
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️⃣ ​​实际执行过程​
  1. _fiq 标签的定义​​:
    通常在汇编中通过 .word 或 DCD 指令定义,例如:

    _fiq: .word FIQ_Handler  ; 将 FIQ_Handler 的地址存入 _fiq 对应的内存
    • ​作用​​:在内存中预留一个位置(如 0x0000001C,FIQ 异常向量地址),存储 FIQ 处理函数的实际地址。
  2. ​指令执行时​​:

    • CPU 根据 _fiq 标签的地址(如 0x0000001C),从内存中读取存储的值(如 0x8000FF00,即 FIQ_Handler 的地址)。
    • 将该值赋值给 PC,程序跳转到 FIQ_Handler 执行。

​关键点​​:

  • ​地址无关性​​:ldr pc, _fiq 是 ​​绝对地址跳转​​,跳转目标由链接器在编译时确定,因此需确保代码已加载到正确内存位置。
  • ​异常向量表​​:FIQ 异常向量位于 0x0000001C,该指令必须放置在此地址,确保 CPU 触发 FIQ 时能自动执行此跳转。

三、与 B 指令的区别

在异常向量表中,​​复位异常(Reset)​​ 通常使用 B reset,而 ​​其他异常(如 FIQ)​​ 使用 ldr pc, _xx,原因如下:

  1. ​复位异常的特殊性​​:
    • 复位时可能尚未初始化内存(如 MMU 未启用),B 指令的 ​​相对跳转​​ 更可靠(不依赖绝对地址)。
  2. ​其他异常的需求​​:
    • FIQ 处理函数可能位于较远地址(如链接脚本指定的高端内存),ldr pc, _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)

五、应用场景

  1. ​嵌入式系统启动​​:
    • 在 Bootloader 的异常向量表中,通过 ldr pc, _fiq 指定 FIQ 处理函数,确保中断触发时能正确响应。
  2. ​动态重定位​​:
    • 若代码从 Flash 复制到 RAM 运行,需在重定位后使用 ldr pc, _fiq,使跳转地址指向 RAM 中的处理函数。

一句话总结

​“ldr pc, _fiq 是 CPU 处理快速中断的‘导航指令’:根据内存中预设的地址(_fiq),跳转到中断服务程序,确保紧急任务优先执行。”​
理解其原理,能帮助开发者设计可靠的中断处理机制,尤其在内存管理和启动流程中至关重要。

通俗解析:​​LDR是什么?为什么说它像“快递员”和“代购”?​

一、核心概念

​LDR(Load Register)​​ 是 ARM 架构中的一条指令,主要作用是​​从内存中搬运数据到寄存器​​,相当于一个“数据快递员”或“内存代购”。它的核心功能可以拆解为两种场景:

  1. ​作为指令​​:直接搬运内存中的数据到寄存器(像快递员取快递)。
  2. ​作为伪指令​​:直接给寄存器赋值大数字或地址(像代购直接送货上门)。

二、LDR的两种身份
1. ​​当“快递员”:搬运内存数据​
  • ​功能​​:从内存地址读取数据(如数字、代码、变量)到寄存器。
  • ​语法​​:LDR R0, [R1]
    • 含义:从内存地址 R1 处读取数据,存入 R0 寄存器。
  • ​变种​​:
    • ​LDRB​​:搬运一个字节(如读一个字符)。
    • ​LDRH​​:搬运半个字(如读一个短整数)。

​生活比喻​​:
假设寄存器是你的手,内存是仓库,LDR 就像从仓库货架(内存地址)上取一个包裹(数据)放到手里(寄存器)。

2. ​​当“代购”:直接赋值大数字​
  • ​功能​​:给寄存器赋一个很大的立即数(如 0x12345678)或地址。
  • ​语法​​:LDR R0, =0x12345678
    • 含义:直接把 0x12345678 这个值存入 R0,编译器会帮你处理内存分配。

​生活比喻​​:
如果你想买一个超大包裹(比如 32 位数字),但快递员(MOV 指令)只能带小包裹(12 位以内),LDR 伪指令就像代购,先帮你把包裹存到附近的寄存点(临时内存),再取回来给你。


三、为什么需要 LDR?
  1. ​解决 MOV 的局限​​:
    • MOV 只能赋值小数字(如 0xFF),而 LDR 伪指令可以赋值任意大数字(如 0xABCD1234)。
  2. ​灵活访问内存​​:
    • 支持多种寻址方式,如基址+偏移(LDR R0, [R1, #8])、自动更新地址(LDR R0, [R1], #8)等。
  3. ​跳转功能​​:
    • 当 PC(程序计数器)作为目标寄存器时,LDR 可以实现绝对地址跳转(如 LDR PC, =main)。

四、常见误区
  1. ​带不带等号的区别​​:
    • LDR R0, [R1]:搬运内存数据(快递员模式)。
    • LDR R0, =0x123:直接赋值(代购模式)。
  2. ​地址对齐问题​​:
    • ARM 要求某些指令的地址必须是 4 字节对齐(如字操作),否则会触发异常。
  3. ​跳转范围限制​​:
    • 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中,各类文件操作的函数openreadwrite,它的实质都是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) ​​硬件自动操作(四大步三小步)​
  1. ​保存现场​​:
    • 将当前模式(如用户模式)的CPSR保存到SPSR_svc(SVC模式的备份状态寄存器)。
    • 将下一条指令地址(PC-4)保存到LR_svc(SVC模式的返回地址寄存器)。
  2. ​切换模式​​:
    • 切换到SVC模式(特权模式),关闭IRQ中断(防止干扰)。
  3. ​跳转入口​​:
    • CPU从向量表0x00000008处读取ldr pc, _software_interrupt,跳转到SVC处理函数。
(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数据手册.pdfARM Cortex-M3与Cortex-M4权威指南.pdfPM0056.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处理(如“先接客户电话,再处理打印机问题”)。

二、中断处理流程(以按键触发为例)
  1. ​来电触发​​:

    • 按键按下 → 输入线(如线3)电压从高变低(下降沿)。
    • ​边沿检测电路​​识别到变化,生成脉冲信号。
  2. ​登记来电​​:

    • 若​​中断屏蔽寄存器​​未屏蔽线3 → 脉冲信号写入​​待处理请求寄存器​​(标记线3有请求)。
  3. ​转接优先级​​:

    • ​NVIC​​检查所有待处理请求,根据优先级决定先处理哪个(如线3优先级高于线5)。
  4. ​老板接电话​​:

    • CPU暂停当前任务,跳转到中断处理函数(如执行按键扫描程序)。
  5. ​挂断恢复​​:

    • 处理完成后,清除待处理请求,CPU继续原来的工作。

三、关键细节
  1. ​为什么需要“上升沿/下降沿触发”?​

    • ​避免误触发​​:比如按键抖动会产生多次电平变化,只有指定边沿(如下降沿)才会触发中断。
    • ​应用场景​​:
      • 上升沿触发:适合检测信号从低到高的跳变(如温度超过阈值)。
      • 下降沿触发:适合检测信号从高到低的跳变(如按键松开)。
  2. ​软件中断事件的作用​

    • ​模拟外部中断​​:测试时无需真实外设,软件写寄存器即可触发中断。
    • ​紧急任务调度​​:操作系统可主动触发中断,强制CPU处理高优先级任务。
  3. ​“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)的电话转接后勤部(低优先级)。

四、总结流程
  1. ​允许谁打电话​​ → ​​EXTI_IMR 寄存器​​(白名单过滤)。
  2. ​电话响铃的条件​​ → ​​EXTI_RTSR/EXTI_FTSR 寄存器​​(边沿触发规则)。
  3. ​电话如何转接​​ → ​​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)​​内部紧急呼叫按钮​软件直接触发中断(如系统调用)。例:程序主动按下按钮,强制前台转接电话给老板。

二、关键操作示例
  1. ​开启按键中断(允许接电话)​

    NVIC->ISER[0] |= (1 << 3);  // 开启中断编号3(按键中断)
    • ​地址​​:0xE000E100(ISER[0]对应中断0~31)。
    • ​效果​​:允许按键触发中断,类似将按键加入白名单。
  2. ​设置火灾报警最高优先级​

    NVIC->IP[5] = 0x00;  // 中断编号5(火灾报警)优先级设为0(最高)
    NVIC->IP[6] = 0x80;  // 中断编号6(打印机缺纸)优先级设为8(较低)
    • ​地址​​:0xE000E400~0xE000E4EF(每个中断占1字节)。
    • ​规则​​:优先级数值越小越优先(0 > 8)。
  3. ​检查当前处理的中断​

    if (NVIC->IABR[0] & (1 << 3)) { 
        printf("正在处理按键中断!"); 
    }
    • ​地址​​:0xE000E300(IABR[0]对应中断0~31)。
    • ​特性​​:只读寄存器,实时显示活跃中断。

三、实际应用场景
  1. ​按键中断处理流程​

    • ​步骤1​​:通过 ISER 开启按键中断。
    • ​步骤2​​:通过 IP 设置按键优先级。
    • ​步骤3​​:按键按下触发中断 → CPU处理中断函数。
    • ​步骤4​​:处理完成后,通过 ICPR 清除挂起状态。
  2. ​软件调试技巧​

    • ​模拟中断​​:通过 ISPR 手动触发中断,测试处理逻辑。
    • ​强制中断​​:通过 STIR 触发系统调用(如Linux的 svc 指令)。

四、注意事项
  1. ​寄存器分组​​:

    • 多个寄存器(如ISER[0]~ISER[7])管理256个中断,每组32位控制32个中断。
    • 例:中断编号35 → 属于 ISER[1] 的第3位(35=32×1 + 3)。
  2. ​优先级抢占规则​​:

    • 高优先级中断可打断低优先级中断(类似VIP客户插队)。
    • 相同优先级时,按硬件中断编号顺序处理。
  3. ​清除挂起状态​​:

    • 处理完中断后必须清除挂起位(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个中断)。

二、操作规则与示例
  1. ​允许火灾报警(中断0)触发​

    // 地址0xE000E100对应ISER[0],将Bit0置1(二进制:00000001)
    NVIC->ISER[0] |= (1 << 0);  // 允许中断0(火灾报警)
    • ​效果​​:当火灾传感器触发时,CPU会立即响应。
  2. ​禁止打印机缺纸中断(中断31)​

    // 地址0xE000E180对应ICER[0],将Bit31置1
    NVIC->ICER[0] |= (1 << 31);  // 禁止中断31(打印机缺纸)
    • ​效果​​:即使打印机缺纸,也不会打扰CPU处理其他任务。
  3. ​查看当前允许的中断​

    if (NVIC->ISER[0] & (1 << 3)) {
        printf("3号中断(如门禁报警)已允许!");
    }
    • ​原理​​:读取寄存器的值,判断对应Bit位是否为1。

三、技术细节与注意事项
  1. ​地址范围与分组逻辑​

    • ​ISER组​​:地址 0xE000E100~0xE000E11C,每组寄存器管理32个中断。
      • ISER[0]:管理中断0~31
      • ISER[1]:管理中断32~63
      • ISER[2]:管理中断64~95
    • ​ICER组​​:地址 0xE000E180~0xE000E19C,分组规则与ISER相同。
  2. ​读写规则​

    • ​写1生效​​:向ISER的Bit位写1 → 允许中断;向ICER的Bit位写1 → 禁止中断。
    • ​写0无效​​:向任意寄存器写0均不改变状态。
    • ​读出值​​:直接反映当前中断的使能状态(1=允许,0=禁止)。
  3. ​复位值​

    • 所有寄存器复位值均为0 → ​​默认禁止所有中断​​,需手动开启所需中断。

四、实际应用场景
  1. ​系统初始化​​:
    • 启动时仅开启关键中断(如系统定时器),其他中断按需动态开启。
  2. ​节能模式​​:
    • 休眠前通过ICER关闭非必要中断(如按键检测),降低功耗。
  3. ​调试阶段​​:
    • 逐步开启中断,定位故障源(如仅开启串口中断,排除其他干扰)。

一句话总结

​“这张图是单片机中断系统的总控开关面板: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)​​开关型号​​:不同版本处理器的寄存器功能可能微调,需参考对应手册。确保代码与硬件版本兼容,避免功能异常。

三、适用场景与注意事项
  1. ​什么时候用PRIMASK?​

    • ​执行关键代码​​:如操作系统的任务切换、内存管理,需避免被中断打断。
    • ​实时性要求极高​​:如电机控制中,必须确保一段代码完整执行,否则可能导致硬件故障。
  2. ​注意事项​

    • ​及时关闭​​:免打扰模式结束后需恢复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,确保关键事件不被忽略。

三、适用场景与注意事项
  1. ​什么时候用FAULTMASK?​

    • ​处理致命错误​​:如内存访问越界、硬件总线错误,需暂停其他任务专心修复。
    • ​系统崩溃前自救​​:在触发硬错误(HardFault)前,强制进入隔离状态,尝试保存关键数据。
  2. ​注意事项​

    • ​慎用!​​:开启后系统几乎“冻结”,只有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客户”​​:


一、核心功能——如何“过滤”中断?
  1. ​设置门槛值​​:

    • ​BASEPRI = 0​​ → 所有中断都能触发(前台接待所有访客)。
    • ​BASEPRI = 非零值(如0xA0)​​ → 优先级≤0xA0的中断被屏蔽(前台只接待优先级高于0xA0的VIP客户)。
  2. ​优先级规则​​:

    • ​数值越小,优先级越高​​(如0x00 > 0x80)。
    • ​举例​​:
      • 火灾报警(优先级0x10) > 打印机缺纸(优先级0xA0)。
      • 若设置BASEPRI=0xA0 → 只处理火灾报警,忽略打印机缺纸。

二、寄存器结构解析(图7和表9)
​寄存器结构​​生活比喻​​技术细节​
​Bits 7:4(优先级掩码位)​​VIP客户门槛值​​:用4位二进制数定义优先级门槛(范围0x00~0xF0)。- 0x00:关闭过滤,允许所有中断。
- 非零值:仅允许优先级更高的中断触发。
​Bits 31:8 和 3:0​​备用开关​​:禁止随意操作(可能导致系统异常)。必须保持默认值(全0),否则可能引发未知错误。

三、适用场景与操作示例
  1. ​何时使用?​

    • ​关键任务保护​​:执行重要代码时屏蔽低优先级中断(如电机控制时忽略按键检测)。
    • ​动态调整优先级​​:在不同场景下灵活调整门槛值(如开机时允许更多中断,运行中收紧限制)。
  2. ​操作示例​

    // 设置优先级门槛为0xA0(二进制1010 0000)
    __set_BASEPRI(0xA0);  // 优先级≤0xA0的中断全部屏蔽
    // 执行关键代码(如传感器校准)
    __set_BASEPRI(0x00);  // 恢复允许所有中断

四、注意事项
  1. ​数值越大,优先级越低​​:

    • 设置 BASEPRI=0xA0 时,实际屏蔽的是优先级值≥0xA0的中断(数值越大优先级越低)。
    • 容易误解点:这里的“优先级”是硬件层面的逻辑值,与应用层定义的优先级可能相反。
  2. ​与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内核,触发中断服务函数(如执行按键处理程序)。

二、​​工作流程(以按键触发中断为例)​
  1. ​来电接入​​:

    • 按键按下 → GPIO引脚(如PA0)电平变化(如从高变低)。
    • ​EXTIMUX​​将PA0的来电转接到EXTI0线(类似前台将电话转接至“投诉专线”)。
  2. ​规则检查​​:

    • ​Trigger​​:检查是否满足接听条件(如“只接听下降沿来电”)。
    • ​Masking​​:若未屏蔽EXTI0线(允许接听),则继续处理。
  3. ​生成事件/中断​​:

    • 如果是普通事件(如唤醒CPU但不触发中断),直接通过 ​​EVG​​ 发送唤醒信号。
    • 如果是紧急中断(如按键需要立即响应),生成脉冲信号发送给 ​​NVIC​​。
  4. ​转接处理​​:

    • NVIC根据优先级将中断转给指定CPU核(如M4核),CPU暂停当前任务,执行按键处理程序。
  5. ​挂断恢复​​:

    • 处理完成后,CPU继续原来的工作(如继续播放音乐)。

三、​​关键细节​
  1. ​Configurable vs Direct Event​

    • ​Configurable(可配置事件)​​:需要前台手动设置规则(如仅允许PA0触发EXTI0),灵活性高。
    • ​Direct Event(直接事件)​​:某些特殊来电(如电源故障)无需配置,直接触发(类似火警电话自动转接)。
  2. ​多核协作(A7 & M4)​

    • 前台(EXTI)可以同时连接多个负责人(CPU核),例如:
      • ​A7核​​:处理复杂任务(如系统调度)。
      • ​M4核​​:处理实时任务(如电机控制)。
    • 不同中断可分配给不同核,实现分工协作(类似“技术问题找M4,系统问题找A7”)。
  3. ​脉冲生成(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中断线)连接起来。可以类比为 ​​公司前台有一部总机电话,需要把不同部门的电话分机(引脚)转接到对应热线(中断线)上​​。


一、核心功能——“分机转接热线”
  1. ​EXTI中断线的作用​

    • 单片机有多个“中断热线”(如EXTI0~EXTI15),每条热线专门处理一类紧急事件(如按键按下、温度超标)。
    • ​例如​​:EXTI0热线专门接听“PA0、PB0、PC0等引脚”的来电(类似公司总机把PA0分机的电话转接给“投诉专线”)。
  2. ​寄存器的作用​

    • ​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)时,只有“管理员”(安全程序)能操作转接台。防止普通程序(非安全程序)乱改接线规则,导致系统异常。

三、操作步骤与示例
  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立即响应。
  2. ​安全模式下的权限控制​

    • ​TZENO=0(普通模式)​​:任何程序都能修改接线规则(风险:恶意程序可能篡改)。
    • ​TZENO=1(安全模式)​​:只有“管理员程序”(安全代码)能操作寄存器,普通程序写入无效。

四、引脚编号对应表(EXTI0)
​寄存器值​​连接的引脚​​生活场景类比​
0x00PA0把“PA0号工位”的电话转接到EXTI0投诉专线。
0x01PB0把“PB0号工位”的电话转接到EXTI0投诉专线。
0x02PC0把“PC0号工位”的电话转接到EXTI0投诉专线。
.........
0x05PF0把“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权限控制​​管理员锁​​:安全模式下,只有管理员(安全程序)能修改开关规则。防止熊孩子(恶意程序)乱调门铃设置。

三、操作示例与场景
  1. ​按键按下时触发中断(上升沿触发)​

    // 打开PA0引脚的“按下响铃”开关(RT0=1)
    EXTI_RTSR1 |= (1 << 0);  // 类似设置门铃“按下时响”
    EXTI_FTSR1 &= ~(1 << 0); // 关闭PA0的“松开响铃”开关
    • ​效果​​:只有按键按下瞬间触发中断,松开不触发。
  2. ​按键松开时触发中断(下降沿触发)​

    EXTI_RTSR1 &= ~(1 << 0); // 关闭PA0的“按下响铃”开关
    EXTI_FTSR1 |= (1 << 0);  // 打开PA0的“松开响铃”开关
    • ​效果​​:只有按键松开瞬间触发中断。
  3. ​按下和松开都触发(双边沿触发)​

    EXTI_RTSR1 |= (1 << 0);  // 按下时响
    EXTI_FTSR1 |= (1 << 0);  // 松开时也响
    • ​应用场景​​:需要检测按键的完整动作(如长按计时)。

四、注意事项
  1. ​避免毛刺干扰​​:

    • 修改寄存器时,要确保操作原子化(如关闭中断后再修改),防止电平突变导致误触发。
    • ​类比​​:装修时调整门铃线路,先关掉电源,避免触电。
  2. ​权限控制(TZEN)​​:

    • ​普通模式​​:所有程序都能改门铃规则(风险:恶意程序让门铃乱响)。
    • ​安全模式​​:只有管理员能改规则,普通程序操作无效(类似密码锁保护)。

一句话总结

​“EXTI_RTSR1和EXTI_FTSR1像门铃的响铃规则开关:设置按下响、松开响,或者都响,让CPU精准识别外设的‘敲门声’!”​
通过这两个寄存器,开发者可以灵活控制外部事件何时触发中断,为实时响应外设动作提供基础。

3. 设置Masking

允许某个EXTI中断:

这张图讲的是单片机中一个名为 ​​EXTI_IMR1​​ 的“值班表”,它的核心功能是 ​​决定哪些外部设备的“紧急呼叫”(中断)能叫醒正在休息的CPU​​。可以类比为 ​​公司门卫的值班规则​​,规定哪些部门的员工有权在深夜拨打紧急电话给老板。


一、核心功能——“谁的电话能叫醒老板?”
  1. ​门卫的值班表(EXTI_IMR1)​

    • 每个外部设备(如按键、传感器)对应一条“电话线”(中断线)。
    • ​IM0~IM31位​​:每个开关(Bit位)对应一条中断线,控制是否允许这条线的中断叫醒CPU。
      • ​Bit = 1​​:允许这条线的中断(门卫放行)。
      • ​Bit = 0​​:禁止这条线的中断(门卫挂断)。
  2. ​默认规则(复位值0xFFFE 0000)​

    • 复位后,大部分中断默认被屏蔽(类似门卫深夜默认不接普通电话)。
    • ​举例​​:复位值 0xFFFE 0000 对应的二进制中,Bit16=0(屏蔽中断16),其他高位部分为1或0,需具体分析。

二、寄存器结构详解
​寄存器部分​​通俗比喻​​技术细节​
​地址偏移0x080​​值班表的位置​​:在单片机内存中的固定位置(如公司文件柜第0x080号抽屉)。程序员通过这个地址找到并修改值班规则。
​IM0~IM31位​​电话线开关​​:每个开关对应一个外部设备(如IM0对应PA0引脚的中断)。- 写1:允许这条线触发中断。
- 写0:禁止这条线触发中断。
​TZEN权限控制​​值班表加密锁​​:当开启安全模式(TZEN=1)时,只有“安全程序”能修改值班规则。防止普通程序(如恶意代码)乱改中断权限(如屏蔽火灾报警)。
​复位值0xFFFE 0000​​默认值班规则​​:大部分高位中断(如IM16~IM31)默认被屏蔽,低位中断部分开放。需根据实际需求手动开启所需中断(如允许按键中断)。

三、操作示例与场景
  1. ​允许按键中断(如开启IM0)​

    // 地址计算:基地址(如0x40000000) + 偏移0x080
    uint32_t *EXTI_IMR1 = (uint32_t*)(0x40000000 + 0x080);  
    // 将IM0位置1(允许PA0引脚中断)
    *EXTI_IMR1 |= (1 << 0);  
    • ​效果​​:当PA0引脚电平变化(如按键按下),CPU会被叫醒处理。
  2. ​禁止传感器中断(如关闭IM5)​

    // 将IM5位清0(屏蔽传感器中断)
    *EXTI_IMR1 &= ~(1 << 5);  
    • ​效果​​:即使传感器报警,CPU也不会被中断(类似门卫不接仓库来电)。

四、关键细节
  1. ​权限控制(TZEN)​

    • ​普通模式​​:所有程序都能修改值班表(风险:恶意程序屏蔽重要中断)。
    • ​安全模式​​:只有受信任的程序(安全代码)能修改,普通程序操作无效。
  2. ​复位值的意义​

    • 默认屏蔽部分中断是为了系统稳定,避免上电时无关外设频繁打扰CPU。
  3. ​“直接事件”与“可配置事件”​

    • ​直接事件​​:某些特殊中断(如电源故障)无需配置值班表,强制叫醒CPU(类似火警电话直通老板)。
    • ​可配置事件​​:需通过IMR1手动开启(如按键、传感器)。

一句话总结

​“EXTI_IMR1像公司门卫的值班表:设置哪些‘电话线’(中断)能深夜叫醒CPU老板,哪些被静默挂断。复位值决定了默认规则,TZEN锁保护它不被篡改!”​
掌握这个寄存器,就能精准控制单片机对外部事件的响应,平衡实时性与低功耗需求。

4. 查看中断状态、清中断

这张图讲的是单片机中两个重要的寄存器:​​EXTI_RPR1(上升沿挂起寄存器)​​和​​EXTI_FPR1(下降沿挂起寄存器)​​。它们的作用可以类比为 ​​公司前台的“未接来电记录本”​​,专门记录哪些外部设备(如按键、传感器)的“来电”(中断请求)还没被处理。以下是通俗解释:


一、核心功能——“记录谁打过电话还没接”
  1. ​上升沿挂起寄存器(EXTI_RPR1)​

    • 专门记录 ​​电压从低跳变到高时触发的中断​​(比如按键按下的瞬间)。
    • ​例如​​:PA0引脚的电平突然变高(上升沿)→ EXTI_RPR1的第0位会被标记为1(类似前台在记录本上写“PA0来电未接”)。
  2. ​下降沿挂起寄存器(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,可以清除挂起状态(类似用橡皮擦掉记录)。

三、典型操作流程(以按键中断为例)
  1. ​按键按下(上升沿触发)​
    • PA0电平从低变高 → EXTI_RPR1的Bit0被自动置1(前台记录“PA0来电”)。
  2. ​CPU处理中断​
    • 程序检测到EXTI_RPR1的Bit0=1 → 执行按键处理函数(如点亮LED)。
  3. ​清除挂起标志​
    EXTI_RPR1 |= (1 << 0);  // 向Bit0写1,清除挂起状态(类似擦除记录)
    • 若不手动清除,下次检测时仍会认为有未处理中断(类似前台重复提醒同一个来电)。

四、注意事项
  1. ​为什么需要手动清除?​

    • 防止重复响应:如果中断处理函数执行期间再次触发相同中断,挂起位会再次置1,确保不漏事件。
    • ​类比​​:前台擦掉记录后,如果同一号码再次来电,会重新记录。
  2. ​与触发寄存器的区别​

    • ​EXTI_RTSR/FTSR​​:决定“什么条件下算来电”(如按下或松开触发)。
    • ​EXTI_RPR1/FPR1​​:记录“哪些来电还没处理”,需手动清理。
  3. ​保留位的意义​

    • 寄存器中部分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的核心功能​
  1. ​接电话:接收所有设备的“来电”​

    • 公司有很多部门(外设),每个部门有事时都会拨打电话(产生中断)。GIC像前台接线员一样,​​统一接听所有电话​​,避免每个部门直接打给老板(CPU核心)导致混乱。
    • ​例如​​:按键按下、传感器报警、定时器超时,都会通过硬件信号“拨号”给GIC。
  2. ​分优先级:谁的“来电”更重要?​

    • 前台接线员会根据紧急程度排序电话。GIC同样会​​给中断分优先级​​,比如火警(高优先级)比打印机缺纸(低优先级)更早转接给老板处理。
  3. ​转接规则:电话该转给哪个负责人?​

    • 如果是公司内部会议通知(软件中断),前台会直接转给指定员工(特定CPU核心);如果是客户投诉(公共外设中断),可能转给所有部门负责人(多核共享处理)。

二、​​GIC的“电话分机系统”​

GIC将中断分为4种类型,类似不同的电话线路:

​中断类型​​通俗比喻​​技术作用​
​SGI​​内部电话​​:员工用座机拨分机号(软件触发),用于同事间沟通(多核协作)。由软件生成,用于CPU核间通信(如通知其他核处理任务)。
​PPI​​私人专线​​:每个部门经理的私人电话(如财务部的报销专线),只能转接给对应负责人。每个CPU核心独有的中断(如本地定时器),不与其他核共享。
​SPI​​公共热线​​:客户服务电话(如400热线),所有部门都能接听。共享外设中断(如网卡、硬盘),可路由到任意CPU核心处理。
​LPI​​VIP直拨​​:重要客户的专属电话(如PCIe设备的中断),直接通过消息传递。基于消息的优先级中断,配置存储在内存表中(GICv3及以上支持)。

三、​​GIC的“接线流程”​

以按键触发中断为例,流程如下:

  1. ​来电接入​​:

    • 按键按下 → 电平变化(如从高变低)→ GIC检测到“电话铃响”(中断信号)。
  2. ​分类登记​​:

    • GIC查看来电类型(SPI/PPI/SGI),记录在“未接来电本”(挂起寄存器)中。
  3. ​优先级排序​​:

    • 如果同时有多个电话(如火灾警报和打印机缺纸),GIC会优先处理更紧急的。
  4. ​转接处理​​:

    • 根据预设规则(如目标CPU核、安全权限),将电话转给指定负责人(CPU核心)。
    • ​例如​​:安全相关的中断(如指纹识别)只能转给“安保主管”(安全模式下的CPU核)。
  5. ​挂断确认​​:

    • CPU处理完中断后,必须告诉GIC“电话已接听”(清除挂起标志),否则GIC会反复提醒。

四、​​高级功能:安全与多核协作​
  1. ​安全模式(TrustZone)​

    • 普通电话(非安全中断)只能转接给普通员工,机密电话(安全中断)必须转给“保密部门”(安全模式CPU核)。
  2. ​多核分工​

    • 前台接线员(GIC)可以同时连接多个老板(多核CPU),比如:
      • ​大核A7​​:处理复杂任务(如系统调度)。
      • ​小核M4​​:处理实时任务(如电机控制)。
  3. ​动态调整规则​

    • 软件可以随时修改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块)。

三、​​关键细节​
  1. ​为什么需要SPSR?​

    • 发生异常(如中断、系统调用)时,CPU自动将当前状态(CPSR)保存到SPSR,处理完异常后再从SPSR恢复。
    • ​类比​​:接电话前先保存作业进度(CPSR→SPSR),挂断后继续写作业(SPSR→CPSR)。
  2. ​模式切换(M[4:0])的意义​

    • ​用户模式​​:普通APP运行,不能直接操作硬件。
    • ​管理模式​​:操作系统内核运行,可访问所有资源。
    • ​其他模式​​:如中断模式(处理紧急事件)、未定义模式(处理未知指令)等,共7种模式。
  3. ​条件标志位的应用​

    CMP R0, R1    ; 比较R0和R1的值
    BGT label     ; 如果R0 > R1(N=0且Z=0),跳转到label
    • CPU根据N、Z、C、V的值决定是否跳转,类似根据考试分数决定奖励。

四、​​总结:CPSR/SPSR的三大核心功能​
  1. ​记录状态​​:最近的计算结果、当前运行模式、是否允许中断。
  2. ​控制行为​​:切换指令集(ARM/Thumb)、屏蔽中断、管理权限。
  3. ​异常恢复​​:通过SPSR保存和恢复现场,确保程序不被异常打断破坏。

一句话总结

​“CPSR是CPU的实时状态记录本,SPSR是异常时的存档备份——它们决定了CPU能做什么、怎么做,以及如何从‘突发事件’中恢复现场。”​
理解这两个寄存器,是掌握ARM处理器状态管理和异常处理机制的关键!

1.4.1 GPIO控制器

1. 配置GPIO中断

每组GPIO中都有对应的GPIOx_ICR1、GPIOx_ICR2寄存器(interrupt configuration register )。 每个引脚都可以配置为中断引脚,并配置它的触发方式:

这张图讲的是单片机如何通过 ​​GPIOx_ICR1寄存器​​ 给每个引脚(如按键、传感器)设置“门铃响的规则”,比如按一下响铃(按下触发)、松开才响(松开触发),或者两种都响。以下是通俗解释:


一、​​核心功能——给每个引脚设置“触发规则”​
  1. ​每个引脚对应一个“门铃开关”​

    • 单片机有多个引脚(如PA0、PB1等),每个引脚可以接一个外设(比如按键)。
    • ​GPIOx_ICR1寄存器​​ 就像一个“门铃设置面板”,每个引脚对应面板上的一个开关(2个比特位),用来定义这个引脚的触发规则。
  2. ​四种触发模式​
    每2个比特位有4种组合,对应四种触发方式(以按键为例):

    ​比特值​​触发规则​​生活比喻​
    ​00​禁止中断门铃坏了,按了也不响。
    ​01​上升沿触发(按下瞬间)按门铃的瞬间响铃。
    ​10​下降沿触发(松开瞬间)松开门铃的瞬间响铃。
    ​11​双边沿触发(按下和松开都响)无论是按下还是松开都响铃。

二、​​寄存器结构详解​
  1. ​地址计算​

    • 寄存器的位置 = ​​基地址(如0x40000000) + 通道偏移​​(类似门铃面板装在某个固定房间)。
    • 程序员通过这个地址找到并操作寄存器。
  2. ​比特位分组​

    • 寄存器共有32位,分成16组(每组2位),对应16个引脚:
      • ​ICR0​​:控制第0号引脚(如PA0)的触发规则。
      • ​ICR1​​:控制第1号引脚(如PA1)的触发规则。
      • ...
      • ​ICR15​​:控制第15号引脚(如PA15)的触发规则。
  3. ​复位值全0​

    • 单片机上电时,所有引脚的中断触发规则默认是 ​​00(禁止中断)​​,需要手动配置才能生效。

三、​​操作示例(配置PA0按键按下触发中断)​
  1. ​确定引脚对应的比特位​

    • PA0是第0号引脚 → 对应 ​​ICR0​​(比特位1:0)。
  2. ​写入触发规则(01)​

    // 基地址 + 通道偏移(假设基地址为0x40000000)
    uint32_t *GPIOx_ICR1 = (uint32_t*)(0x40000000 + 通道偏移);  
    // 将ICR0设置为01(上升沿触发)
    *GPIOx_ICR1 = (*GPIOx_ICR1 & ~0x03) | 0x01;  
    • ​效果​​:当PA0引脚电平从低变高(按键按下瞬间),触发中断通知CPU处理。

四、​​注意事项​
  1. ​先允许中断,再设置规则​

    • 除了配置GPIOx_ICR1,还需通过其他寄存器(如EXTI_IMR)允许该引脚的中断,否则规则无效。
  2. ​防误触发​

    • 如果引脚信号抖动(如按键接触不良),双边沿触发(11)可能导致多次中断。此时需硬件消抖或软件过滤。
  3. ​多引脚独立配置​

    • 不同引脚可以设置不同规则(如PA0用上升沿,PA1用下降沿),互不影响。

一句话总结

​“GPIOx_ICR1像门铃的触发规则面板:每个引脚对应一个开关,设置按下响、松开响,或者都响,让CPU知道什么时候该处理按键、传感器等外设的‘敲门声’!”​
掌握这个寄存器,就能精准控制外部事件何时触发中断,避免漏报或误报。

2. 使能GPIO中断

这张图讲的是单片机中的 ​​GPIOx_IMR寄存器​​,它的核心功能是 ​​决定哪些GPIO引脚的“门铃”(中断)能叫醒CPU​​。可以类比为 ​​小区保安的值班表​​,规定哪些住户的门禁卡(引脚)能触发警报(中断),哪些直接忽略。


一、核心功能——“谁的门禁卡能触发警报?”
  1. ​保安的值班表(GPIOx_IMR)​

    • 每个GPIO引脚(如PA0、PB1)对应一个“门禁卡”(中断线)。
    • ​IMR的32个Bit位​​:每个开关(Bit位)对应一个引脚,控制是否允许这个引脚的中断触发。
      • ​Bit = 1​​:允许中断(保安放行)。
      • ​Bit = 0​​:禁止中断(保安挂断)。
  2. ​默认规则(复位值全0)​

    • 单片机上电时,所有引脚的中断默认被屏蔽(类似保安默认不接任何住户的门禁警报)。
    • 需要手动开启所需引脚的中断功能(比如允许PA0按键触发中断)。

二、寄存器的“值班表结构”
​寄存器部分​​通俗比喻​​技术细节​
​地址:基地址+0x14​​值班表的位置​​:在单片机的内存中,这个寄存器的“门牌号”是基地址+0x14。程序员通过这个地址找到并修改值班规则(比如 0x40000000 + 0x14)。
​Bit0~Bit31​​32个门禁卡开关​​:每个开关对应一个GPIO引脚(如Bit0对应PA0,Bit1对应PA1)。- 0:屏蔽该引脚的中断(保安忽略这个门禁卡的警报)。
- 1:允许该引脚的中断(保安立即上报警报)。
​复位值全0​​默认状态​​:所有引脚的中断被屏蔽(保安不响应任何门禁警报)。必须手动开启需要的中断(比如允许PA0按键触发)。

三、操作示例与场景
  1. ​允许PA0按键触发中断(Bit0=1)​

    // 找到寄存器地址(假设基地址是0x40000000)
    uint32_t *GPIOx_IMR = (uint32_t*)(0x40000000 + 0x14);  
    // 将Bit0置1(允许PA0中断)
    *GPIOx_IMR |= (1 << 0);  
    • ​效果​​:当PA0引脚电平变化(如按键按下),CPU会立即响应。
  2. ​禁止PB1传感器中断(Bit1=0)​

    // 将Bit1清0(屏蔽PB1中断)
    *GPIOx_IMR &= ~(1 << 1);  
    • ​效果​​:即使传感器报警,CPU也不会被打断(类似保安忽略PB1住户的门禁警报)。

四、注意事项
  1. ​先设置触发条件,再开启中断​

    • 除了配置IMR寄存器,还需设置引脚触发方式(如上升沿/下降沿触发),否则中断可能无法正确触发。
  2. ​复位后必须手动配置​

    • 默认所有中断被屏蔽,需根据需求逐个开启(比如只允许按键和温度传感器触发中断)。
  3. ​防止误操作​

    • 修改寄存器时,建议关闭全局中断(类似让保安暂时休息),避免配置过程中被意外中断打断。

一句话总结

​“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寄存器​​,它的核心功能可以比喻为 ​​家门口的“门铃状态灯”​​:当有人按门铃(触发中断)时,对应的灯会亮起,直到你处理完并手动关灯(清除标志)。以下是通俗解释:


一、​​核心功能——“谁的门铃被按了?”​
  1. ​状态指示灯(ISR寄存器)​

    • 每个GPIO引脚(如PA0、PB1)对应一个“门铃灯”(Bit位)。
    • ​Bit = 1​​:这个门铃被按过且还没处理(灯亮)。
    • ​Bit = 0​​:门铃未被按或已处理(灯灭)。
  2. ​自动亮灯规则​

    • 当引脚的电平满足触发条件(如按键按下或松开)时,对应Bit位自动置1(灯亮),​​即使CPU没空处理,灯也会一直亮着​​(状态保持)。
  3. ​手动关灯规则​

    • 处理完中断后,必须​​向对应Bit位写1​​才能关灯(类似按下灯的开关)。

二、​​寄存器的“门铃灯面板”结构​
​寄存器部分​​通俗比喻​​技术细节​
​地址:基地址+0x18​​门铃灯面板的位置​​:在单片机的内存中固定(如小区物业的监控室第0x18号面板)。程序员通过这个地址(如 0x40000000 + 0x18)查看和操作状态灯。
​Bit0~Bit31​​32个门铃灯​​:每个灯对应一个GPIO引脚(如Bit0对应PA0按键,Bit1对应PB1传感器)。- 读操作:查看哪些灯亮(哪些中断未处理)。
- 写操作:向Bit位写1关灯(清除状态)。
​复位值全0​​默认状态​​:所有灯初始熄灭(上电时无中断触发)。无需手动初始化,但需及时处理亮起的灯(防止漏掉中断)。

三、​​操作流程(以按键触发中断为例)​
  1. ​按键按下(触发条件满足)​

    • PA0电平变化(如从高到低)→ Bit0自动置1(PA0的灯亮起)。
  2. ​CPU查看状态灯​

    // 读取寄存器状态
    uint32_t status = *(uint32_t*)(0x40000000 + 0x18);  
    if (status & (1 << 0)) {  
        // 处理PA0按键中断(如点亮LED)
    }
  3. ​手动关灯(清除标志)​

    // 向Bit0写1,熄灭PA0的灯
    *(uint32_t*)(0x40000000 + 0x18) = (1 << 0);  

四、​​关键细节​
  1. ​为什么要手动关灯?​

    • 如果不清除标志,灯会一直亮着,CPU会认为这个中断未被处理(反复提醒)。
    • ​类比​​:快递员按门铃后,你收完快递但没关灯,物业会反复通知你有快递。
  2. ​等待状态的作用​

    • ​读操作需两个等待状态​​:查看灯的状态时,需要短暂确认(防止误判瞬态干扰)。
    • ​复位需一个等待状态​​:关灯操作需要一点时间生效(类似开关延迟)。
  3. ​与IMR寄存器的区别​

    • ​GPIOx_IMR​​:决定哪些门铃能响(允许/禁止中断)。
    • ​GPIOx_ISR​​:记录哪些门铃已经响过(中断触发状态)。

一句话总结

​“GPIOx_ISR像家门口的32个门铃状态灯:有人按铃(中断触发)灯就亮,处理完必须手动关灯(写1清标志),否则系统会一直提醒你有快递(未处理中断)!”​
掌握这个寄存器,就能精准追踪哪些外部事件触发了中断,避免遗漏或重复处理。

 

可以使用以下汇编指令修改I位:

   CPSIE I  ; 清除I位,使能中断
   CPSID I  ; 设置I位,禁止中断

09_GIC介绍与编程

10_实战_GPIO中断编程(IMX6ULL)

//字数太多了后一节讲

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值