stm32 中断与事件*
stm32中断和事件
stm32的中断资源
NVIC 即嵌套向量中断控制器, 是内核的器件,M3 内核都是支持 256 个中断,总共包括:
- 16 个系统中断
- 240 个外部中断
但实际上,ZET6 的系统中断只用了 10 个,外部中断有 60 个

中断向量表在
STM32F103xx.h文件中被定义
NVIC
NVIC 相关的寄存器定义了可以在 core_cm3.h 文件中找到
typedef struct |
ISER
上面说了 CM3 内核总共支持 256 个中断,这里用 8 个 32 位寄存器来控制,刚好一一对应,每个位控制一个中断。但是STM32F103的可屏蔽中断最多只有60个,所以对我们来说,有用的就是两个(ISER[0]和ISER[1]),总共可以表示 64 个中断
你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)
ICER
和ISER相反,作用是关闭中断
ISPR
中断使能挂起控制寄存器组,置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的
ICPR
置 1,可以将挂起的中断解挂。写 0 无效
IABR
中断激活标志位寄存器组, 为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零
IP[240]
中断优先级控制的寄存器组。注意,这里是240个,因此这里NVIC只能管理240个外部中断
每个外部中断,一个8bit的优先级IP,实际有效位是只用了
高 4 位, 这4位又分为,抢占优先级和子优先级。具体两种优先级如何组合,由SCB->AIRCR中的中断分组设置来决定
考虑到和rtos的兼容,我们一般选分组4,全部用抢占优先级

总结:
以上,发现,NVIC寄存器组,可以设置总体的优先级分组,外部中断的使能,优先级
EXTI
EXTI 即是外部中断和事件控制器, 由 20 个产生事件/中断请求的边沿检测器组成, 说白了,就是个边缘检测器,提供触发中断的硬件信号。
说白了,就是给一些没有中断功能的功能,提供中断
框图

可以看到,有两条主线,一条是由输入线到 NVIC 中断控制器,一条是由输入线到脉冲发生器。
这就恰恰是 EXTI 的两大部分功能,
- 产生中断
- 产生事件
两者从硬件上就存在不同
产生中断
- 通过配置寄存器设置为任何一个 GPIO 口,或者是一些外设的事件。输入线一般都是存在电平变化的信号
- 经过边沿检测电路,捕获上升沿下降沿
- 考虑判断是否软件中断
- 判断中断屏蔽IMR
产生脉冲事件
- 事件产生,不多细说,方法差不多,核心就是,使脉冲发生器电路产生一个脉冲
一般输出到如定时器,模拟数字转换器等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换
产生中断线路目的使把输入信号输入到 NVIC,进一步运行中断服务函数,实现功能。
产生事件线路目的是传输一个脉冲信号给其他外设使用,属于硬件级功能
EXTI支持输入
EXTI 支持 19 个外部中断/事件请求
- EXTI 线 0~15:对应外部 IO 口的输入中断
- EXTI 线 16:连接到 PVD 输出
- EXTI 线 17:连接到 RTC 闹钟事件
- EXTI 线 18:连接到 USB 唤醒事件
- EXTI 线 19:连接到以太网唤醒事件
IO 口使用的中断线只有 16 个,但这个不是说,EXTI只能给16个GPIO提供中断功能
STM32,一共7个GPIO组外设(ABCDEFG),每组16个
即便这样,那么每个EXTI0-15, 还要对应7个gpio (exti0 -> A0,B0….G0),具体对应哪个gpio呢?
GPIO 和中断线映射关系是在寄存器
AFIO_EXTICR1 ~ AFIO_EXTICR4中配置的
注意:我们配置 AFIO 相关寄存器前,还需要打开 AFIO 时钟,因为AFIO也是一个外设。
总结:我们如果需要gpio输入中断,涉及的外设有:
- NVIC
- GPIO组
- EXTI
- AFIO
实验
细节很简单,就不用说了
以PE4作为输入中断为例
int main(void) |
在extix_init()内部:
void extix_init(void) |
HAL_GPIO_Init()已经帮你配置exti和afio这两个外设了,看看HAL的驱动里面的实现
/*--------------------- EXTI Mode Configuration ------------------------*/ |
可以看到,如果你配置了GPIO的模式是用外部中断,那么会先使能AFIO外设
另外也可以看到,extix_init()里面,最后,也设置NVIC的相应中断优先级+使能中断(这个就是NVIC仅仅能做的事)。
最终,当触发中断后,会跳转到来执行。
DCD EXTI4_IRQHandler ; EXTI Line 4 |
但是要注意,这个中断处理函数,对应的EXTI4,是对应很多个GPIO的触发可能的。所以,内部还需要判断中断源头。
一般来说,为了方便管理,我们把中断向量表里面的中断处理函数重定向到HAL中,这样同意都是在驱动层进行统一处理和管理。
void KEY0_INT_IRQHandler(void) |
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) |
可以看到,HAL里面,仅仅只是对中断标志位清除一下,或者判断一些信息。然后调用回调函数。而回调函数,是一个弱定义,所以你可以自己在业务层去编写自己的回调函数。
exti.c中
/** |
旧
定义
中断和事件是两个东西
中断是指的,cpu的中断控制器在收到中断请求后,停止当前任务,转去处理中断事件,运行中断处理函数,根据IVT中定义的函数策略,都是WEAK定义,你可以自己覆写。
事件则指的,外部的输入信号,不输入NVIC, 直接到脉冲产生模块,从而产生一个脉冲事件,这个脉冲信号会发给你想要发给的设备。


中断产生流程
- 外设输入端gpio产生信号(比如下降沿,上升沿)
- EXTI起到路由的作用,
- 既可以产生软件中断(内部设置软件中断寄存器把或门置1),
- 也可以产生硬件中断(判定上升沿触发,边沿检测输出1,自动把请求挂起寄存器置1,之后只要把中断屏蔽寄存器置1开中断即可让NVIC产生中断,NVIC会通知CM3内核去按照IVT执行相应的中断处理函数)
- 产生事件(脉冲信号,直接发给其他的设备)
- NVIC接收到中断请求,开始通知CM3产生中断
- CM3保存当前堆栈,然后跳转到IVT指定的中断处理函数。
- EXTI4_IRQHandler中断处理函数内,清除挂起位,表面CM3已经知道并且正在处理此中断,并调用对应的回调函数来处理中断。处理完之后,再次清中断,防止多次处理。
- CM3恢复现场
NVIC
CM3内核内部的模块,具体定义在ARM中。
帮助设置所有中断的优先级组,同时设置一个中断的抢占式优先级和子优先级。
EXTI
可以看成是一个输入信号的路由模块,把我们输入的输入信号,帮助NVIC判断是什么方式触发,是想要软件触发还是外部硬件触发,是想要触发中断还是触发事件。
实现流程思路
总共设置4个部分
输入gpio –> EXTI –> NVIC –> 输出GPIO
NVIC部分:
- 设置优先级组
- 设置抢占式优先级和子优先级
- 使能NVIC
- 找到IVT对应的想要的中断处理函数入口名称
EXTI:
- 设置下降沿触发
- 关闭软件中断(可有可无)
- 开中断(IMR)
GPIO输入:
- 设置gpio相关
- 与EXTI绑定
(hal_gpio库中,已经将输入GPIO和EXTI合并,可以直接像设置gpio一样选择AFIO复用EXTI输入)
GPIO输出:
- 重写回调函数,gpio中断的回调函数,hal库中已经写好了weak定义。