寄存器操作 STM32 临时屏蔽 GPIO EXTI 外部按键中断
在编写单片机程序时,常常会有这样的需求:
graph LR
A[监听按键事件中断] -- 按键按下 ---> B[中断回调函数] --> C[执行用户代码]
但是,由于按键抖动等原因,按键中断可能会被意外触发,导致按钮事件重复执行,这不是我们希望看到的。
解决此问题的一个方法,就是“在中断回调函数头部屏蔽按键中断事件,在尾部重新开启中断事件”,以达到防止按钮误触的目的。
本文以 STM32F103C8T6
为例,介绍通过寄存器屏蔽特定线路的外部中断的方法。
EXTI Line
要想操作外部中断,首先要了解 “EXTI 线”。这里放一张手册上的图。
本次我们把目光聚集于 EXTI1
~ EXTI15
部分。
我们可以发现,每个 EXTIx
都对应了 GPIOx
。
而且,此处的 GPIOx
对所有 GPIO Group
都有效。
也就是说,如果我们想要屏蔽 PB12
上的外部中断,那么我们就需要屏蔽 EXTI12
,由此带来的副作用就是 PA12、PC12、PD12...
上的外部中断都会被屏蔽(呃,如果有更好的解决办法欢迎留言补充)。
Register
寄存器的主角是 EXTI_IMR
。查阅手册可知,其名称为“外部中断屏蔽寄存器”。
寄存器的 20~31 为保留位,剩下的每一位都对应一条
EXTI Line
。
使用方式也很简单,如果想要屏蔽第 x
条
EXTI Line
,就把 MRx
置为1即可。
代码操作
了解寄存器结构后,代码操作就很简单了:
1 | EXTI->IMR &= ~(GPIO_PIN_12); // 临时关闭 |
将上下两条语句分别插入在中断回调函数的头和尾部分即可。亲测能有效屏蔽中断,防止误触。
当然,按照位运算的规律,如果要同时操纵两条 EXTI LINE
,可以这么写:
1 | EXTI->IMR &= ~(GPIO_PIN_12 | GPIO_PIN_8); |
注意事项
我在开发时使用了 STM32CubeMX ,并给管脚自定义了名称
KEY_1_Pin
。
于是在生成的 main.h
中有了如下宏定义:
1 |
需要注意,不要被宏名称误导,不能写成:
1 | EXTI->IMR &= ~(EXTI15_10_IRQn); |
我们预期的右值是 0x1000
,但是
KEY_1_EXTI_IRQn
的值为 40。
或者也不能写成:
1 | EXTI->IMR &= ~(EXTI_LINE_12); |
因为此处 EXTI_LINE_12
的值为 12。