3、外部中断EXTI:结构框图、边沿检测与软件触发
外部中断,也就是EXTI,是STM32里最常用的中断之一。说白了,就是让芯片对外部引脚上的电平变化做出反应。比如按键按下、传感器输出跳变、或者某个信号来了个上升沿——这些都能触发中断。
我个人习惯把EXTI叫做“敲门中断”。为什么?因为它的作用就像有人敲门,CPU正在屋里忙别的事,听到敲门声就停下来看看是谁。嗯,这个比喻虽然简单,但很形象。
3.1 EXTI结构框图——先看整体
要理解EXTI,先得看它的内部结构。我画了一张图,帮你理清信号是怎么从引脚走到CPU的。
从图上你能看到,EXTI的路径其实不复杂:
- GPIO引脚:外部信号从这里进来
- 边沿检测电路:判断是上升沿还是下降沿
- 或门:硬件触发和软件触发在这里汇合
- 挂起寄存器:记录中断是否发生
- 中断屏蔽寄存器:决定是否放行
- NVIC:最终送到CPU
我在项目中遇到过一个问题:明明配置好了EXTI,但中断就是不进。查了半天,发现是挂起寄存器没清干净。嗯,这个坑后面会细说。
3.2 边沿检测配置——上升沿还是下降沿?
边沿检测是EXTI的核心功能。你想想看,一个信号从低变高,或者从高变低,CPU怎么知道该在哪个时刻响应?
STM32提供了三种触发方式:
| 触发方式 | 寄存器配置 | 适用场景 |
|---|---|---|
| 上升沿触发 | EXTI_RTSR 对应位置1 | 按键按下(高电平有效) |
| 下降沿触发 | EXTI_FTSR 对应位置1 | 按键松开(低电平有效) |
| 双边沿触发 | RTSR和FTSR同时置1 | 脉冲计数、编码器信号 |
配置代码其实很简单。我习惯用HAL库,但也会看看寄存器版本,心里更有底:
// HAL库方式
HAL_GPIO_EXTI_IT_Init(GPIO_PIN_0, EXTI_TRIGGER_RISING);
// 寄存器方式(更直接)
EXTI->RTSR |= EXTI_RTSR_TR0; // 使能PA0上升沿触发
EXTI->FTSR &= ~EXTI_FTSR_TR0; // 关闭下降沿触发
我的经验:如果你做按键检测,建议用下降沿触发。为什么?因为按键按下时会有抖动,下降沿触发配合硬件消抖或者软件延时,效果更好。上升沿触发容易误判。
3.3 软件中断触发——调试利器
软件中断触发,说白了就是你在代码里手动“捏造”一个中断信号。这个功能在调试时特别有用。
我记得有一次,硬件工程师说某个传感器信号不稳定,让我查查是不是中断处理有问题。我直接用软件触发模拟了一个中断,发现处理函数里有个变量没初始化。嗯,要是没有软件触发,我得拿着示波器在那等半天。
软件触发的寄存器是 EXTI_SWIER:
// 软件触发EXTI线0
EXTI->SWIER |= EXTI_SWIER_SWI0;
// 检查是否触发成功
if(EXTI->PR & EXTI_PR_PR0)
{
// 中断挂起标志已置位
// 说明软件触发生效了
}
使用软件触发时,有几个要点:
- 软件触发和硬件触发走的是同一条路径,都会经过挂起寄存器
- 触发后必须手动清除挂起标志,否则中断会一直进
- 软件触发不受边沿检测配置影响,直接生效
我曾经踩过的坑:软件触发后忘记清挂起标志,结果中断函数被反复调用,系统直接卡死。后来我养成了一个习惯:在中断处理函数的最后,一定先清挂起标志,再做其他操作。
3.4 中断处理流程——从触发到响应
一个完整的EXTI中断流程是这样的:
- 外部信号到达GPIO引脚
- 边沿检测电路判断是否符合触发条件
- 信号通过或门(与软件触发汇合)
- 挂起寄存器对应位置1
- 如果中断屏蔽寄存器允许,信号继续传递
- NVIC收到中断请求,判断优先级
- CPU暂停当前任务,执行中断服务函数
- 中断服务函数中清除挂起标志
- CPU返回断点继续执行
这里有个细节很多人会忽略:挂起标志必须在中断服务函数中清除。如果你在主循环里清,中断可能会再次触发,形成死循环。
// 中断服务函数示例
void EXTI0_IRQHandler(void)
{
// 先清挂起标志
if(EXTI->PR & EXTI_PR_PR0)
{
EXTI->PR = EXTI_PR_PR0; // 写1清除
// 处理中断事件
// 比如读取按键状态、记录时间戳等
}
}
核心要点:EXTI的配置其实就三步——选引脚、配边沿、开中断。但真正用好它,你得理解挂起寄存器的行为,以及软件触发在调试中的价值。
好了,EXTI的内容就这些。你想想看,从结构到配置,再到软件触发,其实都不复杂。关键是多动手,在板子上跑一遍,遇到问题就知道怎么调了。