13-EXIT

1 概念

外部中断/事件控制器(EXTI)是 STM32 系列微控制器中的一个重要模块,负责管理控制器的 23 条中断/事件线。

  • 中断/事件线
    • EXTI 提供了 23 条中断/事件线,每条线可以独立地进行配置和管理。这使得开发者可以灵活地选择哪些引脚作为中断源。
  • 边沿检测
    • 每条中断/事件线都配备有边沿检测器,可以实现输入信号的上升沿和下降沿检测。这意味着可以对信号变化进行响应,例如检测按钮按下或释放的瞬间。
  • 独立配置
    • EXTI 允许对每个中断/事件线进行单独配置
      • 中断或事件选择: 每条线可以被配置为触发中断或作为事件处理。
      • 触发方式: 可以设置为上升沿触发、下降沿触发或双边沿触发,灵活应对不同的应用场景。

2 EXTI 功能框图

STM32F429 上 EXTI 是在 APB2 总线上的。

图中23 表示表示在控制器内部类似的信号线路有 23 个。

外部中断/事件控制器(EXTI)在 STM32 微控制器中具有两个主要功能:产生中断和产生事件。

2.1 中断产生流程

  • 输入线(编号 1): EXTI 控制器有 23 个中断/事件输入线,这些输入线可以通过寄存器配置为任意 GPIO 引脚或某些外设的事件源。这些输入线主要用于监测电平变化的信号。
  • 边沿检测电路(编号 2): 该电路根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)的配置来检测信号的边沿变化。如果检测到边沿跳变,电路会输出有效信号(1),否则输出无效信号(0)。EXTI_RTSR 和 EXTI_FTSR 的设置允许选择检测上升沿、下降沿或双边沿。
  • 或门电路(编号 3): 该电路的一个输入来自边沿检测电路,另一个输入来自软件中断事件寄存器(EXTI_SWIER)。
    • EXTI_SWIER 允许通过程序控制来启动中断/事件线。若任一输入为 1,或门输出为 1。
  • 与门电路(编号 4): 该电路的输入为编号 3 的输出和中断屏蔽寄存器(EXTI_IMR)。只有当 EXTI_IMR 设置为 1 时,编号 4 的输出才会受到编号 3 输出的影响。输出信号会被保存在挂起寄存器(EXTI_PR)中,若编号 4 输出为 1,则会将 EXTI_PR 对应位置为 1。
  • NVIC 输出(编号 5): EXTI_PR 寄存器的内容最终输出到 NVIC,实现系统中断事件的控制。

2.2 事件产生流程

  • 与门电路(编号 6): 该电路的一个输入来自编号 3,另一个输入来自事件屏蔽寄存器(EXTI_EMR)。只有当 EXTI_EMR 设置为 1 时,编号 6 的输出才会受到编号 3 输出的影响。
  • 脉冲发生器电路(编号 7): 当编号 6 的输出为有效信号(1)时,该电路会产生一个脉冲信号;若输出无效,则不会产生脉冲。
  • 脉冲信号(编号 8): 该脉冲信号是事件产生的最终结果,可以被其他外设使用,例如定时器(TIM)、模拟数字转换器(ADC)等。

2.3 中断与事件的区别

  • 中断: 产生中断的目的是将输入信号传输到 NVIC,进而执行中断服务函数,实现软件层面的响应。
  • 事件: 产生事件的目的是传输一个脉冲信号给其他外设,属于电路级别的信号传输,通常用于硬件层面的应用。

3 EXTI 中断/事件线

EXTI(外部中断/事件控制器)提供了 23 条中断/事件线,每条线都可以被配置为特定的输入源。

3.1 概述

  • EXTI 包含 23 条中断/事件线:
    • EXTI0 至 EXTI15: 这 16 条线可以通过编程配置为任意 GPIO 引脚(PA0 到 PI0),实现灵活的输入源选择。
    • EXTI16 至 EXTI22: 这 7 条线专门用于特定外设事件
中断/事件线 输入源 中断处理函数
EXTI0 PX0 (X 可为 A,B,C,D,E,F,G,H,I) KEY1_IRQHandler
EXTI1 PX1 (X 可为 A,B,C,D,E,F,G,H,I) KEY2_IRQHandler
EXTI2 PX2 (X 可为 A,B,C,D,E,F,G,H,I) EXTI2_IRQHandler
EXTI3 PX3 (X 可为 A,B,C,D,E,F,G,H,I) EXTI3_IRQHandler
EXTI4 PX4 (X 可为 A,B,C,D,E,F,G,H,I) EXTI4_IRQHandler
EXTI5 PX5 (X 可为 A,B,C,D,E,F,G,H,I) EXTI9_5_IRQHandler
EXTI6 PX6 (X 可为 A,B,C,D,E,F,G,H,I) EXTI9_5_IRQHandler
EXTI7 PX7 (X 可为 A,B,C,D,E,F,G,H,I) EXTI9_5_IRQHandler
EXTI8 PX8 (X 可为 A,B,C,D,E,F,G,H,I) EXTI9_5_IRQHandler
EXTI9 PX9 (X 可为 A,B,C,D,E,F,G,H,I) EXTI9_5_IRQHandler
EXTI10 PX10 (X 可为 A,B,C,D,E,F,G,H,I) EXTI15_10_IRQHandler
EXTI11 PX11 (X 可为 A,B,C,D,E,F,G,H,I) EXTI15_10_IRQHandler
EXTI12 PX12 (X 可为 A,B,C,D,E,F,G,H,I) EXTI15_10_IRQHandler
EXTI13 PX13 (X 可为 A,B,C,D,E,F,G,H,I) EXTI15_10_IRQHandler
EXTI14 PX14 (X 可为 A,B,C,D,E,F,G,H,I) EXTI15_10_IRQHandler
EXTI15 PX15 (X 可为 A,B,C,D,E,F,G,H) EXTI15_10_IRQHandler
EXTI16 可编程电压检测器 (PVD) 输出 PVD_IRQHandler
EXTI17 RTC 闹钟事件 RTC_Alarm_IRQHandler
EXTI18 USB OTG FS 唤醒事件 OTG_FS_WKUP_IRQHandler
EXTI19 以太网唤醒事件 ETH_WKUP_IRQHandler
EXTI20 USB OTG HS(在 FS 中配置)唤醒事件 OTG_HS_WKUP_IRQHandler
EXTI21 RTC 入侵和时间戳事件 TAMP_STAMP
EXTI22 RTC 唤醒事件 RTC_WKUP_IRQHandler

3.2 GPIO 配置

EXTI0 至 EXTI15 的每条中断/事件线可以通过 SYSCFG 外部中断配置寄存器 1(SYSCFG_EXTICR1)进行配置。

例如,EXTI0 可以通过 EXTI0[3:0] 位选择配置为 PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或 PI0。其余 EXTI 线的配置类似。

3.3 特定外设事件

EXTI 的其他 7 条中断/事件线(EXTI16 至 EXTI22)由特定外设触发。这些线用于响应外设事件,例如 RTC(实时时钟)、USB OTG、以太网等。

4 EXTI 初始化结构体

4.1  初始化结构体

typedef struct {
    uint32_t EXTI_Line;              // 中断/事件线
    EXTIMode_TypeDef EXTI_Mode;      // EXTI 模式
    EXTITrigger_TypeDef EXTI_Trigger; // 触发事件
    FunctionalState EXTI_LineCmd;     // EXTI 控制
} EXTI_InitTypeDef;
  1. EXTI_Line: 选择要配置的 EXTI 中断/事件线,可以选择 EXTI0 至 EXTI22。
  2. EXTI_Mode: 配置 EXTI 的模式,可以选择产生中断(EXTI_Mode_Interrupt)或产生事件(EXTI_Mode_Event)。
  3. EXTI_Trigger: 配置 EXTI 的触发方式,可以选择:
    • 上升沿触发(EXTI_Trigger_Rising)
    • 下降沿触发(EXTI_Trigger_Falling)
    • 上升沿和下降沿都触发(EXTI_Trigger_Rising_Falling)
  4. EXTI_LineCmd: 控制是否使能该 EXTI 线,可以选择使能(ENABLE)或禁用(DISABLE)。

5 中断实验

本实验采用野火STM32F429IGTb V2 开发板,以下代码来自野火:
https://gitee.com/Embedfire-stm32f429-tiaozhanzhe/ebf_stm32f429_tiaozhanzhe_std_code

5.1 硬件设计

5.2 前提准备

创建两个文件:exti.c 和 exti.h 文件用来存放 EXTI 驱动程序及相关宏定义,中断服务函数放在 stm32f4xx_it.h 文件中。

5.3 exti.h

#ifndef __EXTI_H
#define __EXTI_H

#include "stm32f4xx.h"

// KEY1 相关定义
#define KEY1_INT_GPIO_PORT                GPIOA                       /*!< KEY1 中断 GPIO 端口 */
#define KEY1_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOA       /*!< KEY1 GPIO 时钟使能 */
#define KEY1_INT_GPIO_PIN                 GPIO_Pin_0                 /*!< KEY1 中断引脚 */
#define KEY1_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOA       /*!< KEY1 EXTI 端口源 */
#define KEY1_INT_EXTI_PINSOURCE           EXTI_PinSource0            /*!< KEY1 EXTI 引脚源 */
#define KEY1_INT_EXTI_LINE                EXTI_Line0                  /*!< KEY1 EXTI 线 */
#define KEY1_INT_EXTI_IRQ                 EXTI0_IRQn                  /*!< KEY1 EXTI 中断请求号 */

#define KEY1_IRQHandler                   EXTI0_IRQHandler            /*!< KEY1 中断处理函数 */

// KEY2 相关定义
#define KEY2_INT_GPIO_PORT                GPIOC                       /*!< KEY2 中断 GPIO 端口 */
#define KEY2_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOC       /*!< KEY2 GPIO 时钟使能 */
#define KEY2_INT_GPIO_PIN                 GPIO_Pin_13                /*!< KEY2 中断引脚 */
#define KEY2_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOC       /*!< KEY2 EXTI 端口源 */
#define KEY2_INT_EXTI_PINSOURCE           EXTI_PinSource13           /*!< KEY2 EXTI 引脚源 */
#define KEY2_INT_EXTI_LINE                EXTI_Line13                 /*!< KEY2 EXTI 线 */
#define KEY2_INT_EXTI_IRQ                 EXTI15_10_IRQn              /*!< KEY2 EXTI 中断请求号 */

#define KEY2_IRQHandler                   EXTI15_10_IRQHandler        /*!< KEY2 中断处理函数 */

// 函数声明
void EXTI_Key_Config(void);              /*!< 配置外部中断的函数 */

#endif /* __EXTI_H */

5.4 exti.c

#include "./key/bsp_exti.h"

static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置 NVIC 为优先级组 1
  
    // 配置中断源:按键 1
    NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 配置抢占优先级:1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 配置子优先级:1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
    NVIC_Init(&NVIC_InitStructure);
  
    // 配置中断源:按键 2,其他使用上面相关配置  
    NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
    NVIC_Init(&NVIC_InitStructure);
}

void EXTI_Key_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; 
    EXTI_InitTypeDef EXTI_InitStructure;
  
    RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK | KEY2_INT_GPIO_CLK, ENABLE); // 开启按键 GPIO 口的时钟
  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // 使能 SYSCFG 时钟,使用 GPIO 外部中断时必须使能 SYSCFG 时钟
  
    NVIC_Configuration(); // 配置 NVIC
  
    // 选择按键 1 的引脚 
    GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 设置引脚为输入模式
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // 设置引脚不上拉也不下拉
    GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); // 使用上面的结构体初始化按键 

    SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); // 连接 EXTI 中断源到 key1 引脚

    EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE; // 选择 EXTI 中断源
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能中断/事件线
    EXTI_Init(&EXTI_InitStructure);
  
    // 选择按键 2 的引脚 
    GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;  
    GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure); // 其他配置与上面相同

    SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE); // 连接 EXTI 中断源到 key2 引脚

    EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE; // 选择 EXTI 中断源
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能中断/事件线
    EXTI_Init(&EXTI_InitStructure);
}

5.5 stm32f4xx_it.h

#include "stm32f4xx_it.h"
#include "./led/bsp_led.h"
#include "./key/bsp_exti.h"

void KEY1_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		// LED1 取反		
		LED1_TOGGLE;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}

void KEY2_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) 
	{
		// LED2 取反		
		LED2_TOGGLE;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);     
	}  
}

5.6 main.c

#include "stm32f4xx.h"
#include "./led/bsp_led.h"
#include "./key/bsp_exti.h"
#include "core_cm4.h"

// 函数声明
void Delay(__IO uint32_t nCount); 

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
    // 初始化 LED 相关的 GPIO 端口
    LED_GPIO_Config();
  
    // 初始化外部中断(EXTI),配置按键的中断功能
    // 按下按键时会触发中断,进入相应的中断处理函数
    // 中断处理函数在 stm32f4xx_it.c 文件中定义,具体为 KEY1_IRQHandler 和 KEY2_IRQHandler
    // 这些处理函数负责处理中断事件,比如反转 LED 灯的状态
    EXTI_Key_Config(); 
    
    // 主循环,程序运行在此循环中
    // 由于使用中断方式,CPU 不需要轮询按键状态,节省了 CPU 资源
    while (1)                            
    {
        // 主循环可以执行其他任务或进入低功耗模式
    }
}

// 简单的延时函数,未使用本代码中,但可以用于其他地方
void Delay(__IO uint32_t nCount)     
{
    for (; nCount != 0; nCount--); // 循环计数,消耗时间
}

发表评论