02buhsdoc4y64078292208.gif
0 h9 k2 ~9 I- f( @; k) R点击上方蓝色字体,关注我们
& R% A* D2 k( }' ]1 a$ x* l
p$ \; N& a4 v7 R
3 V! m$ p% p0 `2 q$ i! y7 a1- P# u, F5 P. p
中断向量表; k- C" O+ z- I' b: D) T) o
中断向量表(IVT)是一个存储中断和异常处理程序地址的表格,位于MCU的内存中。在ARM Cortex-M处理器中,IVT通常从地址0x00000000开始,包含初始堆栈指针和各种异常处理程序的地址。1 g% l# O& o" M7 U0 G' \' }
2 l! i! Q4 l# [" ]' V' A- Z每个表项占用4字节,存储一个32位地址,指向对应的ISR或异常处理函数。! ^& y' j2 ]0 P
/ q. n* h9 Q/ s, f, y# p
IVT的作用是提供一种快速查找机制,使MCU在中断发生时能够迅速定位并执行相应的处理程序。例如,当定时器中断触发时,MCU会根据中断号从IVT中获取ISR地址。
/ ^. P& Y! o4 n1 Q; g6 [' i; h
6 r- ~5 Q& R$ q7 [以下是一个简化的IVT结构示例:
; I! D7 h! r Z; k$ B3 U0 a) L( q2 m5 _" L
0xi3c0kvwlb64078292308.png
/ H2 `& E/ ^3 Z! k
$ \4 h8 h( x, p3 v2 H7 M3 T5 X
IVT通常存储在闪存(Flash)中,但可以通过向量表偏移寄存器(VTOR)将其重定位到其他内存区域(如SRAM),以支持动态更新或引导加载程序(Bootloader)场景。
# @2 b$ q" C0 Y/ V( |; {
" X5 J B8 K0 e7 F$ t' yARM Cortex-M处理器通过嵌套向量中断控制器(NVIC)管理中断。以下是中断处理的具体步骤:5 z3 I8 v# W2 u% ]
完成当前指令:处理器首先完成当前正在执行的指令,除非该指令是可中断继续指令(interrupt-continuable instruction)。这确保了指令的原子性。保存上下文:处理器自动将当前程序的上下文保存到堆栈上,包括寄存器R0-R3、R12、链接寄存器(LR)、程序计数器(PC)和程序状态寄存器(xPSR)。这些寄存器共占用32字节(每个寄存器4字节)。对于使用浮点单元(FPU)的系统,FPU寄存器也可能被保存。读取中断号:NVIC从中断程序状态寄存器(IPSR)获取中断号(异常号),该中断号用于索引IVT。获取ISR地址:使用中断号N,处理器计算IVT中的地址(4 × N),从中读取ISR的起始地址。例如,SysTick中断(异常号15)的地址位于0x0000003C。更新寄存器:处理器更新堆栈指针(SP)、链接寄存器(LR)和程序计数器(PC)。LR被设置为特殊值(如0xFFFFFFF9),指示返回模式(例如,返回线程模式并使用主堆栈指针)。执行ISR:处理器跳转到ISR地址并执行中断处理程序。ISR通常会清除中断标志并执行必要的操作。异常返回:ISR完成后,处理器从堆栈恢复保存的上下文,并使用LR中的特殊值返回到被中断的程序,继续执行。: W" E2 S' ^! k. f/ b
向量表通常在启动代码中定义,由工具链提供或由开发者编写。在C语言中,向量表可以定义为一个函数指针数组。例如:1 k1 }7 m- G( d: _+ v6 C
- Q& H9 i9 U. \9 M( w% y. ^
void (* const vectors[])(void) __attribute__((section(".vectors"))) = { (void (*)(void)) &_stack_top, // 初始堆栈指针 Reset_Handler, // 复位处理程序 NMI_Handler, // NMI处理程序 HardFault_Handler, // 硬故障处理程序 // ... 其他处理程序 SysTick_Handler // SysTick处理程序};( v+ z, v# g5 i1 h1 S) L. C
其中:
$ T/ H, y1 C1 l v/ r+ h+ o! U" G_stack_top由链接脚本定义,表示堆栈顶部地址。__attribute__((section(".vectors")))确保数组被放置在链接脚本指定的.vectors段,通常映射到0x00000000。每个处理程序(如Reset_Handler)是一个函数,定义在其他代码文件中。9 P- v( |3 v1 [
' X) F6 H5 A% N; j# F( U在汇编语言中,向量表可能如下定义(以Keil MDK的startup.s为例):% I7 s: W; X2 ?( m
1 [; ^- [. E( c) K9 h8 X" |+ r .section .vectors .word _estack .word Reset_Handler .word NMI_Handler .word HardFault_Handler // ... .word SysTick_Handler链接脚本确保.vectors段位于正确地址。开发者通常无需直接修改向量表,只需实现对应的ISR函数。
) |# Y7 Q& d2 w' q- M) `. S2" g' M9 q- q) H6 C, s/ f+ Q
编写中断服务例程
( |+ D4 `5 V* n( y9 d3 W' kISR是处理特定中断的函数,必须高效且可靠。以下是编写ISR的最佳实践:
* W0 k4 K& l* V* K8 x# a保持简洁:ISR应尽量短,避免复杂计算或阻塞调用。清除中断标志:确保清除触发中断的标志,以防止重复触发。使用volatile变量:防止编译器优化导致变量访问错误。避免嵌套复杂逻辑:如果需要复杂处理,可将任务标记为待处理并在主循环中执行。
$ n* ?( _9 r4 d) `' F以下是一个简单的定时器中断ISR示例:& k! ?' h5 }+ Q/ q6 @2 ?" T
6 M* G8 Y9 d, ^- `6 b& A5 O: Z* u
void TIM2_IRQHandler(void) { TIM2->SR &= ~TIM_SR_UIF; // 清除更新中断标志 // 执行操作,例如切换LED状态}ARM Cortex-M通过NVIC支持嵌套中断,允许高优先级中断抢占低优先级中断。优先级通过NVIC的优先级寄存器(IPR)配置,值越小优先级越高。例如,Cortex-M3/M4支持最多256个优先级级别,但具体实现可能只使用部分位(如4位,支持16个级别)。7 j$ E. i6 p% W# Q5 b
; V" f6 H% K% |4 [. S# m) R
开发者可以通过以下方式管理中断优先级:4 |' p+ i+ a. h! u4 h
使用NVIC函数(如NVIC_SetPriority(IRQn, priority))设置优先级。通过__enable_irq()和__disable_irq()全局启用或禁用中断。使用BASEPRI寄存器屏蔽低于某优先级的中断(在Cortex-M3/M4中支持)。9 }3 G4 J# d) p4 b9 N* F1 U
3 u( Y6 Y2 V: M( P# c
嵌套中断的典型场景是高优先级中断(如紧急传感器触发)打断低优先级中断(如定时器任务),确保关键任务优先执行。$ E. |4 Z9 {2 V7 n _. L
& Q& n- x/ P0 \( r从向量表到中断服务的过程是嵌入式系统中中断管理的核心。通过理解IVT的结构、中断处理流程以及如何配置和编写ISR,开发者可以设计高效、实时的嵌入式系统。ARM Cortex-M的NVIC和硬件优化(如尾链式处理)进一步降低了中断延迟,提高了系统性能。2 @ I4 h$ N1 h& F I. M' ]4 E) `9 d
' ?' y( S5 L. x1 h4 c* g/ m" `
虽然本文以ARM Cortex-M为例,但其他MCU(如8051)也有类似的中断处理机制,尽管向量表地址和异常号可能不同。开发者应参考具体MCU的数据手册和工具链文档以获取详细信息。
5 H% J! B" I0 |! G! y" T+ t7 O* n5 \
ggpti3cpqz564078292408.jpg
4 Z* m* N' b ]; R4 w+ K. D
w54w20thrgm64078292508.gif
4 Q. l1 o2 B, w p O点击阅读原文,更精彩~ |