12. EXTI—外部中断/事件控制器¶
本章参考资料:《dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics》参考手册 第24章 Extended interrupt and event controller (EXTI)章节
上一章节我们已经详细介绍了NVIC,对STM32MP157 Cortex-M4内核中断管理系统有个全局的了解, 这章的内容是NVIC的实例应用,也是STM32MP157 Cortex-M4控制器非常重要的一个资源。 学习本章时,配合《dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics》 参考手册Extended interrupt and event controller (EXTI)章节一起阅读,效果会更佳。
特别说明,本书内容是以STM32MP157 Cortex-M4控制器资源讲解。
12.1. EXTI简介¶
外部中断/事件控制器(EXTI)管理了控制器的16个中断/事件线。EXIT时钟挂载在AHB高级高性能总线上, 每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
12.2. EXTI功能框图¶
EXTI的功能框图包含了EXTI核心内容,掌握了功能框图,对EXTI就有一个整体的把握, EXTI功能框图如下 。
图中标号1为EXIT的外设时钟。标号2为选择需要产生中断/事件的GPIO引脚,标号3为外设产生中断或者唤醒信号, 标号4为事件的触发控制,标号5为可配置事件(y)相关联的CPU1和CPU(m)中断,标号6为M4内核传递中断部分。
12.3. 中断/事件线¶
EXTI有16个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0至EXTI15。 具体用法参考《dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics》 参考手册中对外设的具体说明。
表 11‑1 EXTI中断/事件线
中断/事件线 |
输入源 |
---|---|
EXTI0 |
PX0(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI1 |
PX1(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI2 |
PX2(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI3 |
PX3(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI4 |
PX4(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI5 |
PX5(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI6 |
PX6(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI7 |
PX7(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI8 |
PX8(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI9 |
PX9(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI10 |
PX10(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI11 |
PX11(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI12 |
PX12(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI13 |
PX13(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI14 |
PX14(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXTI15 |
PX15(X可为A,B,C,D,E,F,G,H,I,J,K,Z) |
EXIT能够产生中断和事件,中断和事件最大的区别在于一个需要软件的参与,另一个不需要软件的参与, 举个例子,当需要使用外部的信号触发adc采样时,在使用中断的情况下需要在产生中断进入中断服务程序之后 使用软件去触发adc采样,而如果使用事件触发则完全不需要软件代码的干预, 在产生事件之后硬件会进行adc的采样。这就是中断和事件的区别,当然这中间需要正确得配置EXIT的模式。
EXTI0可以通过EXIT外部中断配置寄存器1(EXTI_EXTICR1)的EXTI0[7:0]位选择配置为PA0、PB0、PC0、PD0、 PE0、PF0、PG0、PH0、PI0、PJ0、PK0或者PZ0,如下, 其他EXTI线(EXTI中断/事件线)使用配置都是类似的。
图 11‑2 EXTI0输入源选择
12.4. EXTI初始化详解¶
HAL库函数的EXIT初始化非常简单,只需配置好IO口的模式,然后配置中断源、中断优先级、使能中断。
HAL_NVIC_SetPriority:该函数负责EXTI中断/事件线选择,可选EXTI0至EXTI15,可参考表 17-1选择,配置优先级。
HAL_NVIC_EnableIRQ:该函数负责控制使能中断。
12.5. 外部中断控制实验¶
中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的,
我们设计使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制LED的任务。 本次实验所用工程在之前 向工程中添加代码文件 章节的工程上所修改,设置了M4的工作时钟频率为209Mhz, LED初始化的过程以及rcc如何配置在本章节中并不涉及,重点讲解内容放在EXIT中断配置上。
12.5.2. 软件设计¶
这里只讲解EXIT相关配置以及EXIT相关代码,LED初始化并没涉及到。 完整的代码请参考本章配套的工程。
12.5.2.1. 将引脚设置为EXIT模式¶
将KEY1所在引脚PB13设置为EXIT功能模式
选择分配给M4内核。
将KEY2所在引脚PH7设置为EXIT功能模式
选择分配给M4内核。
设置引脚标签(不是必需步骤)
12.5.2.2. 设置中断触发方式¶
此处可选择将产生中断还是事件,其中中断与事件各分为三种模式:上升沿触发、下降沿触发、双边沿触发。 默认为上升沿触发,此处设置KEY1设置为上升沿触发,KEY2为双边沿触发。
选择NVIC勾选enable使能中断触发,当需要设置中断优先级时,可选择System Core中的NVIC进行设置。 在本工程中保持默认分配的中断优先级分组以及中断优先级。
最后选择生成代码即可
12.5.2.3. MX_GPIO_Init函数¶
代码清单 17‑3 EXTI中断服务函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | void MX_GPIO_Init(void)
{
/*省略LED初始化代码*/
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = KEY2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(KEY2_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = KEY1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI7_IRQn);
HAL_NVIC_SetPriority(EXTI13_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI13_IRQn);
/*省略LED初始化代码*/
}
|
在STM32CubeIDE中设置KEY1为上升沿触发,设置KEY2为下降沿触发,此处GPIO_InitStruct.Mode 分别被设置成了 GPIO_MODE_IT_RISING 及 GPIO_MODE_IT_RISING_FALLING , 同时使用了HAL_NVIC_SetPriority设置了中断优先级,使用HAL_NVIC_EnableIRQ开启相对应的中断。 可见EXIT的配置过程并不复杂。
12.5.2.4. EXIT中断服务函数¶
当中断发生时,对应的中断服务函数就会被执行,中断服务函数的入口函数在stm32mp1xx_it.c文件中, 在stm32mp1xx_it.c文件中有以下两个函数
1 2 3 4 5 6 7 8 9 | void EXTI7_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
}
void EXTI13_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}
|
在EXIT7以及EXIT13中均调用了HAL_GPIO_EXTI_IRQHandler函数, 查看下HAL_GPIO_EXTI_IRQHandler函数的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_RISING_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_Pin);
HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin);
}
if (__HAL_GPIO_EXTI_GET_FALLING_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_Pin);
HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin);
}
}
|
__HAL_GPIO_EXTI_GET_RISING_IT函数用来获取EXTI上升沿的中断标志位状态, 如果EXTI线有上升沿中断发生函数返回“SET”否则返回“RESET”。 调用__HAL_GPIO_EXTI_CLEAR_RISING_IT函数清除EXTI线的上升沿中断标志位。 并调用HAL_GPIO_EXTI_Rising_Callback处理上升沿中断, 同理对下降沿中断的处理也是类似的。通过调用HAL_GPIO_EXTI_Falling_Callback处理下降沿中断。
HAL_GPIO_EXTI_Rising_Callback和HAL_GPIO_EXTI_Falling_Callback函数定义如下所示
1 2 3 4 5 6 7 8 9 | __weak void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
UNUSED(GPIO_Pin);
}
__weak void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
UNUSED(GPIO_Pin);
}
|
这两个回调函数并没有实现什么功能,同时这两个函数使用 __weak声明, 因此需要在外部重新编写这两个函数。
12.5.2.5. 添加中断服务函数文件¶
我们知道在stm32mp1xx_it.c已经有了中断服务函数的入口了,若直接将回调函数写在stm32mp1xx_it.c文件中, 重新生成代码时回调函数就会被覆盖。 因此在User目录下添加一个exit目录,新建bsp_exit.c以及bsp_exit.h文件, 重新编写HAL_GPIO_EXTI_Rising_Callback和HAL_GPIO_EXTI_Falling_Callback回调函数的内容。 (原来的HAL_GPIO_EXTI_Rising_Callback和HAL_GPIO_EXTI_Falling_Callback函数使用的是 __weak弱定义 声明的,为保证HAL库的完整性请不要去修改或者删除它)
12.5.2.5.2. 重新编写HAL_GPIO_EXTI_Rising_Callback和HAL_GPIO_EXTI_Falling_Callback函数¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include "./exit/bsp_exit.h"
#include "./led/bsp_led.h"
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
//判断是按键1产生的中断还是按键2
if(KEY1_Pin == GPIO_Pin)
{
LED1_TOGGLE;
}
if(KEY2_Pin == GPIO_Pin)
{
LED2_TOGGLE;
}
}
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
if(KEY2_Pin == GPIO_Pin)
{
LED2_TOGGLE;
}
}
|
在中断函数执行过程中:EXTI7_IRQHandler或EXTI13_IRQHandler中都是调用 HAL_GPIO_EXTI_IRQHandler函数,而在HAL_GPIO_EXTI_IRQHandler函数中调用以上两个 函数,因此需要在HAL_GPIO_EXTI_Rising_Callback和HAL_GPIO_EXTI_Falling_Callback函数 区分具体是由哪个引脚产生的中断。
这两个回调函数中,实现的功能非常简单,判断是由KEY1还是KEY2产生的中断, 并翻转相对应的LED。
12.5.2.6. 主函数¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int main(void)
{
HAL_Init(); //HAL初始化 包含设置中断优先级分组,Systick初始化等
if(IS_ENGINEERING_BOOT_MODE()) //判断是否处于工程模式启动
{
SystemClock_Config(); //初始化时钟
}
MX_GPIO_Init(); //初始化GPIO以及EXIT
while (1)
{
}
}
|
由于采用中断控制LED,并不需要在main函数中添加或者修改什么内容。
12.5.3. 下载验证¶
关于如何下载程序,请参照 STM32MP157 Cortex-M4开发实验说明 章节。 运行程序后当按下KEY1时可正常控制板子上的LED1,当按下以及松开KEY2时,板子上的LED2都会发生变化。