3. 外部中断EXTI:结构框图、触发方式与软硬件中断
外部中断,也就是EXTI,是嵌入式开发里最常用的外设之一。说白了,它就是芯片的「门铃」——外部来了一个信号,芯片就知道该停下手里的事去处理了。
我个人习惯把EXTI比作一个「带过滤器的门卫」。它不光要通知你有人来了,还得告诉你:是前门还是后门?是按门铃还是踹门?是长按还是短按?这些细节,全在EXTI的结构里。
3.1 EXTI结构框图
先看整体结构。EXTI的核心其实不复杂,我画了一张图帮你理清脉络:
这张图从左往右看。最左边是输入源,可以是GPIO引脚、软件中断寄存器(SWIER),或者其他外设(比如RTC闹钟)。中间是边沿检测,决定你是上升沿触发还是下降沿触发。最右边是输出,经过屏蔽寄存器后,送到NVIC或者直接输出事件脉冲。
核心要点:EXTI的输入源有3种,但同一时间一个EXTI线只能选一个。GPIO和软件中断是互斥的,别想着同时用。
3.2 触发方式选择
触发方式,说白了就是「什么样的信号变化才算数」。EXTI支持三种模式:
| 触发方式 | 配置寄存器 | 典型应用场景 |
|---|---|---|
| 上升沿触发 | RTSR | 按键按下(从低到高) |
| 下降沿触发 | FTSR | 按键松开(从高到低) |
| 双边沿触发 | RTSR + FTSR | 编码器信号、脉冲计数 |
我在项目中遇到过一个问题:用下降沿检测按键,结果按键抖动导致一次按下触发了七八次中断。后来我改成「下降沿+硬件消抖+软件延时过滤」,才算稳定下来。你想想看,如果没做消抖,一个按键能让你系统卡死——中断太频繁了。
我的习惯:对于机械按键,我一般用下降沿触发,然后在中断服务函数里加一个10-20ms的延时去抖。虽然不优雅,但可靠。如果你用RTOS,可以用定时器消抖,更专业。
3.3 软件中断与硬件中断
这里有个容易混淆的点:EXTI的中断源,既可以是硬件引脚,也可以是软件写寄存器。我刚开始学的时候也懵过——软件中断?那不就是自己给自己发中断吗?
嗯,没错,就是自己给自己发。但这不是闲得慌,而是有实际用途的。
硬件中断
硬件中断就是外部引脚电平变化触发的。比如你接了一个按键,按下时引脚从高变低,下降沿触发中断。这是最常用的方式。
// 配置PA0为下降沿触发中断
void EXTI0_Init(void) {
// 1. 使能GPIOA时钟
RCC->AHB1ENR |= (1 << 0);
// 2. 配置PA0为输入模式
GPIOA->MODER &= ~(0x3 << 0);
// 3. 连接EXTI0到PA0
SYSCFG->EXTICR[0] &= ~(0xF << 0);
SYSCFG->EXTICR[0] |= (0x0 << 0); // PA0
// 4. 配置下降沿触发
EXTI->FTSR |= (1 << 0);
// 5. 取消中断屏蔽
EXTI->IMR |= (1 << 0);
// 6. 配置NVIC
NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_SetPriority(EXTI0_IRQn, 1);
}
软件中断
软件中断是通过写SWIER寄存器来模拟一个中断。说白了,就是你在代码里「假装」外部来了一个信号。有什么用呢?
- 调试阶段:你可以手动触发中断,测试中断服务函数是否正常,不用真的去碰硬件。
- 任务同步:在某些设计里,软件中断可以用来唤醒一个等待中断的任务。
- 模拟外部事件:比如你要测试一个超时逻辑,但外部信号很难精确控制,软件触发就方便多了。
// 软件触发EXTI0中断
void EXTI0_SoftwareTrigger(void) {
// 写1到SWIER0,立即触发中断
EXTI->SWIER |= (1 << 0);
// 注意:硬件会自动清除这个位,不需要手动清
// 但中断标志位PR还是要清的
}
我曾经踩过的坑:软件中断触发后,中断标志位(PR寄存器)不会自动清除!你必须像处理硬件中断一样,在ISR里手动写1清标志。否则,中断会一直触发,系统直接卡死。别问我怎么知道的...那次调试花了我整整一个下午。
3.4 中断优先级与嵌套
EXTI的中断优先级由NVIC管理。这里有个原则:高优先级中断可以打断低优先级中断。但EXTI本身有多个线(EXTI0~EXTI15),每个线可以独立配置优先级。
我个人习惯把实时性要求高的中断(比如编码器计数)设高优先级,把按键这种不那么紧急的设低优先级。你想想看,如果按键中断把电机控制中断给抢了,电机可能就抖一下——这在工业现场是要出事故的。
| EXTI线 | 典型用途 | 建议优先级 |
|---|---|---|
| EXTI0 | 按键、唤醒 | 低(3~5) |
| EXTI1 | 编码器Z信号 | 高(0~1) |
| EXTI2~EXTI15 | 传感器、通信 | 按需分配 |
一个小技巧:如果你有多个EXTI线共用一个中断服务函数(比如EXTI9_5_IRQHandler),在ISR里一定要先判断是哪个线触发的。读EXTI->PR寄存器,然后分别处理。别偷懒,否则你会收到「莫名其妙的中断」bug反馈。
3.5 实际项目中的配置流程
好了,理论说完了,咱们捋一下实际配置的步骤。我一般按这个顺序来:
- 确定引脚和EXTI线——比如PA0对应EXTI0,PB1对应EXTI1,一一对应,别搞混。
- 配置GPIO为输入模式——上拉还是下拉?看你的外部电路。按键一般配上拉。
- 连接GPIO到EXTI——通过SYSCFG_EXTICR寄存器选择。
- 选择触发方式——RTSR或FTSR,或者两个都选。
- 配置中断屏蔽——IMR寄存器,别忘了。
- 配置NVIC优先级——根据实时性要求来。
- 编写ISR——记得清PR标志位!
嗯,基本上就这些。EXTI本身不复杂,但细节多。尤其是那个PR标志位,我见过太多新手在这上面翻车了。记住:进ISR第一件事,读PR,清PR,再干活。
可可精品资料库