STM32的外部中断及函数封装

STM32 的外部中断(External Interrupt)是一种由外部硬件信号触发的中断机制,用于响应外部事件(如按键按下、外部传感器信号、信号变化等)。STM32 提供了灵活的中断管理机制,通过使用外部中断,可以有效地处理外部硬件信号的变化,而无需持续轮询。

1. 外部中断的基本概念

外部中断通常由外部硬件(例如按钮、传感器、开关、信号源等)触发。STM32 的外部中断可以由以下方式触发:

  • 上升沿触发(例如按键按下的信号变化)
  • 下降沿触发(例如按键释放的信号变化)
  • 电平触发(高电平或低电平)

2. STM32 中的外部中断管理

STM32 使用 外部中断控制器(EXTI) 来管理外部中断。每个外部引脚(如 GPIO 引脚)都可以与 EXTI 线相连,STM32 提供了多个中断通道(取决于具体的 STM32 系列),包括16个GPIO_PIN,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒等。

支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断,因为同一PIN口的GPIO引脚都相连于同一EXIT通道。

3. STM32 的 EXTI 控制器

STM32 的 EXTI 控制器可以连接多个 GPIO 引脚,每个引脚可以配置成外部中断线。在 STM32F1 系列中,EXTI 控制器支持最多 16 条中断线,其他系列的 STM32 可能支持更多的中断线。

3.1. 中断线路与引脚的对应关系

  • 每个外部引脚(例如,GPIOA、GPIOB、GPIOC 等)可以连接到对应的 EXTI 线。
  • EXTI 线通过外部引脚的输入信号触发中断。例如,GPIOA 的 0 引脚可以触发 EXTI0 中断,GPIOB 的 1 引脚可以触发 EXTI1 中断,以此类推。

4. 外部中断的配置过程

在 STM32 中配置外部中断的流程包括以下几个步骤:

4.1. GPIO 配置

首先,需要对目标引脚进行配置,确保它作为输入引脚并且可以触发外部中断。例如,配置 GPIO 为输入模式,启用上拉或下拉电阻(根据需要)。

GPIO_InitTypeDef GPIO_InitStruct = {0};

// 假设我们使用的是 GPIOA 的 0 引脚
__HAL_RCC_GPIOA_CLK_ENABLE();  // 启用 GPIOA 时钟

// 配置 GPIOA0 为输入,并启用上拉电阻
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;  // 上升沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLUP;  // 启用上拉电阻
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

4.2. EXTI 配置

配置引脚对应的 EXTI 线。STM32 提供了 HAL_NVIC_SetPriority()HAL_NVIC_EnableIRQ() 来配置中断的优先级,并使能相应的中断线。

// 设置外部中断优先级,并使能 EXTI0 中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);  // 优先级 0
HAL_NVIC_EnableIRQ(EXTI0_IRQn);  // 使能 EXTI0 中断

4.3. 中断处理函数

每当外部中断触发时,STM32 会自动跳转到与之对应的中断服务程序(ISR)。例如,EXTI0 中断会触发 EXTI0_IRQHandler() 函数。这个函数会调用 HAL_GPIO_EXTI_IRQHandler() 来处理具体的中断事件。

void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);  // 调用 HAL 库的中断处理函数
}
这两个函数 EXTI0_IRQHandler()HAL_GPIO_EXTI_Callback()都是在 STM32 的头文件中进行了声明,而 没有 在库中提供具体的实现。

HAL_GPIO_EXTI_IRQHandler() 中,调用了 HAL_GPIO_EXTI_Callback() 回调函数,允许用户在其中编写自己的中断处理代码。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_0)  // 如果是 GPIOA0 引脚触发的中断
    {
        // 执行按键按下的处理逻辑
    }
}
HAL_GPIO_EXTI_Callback() 库中唯一的回调函数,它由 HAL 库提供并在外部中断发生时被自动调用。你不需要创建多个回调函数来处理不同的中断线。相反,你只需在这个函数内根据不同的 GPIO_Pin 参数来区分和处理不同的外部中断

4.4. 中断处理总结

当外部中断发生时,STM32的中断控制器会调用中断向量表中与该中断相关的中断服务程序(ISR)。

例如,如果你配置了 PA0 为外部中断,并且发生了中断,STM32的硬件会自动触发 EXTI0_IRQHandler(),而 EXTI0_IRQHandler() 会调用 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0)

然后,HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0) 会调用 HAL_GPIO_EXTI_Callback(),并将 GPIO_PIN_0 作为参数传递给回调函数。这时,用户的回调函数 HAL_GPIO_EXTI_Callback() 会根据 GPIO_Pin 来执行特定的操作。

5. EXTI 中断的触发方式

STM32 的外部中断可以通过以下几种方式进行触发:

  • 上升沿触发(Rising edge):当信号从低电平跳变到高电平时触发中断。
  • 下降沿触发(Falling edge):当信号从高电平跳变到低电平时触发中断。
  • 双沿触发(Both edges):当信号在两个边沿(上升沿和下降沿)时都会触发中断。
  • 电平触发(Level trigger):当信号处于高电平或低电平时,持续触发中断,直到电平变化。

在 STM32 中配置引脚的方式决定了触发方式。例如,使用 GPIO_MODE_IT_RISING 配置上升沿触发,GPIO_MODE_IT_FALLING 配置下降沿触发。

6. 外部中断的中断服务程序(ISR)

STM32 中断处理程序通过中断向量表来触发。当外部中断发生时,STM32 会跳转到对应的中断服务程序(ISR)。对于 EXTI0 中断,ISR 为 EXTI0_IRQHandler()。在该函数中,用户可以使用 HAL_GPIO_EXTI_IRQHandler() 来调用 HAL 库中的中断处理程序。

7. 外部中断的优先级

STM32 允许用户设置中断的优先级。每个中断都可以被分配一个优先级,优先级越低的中断会先执行。用户可以使用 HAL_NVIC_SetPriority() 函数来设置中断优先级,并使用 HAL_NVIC_EnableIRQ() 来使能中断。

8. 外部中断的中断嵌套

STM32 支持中断嵌套,即在处理中断时,低优先级的中断可以被高优先级的中断打断。可以通过配置 NVIC(嵌套向量中断控制器)来控制中断的优先级。

9. 总结

STM32 外部中断的基本流程包括:

  1. 配置 GPIO 引脚为输入模式,并设置中断触发条件。
  2. 启用外部中断的时钟和中断线路。
  3. 配置中断优先级和中断服务程序。
  4. 在中断回调函数中编写自己的业务逻辑。

通过外部中断,STM32 可以高效地响应外部事件,而无需轮询,减少了处理器的负担。

10. 中断函数封装示例

如果我们希望让代码能顺利移植到其他型号的STM单片机,我们可以对配置中断的模块进行封装,接下来以一个按键(KEY1)进行举例

10.1. 新建.c / .h文件

为了增强代码的易读性,我们对于每个模块应当在头文件中申明函数,在.c文件中实现其功能。我们可以在工程文件中新建一个文件夹(KEY1-Drive),里面新建好key1_driver.ckey1_driver.h,并在Keil uVision5中新建一个组(GROUP),用来管理这个文件夹。

10.2. 编辑头文件

key1.h

  • 定义了按键 KEY1 相关的宏,包括 GPIO 端口、引脚、时钟使能函数等。
  • KEY1_Init() 函数用于按键初始化,配置 PA1 为上升沿触发中断。
  • KEY1_IRQHandler() 是外部中断服务程序,用于处理中断。
  • HAL_GPIO_EXTI_Callback() 是 HAL 库提供的回调函数,用于按键中断处理。
#ifndef __KEY1_H__
#define __KEY1_H__

#include "stm32f1xx_hal.h"  // 适配 STM32F1 库,若使用其他系列的 STM32,修改为对应的 HAL 库

// 按键 KEY1 的引脚和端口定义
#define KEY1_GROUP              GPIOA               // 按键连接的端口
#define KEY1_CLK_ENABLE()       __HAL_RCC_GPIOA_CLK_ENABLE()  // 启用 GPIOA 时钟
#define KEY1_PIN                GPIO_PIN_1          // 按键连接的引脚

// 外部中断处理函数原型
void KEY1_Init(void);               // 按键初始化
define KEY1_IRQHandler EXTI1_IRQHandler        // 外部中断服务函数

#endif /* __KEY1_H__ */

10.3. 编辑.c文件

在.c文件中,我们应当完成以下流程

  • KEY1_Init():初始化 GPIO 和中断,设置 PA1 为输入引脚,并启用上拉电阻。该引脚通过上升沿触发外部中断。
  • EXTI1_IRQHandler():中断处理程序。该函数由 STM32 自动调用,负责触发 HAL 库的中断处理函数 HAL_GPIO_EXTI_IRQHandler()
  • HAL_GPIO_EXTI_Callback():由 HAL_GPIO_EXTI_IRQHandler() 调用。在此函数中,你可以编写中断触发时的业务逻辑。
#include "key1.h"

// 按键初始化函数
void KEY1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 启用 GPIOA 时钟
    KEY1_CLK_ENABLE();

    // 配置 PA1 为输入模式,上升沿触发中断
    GPIO_InitStruct.Pin = KEY1_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;  // 上升沿触发
    GPIO_InitStruct.Pull = GPIO_PULLUP;          // 启用上拉电阻
    HAL_GPIO_Init(KEY1_GROUP, &GPIO_InitStruct);

    // 配置中断优先级并使能 EXTI1 中断
    HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0);  // 优先级 0
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);          // 使能 EXTI1 中断
}

// 外部中断服务程序 (ISR)
void EXTI1_IRQHandler(void)
{
    // 调用 HAL 库的中断处理函数
    HAL_GPIO_EXTI_IRQHandler(KEY1_PIN);
}

// 按键中断回调函数 (用户可根据需求编写处理逻辑)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == KEY1_PIN)  // 判断是否是 KEY1 引脚触发的中断
    {
        // 在此处编写按键按下时的处理逻辑,例如:
        // 可以设置一个标志位,或者调用其他函数
        // 例如:Toggle_LED();  // 假设有一个LED切换函数
    }
}

10.4. 在mian函数中进行调用

在主程序中调用 KEY1_Init() 初始化按键:

int main(void)
{
    HAL_Init();  // 初始化 HAL 库
    KEY1_Init(); // 初始化按键
    while (1)
    {
        // 主循环中的其他任务
    }
}

当按下 KEY1 按键并触发上升沿时,会调用 HAL_GPIO_EXTI_Callback() 回调函数,可以在其中添加按键事件的处理逻辑。

上一篇
下一篇