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功能框图如下 。

图 11‑1 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‑1 EXTI中断/事件线

图 11‑2 EXTI0输入源选择

12.4. EXTI初始化详解

HAL库函数的EXIT初始化非常简单,只需配置好IO口的模式,然后配置中断源、中断优先级、使能中断。

  1. HAL_NVIC_SetPriority:该函数负责EXTI中断/事件线选择,可选EXTI0至EXTI15,可参考表 17-1选择,配置优先级。

  2. HAL_NVIC_EnableIRQ:该函数负责控制使能中断。

12.5. 外部中断控制实验

中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的,

我们设计使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制LED的任务。 本次实验所用工程在之前 向工程中添加代码文件 章节的工程上所修改,设置了M4的工作时钟频率为209Mhz, LED初始化的过程以及rcc如何配置在本章节中并不涉及,重点讲解内容放在EXIT中断配置上。

12.5.1. 硬件设计

按键在按下时会使得引脚接通,通过电路设计可以使得按下时产生电平变化,见 按键电路设计。 两个按键分别连接到了PB13与PH7引脚中。

图 11‑3 按键电路设计

图 11‑3 按键电路设计

12.5.2. 软件设计

这里只讲解EXIT相关配置以及EXIT相关代码,LED初始化并没涉及到。 完整的代码请参考本章配套的工程。

12.5.2.1. 将引脚设置为EXIT模式

将KEY1所在引脚PB13设置为EXIT功能模式

图 11‑4

选择分配给M4内核。

图 11‑5

将KEY2所在引脚PH7设置为EXIT功能模式

图 11‑6

选择分配给M4内核。

图 11‑7

设置引脚标签(不是必需步骤)

图 11‑8

12.5.2.2. 设置中断触发方式

图 11‑9

此处可选择将产生中断还是事件,其中中断与事件各分为三种模式:上升沿触发、下降沿触发、双边沿触发。 默认为上升沿触发,此处设置KEY1设置为上升沿触发,KEY2为双边沿触发。

图 11‑10

选择NVIC勾选enable使能中断触发,当需要设置中断优先级时,可选择System Core中的NVIC进行设置。 在本工程中保持默认分配的中断优先级分组以及中断优先级。

图 11‑11

最后选择生成代码即可

图 11‑12

12.5.2.3. MX_GPIO_Init函数

代码清单 17‑3 EXTI中断服务函数

gpio.c
 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_RISINGGPIO_MODE_IT_RISING_FALLING , 同时使用了HAL_NVIC_SetPriority设置了中断优先级,使用HAL_NVIC_EnableIRQ开启相对应的中断。 可见EXIT的配置过程并不复杂。

12.5.2.4. EXIT中断服务函数

当中断发生时,对应的中断服务函数就会被执行,中断服务函数的入口函数在stm32mp1xx_it.c文件中, 在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函数的内容

stm32mp1xx_hal_gpio.c
 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函数定义如下所示

stm32mp1xx_hal_gpio.c
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.1. 添加EXIT中断相关文件

新建exit目录

图 11‑13 图 11‑14

新建bsp_exit.c文件

图 11‑13 图 11‑14

新建bsp_exit.h文件

图 11‑13 图 11‑14
12.5.2.5.2. 重新编写HAL_GPIO_EXTI_Rising_Callback和HAL_GPIO_EXTI_Falling_Callback函数
bsp_exit.c 中断回调函数
 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. 主函数

main.c
 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都会发生变化。