一、中断基础概念:中断的定义、作用、分类与向量表
各位同学,咱们今天聊中断。说实话,中断这玩意儿是嵌入式系统的灵魂之一。我做了十几年嵌入式,见过太多因为中断没处理好导致系统崩溃的案例。你想想看,一个没有中断的系统,就像一个人只能排队办事,前面的人不走你就得一直等着——这效率,太低了。
1.1 中断到底是什么?
中断,说白了就是CPU正在干一件事的时候,突然被一个紧急事件打断,转去处理这个紧急事件,处理完再回来接着干原来的事。
举个生活中的例子:你正在看书(主程序),突然手机响了(中断事件),你接电话(中断服务程序),挂断后继续看书(返回主程序)。嗯,就是这么个道理。
中断的核心特征:
- 异步性:中断事件的发生时间不可预测
- 优先级:紧急程度不同,处理顺序不同
- 可嵌套:处理一个中断时,可以被更高优先级的中断打断
1.2 中断的作用——为什么我们需要它?
我刚开始做嵌入式开发时,用的是轮询方式——就是主循环里不断检查各个外设的状态。那时候做一个简单的按键检测,CPU 90%的时间都在空转。后来用了中断,CPU利用率直接降到10%以下。
中断的主要作用有这几个:
| 作用 | 说明 | 实际场景 |
|---|---|---|
| 提高CPU利用率 | CPU不用一直等待事件发生 | 串口接收数据,有数据才触发中断 |
| 实时响应 | 对紧急事件快速处理 | 电机过流保护,必须在微秒级响应 |
| 任务切换 | 操作系统调度的基础 | FreeRTOS的SysTick中断 |
| 功耗管理 | CPU可以进入休眠,中断唤醒 | 物联网设备待机模式 |
我的经验:在低功耗项目中,我习惯把不用的外设中断全部关掉,只保留唤醒源的中断。曾经有个项目,就是因为一个GPIO中断没关,设备待机电流从10μA飙到了2mA——电池寿命直接砍半。
1.3 中断的分类——三种中断,各有各的脾气
中断按来源分,主要有三类。我画了个图,方便你理解它们之间的关系:
1.3.1 外部中断
外部中断来自芯片的引脚。比如你按下一个按键,电平变化触发中断。我记得有个项目,客户说设备偶尔死机,查了三天发现是按键抖动触发了多次中断,导致中断栈溢出。后来加了个硬件去抖电路,问题就解决了。
1.3.2 内部中断
内部中断由芯片内部的外设产生。定时器溢出、ADC转换完成、DMA传输结束——这些都是内部中断的典型场景。我建议你在设计系统时,能用DMA的地方尽量用DMA,它可以在不占用CPU的情况下完成数据传输,只在完成时触发一次中断。
1.3.3 软件中断
软件中断是CPU执行特定指令触发的。在ARM Cortex-M上,SVC指令就是典型的软件中断。操作系统用它来实现系统调用——用户程序通过SVC指令请求内核服务,比如创建任务、申请内存。
注意:软件中断的优先级通常由操作系统管理,不要随意修改。我曾经在一个RTOS项目中,手欠改了SVC中断的优先级,结果系统调度直接乱套——任务该跑的不跑,不该跑的抢着跑。调试了整整两天才找到原因。
1.4 中断向量表——CPU的紧急联络簿
中断向量表,你可以把它想象成一本电话簿。CPU收到中断信号后,查这个表就知道该跳到哪里去处理。
以ARM Cortex-M3为例,向量表放在Flash的起始地址:
; 向量表示例(STM32F103)
__Vectors DCD __initial_sp ; 0x00000000 栈顶指针
DCD Reset_Handler ; 0x00000004 复位中断
DCD NMI_Handler ; 0x00000008 NMI中断
DCD HardFault_Handler ; 0x0000000C 硬件错误
DCD MemManage_Handler ; 0x00000010 内存管理
DCD BusFault_Handler ; 0x00000014 总线错误
DCD UsageFault_Handler ; 0x00000018 用法错误
; ... 后续是外设中断
DCD TIM2_IRQHandler ; 定时器2中断
DCD USART1_IRQHandler ; 串口1中断
向量表的关键点:
| 特性 | 说明 | 注意事项 |
|---|---|---|
| 固定位置 | 通常从0地址开始 | 部分芯片支持重定位 |
| 固定大小 | 每个向量4字节(32位) | 64位芯片为8字节 |
| 顺序固定 | 中断号对应固定位置 | 芯片手册有明确列表 |
| 优先级关联 | 向量位置决定默认优先级 | 可编程修改 |
避坑指南:我曾经在Bootloader项目中,把应用程序的向量表重定位到了0x08010000。结果忘了修改SCB->VTOR寄存器,中断一来CPU直接跳到0地址——那里是Bootloader的向量表,应用程序的中断一个都跑不对。嗯,从那以后我每次做Bootloader,第一件事就是检查VTOR的值。
1.5 中断处理流程——CPU到底干了啥?
一个完整的中断处理流程,我习惯把它分成6步:
- 中断请求:外设或引脚产生中断信号
- 中断判优:CPU检查当前中断优先级,决定是否响应
- 保护现场:自动压栈PC、PSR、LR等寄存器
- 查向量表:根据中断号找到对应的ISR入口地址
- 执行ISR:运行中断服务程序
- 恢复现场:出栈,返回被中断的程序
这里有个细节:Cortex-M3/M4处理器在硬件层面自动完成了压栈和出栈,不需要你在ISR里手动保存寄存器。但有些老架构(比如ARM7)需要手动处理——我刚开始做ARM7时,就因为忘了保存R0-R3,导致中断返回后数据全乱了。
核心原则:中断服务程序要短、要快、要干净。我给自己定的规矩是:ISR里只做标志位设置和数据搬运,真正的处理逻辑放到主循环或任务里。超过100条指令的ISR,基本就是设计有问题。
1.6 本章小结
中断是嵌入式系统的核心机制,理解它的基础概念是后续性能调优和调试的前提。外部中断、内部中断、软件中断各有各的应用场景,向量表是CPU响应中断的导航图。记住:中断处理的核心是快进快出,别在ISR里干重活。