EXTI外部中断的执行逻辑|以对射式红外传感器计次为例

对射式红外传感器计次 用到了外部中断 对射式红外传感器接在了GPIOB14上 本文详细介绍了从RCC->GPIO->AFIO->EXTI->NVIC的中断配置过程

分析部分:

RCC(时钟)的配置

RCC配置的必要性: STM32 的所有外设默认处于 "时钟关闭" 状态(低功耗设计),必须先使能时钟,外设才能响应配置和工作。若未使能 GPIOB 时钟,GPIO 配置无效;未使能 AFIO 时钟,EXTI 与 GPIO 的映射会失败,导致外部中断无法触发。

RCC配置的内容:

  • GPIOB 时钟(传感器接在 GPIOB14,需 GPIO 外设工作);

  • AFIO 时钟(用于 EXTI 与 GPIO 的映射);

  • EXTI 本身无需单独使能时钟(EXTI 属于 APB2 外设,随 APB2 时钟默认使能,但需确保 APB2 时钟已开启)。

  • NVIC是内核外设,RCC是开启外设的,内核外设不归RCC管理

    复制代码
    // 使能GPIOB和AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

GPIO(引脚配置)配置

GPIO配置内容: 将 GPIOB14 配置为输入模式(外部中断需检测引脚电平变化,必须为输入模式),根据传感器特性选择上拉 / 下拉输入:

  • 对射式红外传感器原理:无遮挡时,接收管导通,输出高电平;有遮挡时,接收管截止,输出低电平(或相反,需根据传感器 datasheet 确认)。

  • 假设传感器无遮挡时输出高电平,遮挡时输出低电平,因此配置为上拉输入 (确保无遮挡时电平稳定为高,避免浮动)。

    复制代码
    GPIO_InitTypeDef GPIO_InitStructure;
    // 配置GPIOB14为上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输入模式下速度无意义,可随便填
    GPIO_Init(GPIOB, &GPIO_InitStructure);

AFIO(复用功能配置)配置【AFIO的作用:建立 EXTI 与 GPIO 的 "映射关系"】

AFIO配置内容:EXTI(外部中断控制器)有 16 条中断线(EXTI0~EXTI15),每条线可对应多个 GPIO 引脚(如 EXTI14 可对应 GPIOA14、GPIOB14、GPIOC14 等)。需通过 AFIO 配置EXTI14 与 GPIOB14 的映射,告诉 EXTI:"监测 GPIOB14 的电平变化"。

复制代码
// 配置EXTI14映射到GPIOB14
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

AFIO配置的必要性: EXTI 本身不直接连接到具体 GPIO 端口(如 A/B/C),而是通过 AFIO 动态映射。若不配置 AFIO,EXTI14 会默认监测 GPIOA14(或其他端口),而非 GPIOB14,导致传感器信号无法触发中断。

EXTI(外部中断控制器)配置【EXTI的作用:定义"中断触发规则",开启中断的开关】

**EXTI配置的内容:**配置 EXTI14 的触发方式(上升沿 / 下降沿 / 双边沿)、中断使能,明确 "什么情况下产生中断请求":

  • 对射式传感器计次:通常统计 "遮挡次数",即当遮挡发生时(电平从高→低)触发中断,因此选择下降沿触发

    复制代码
    EXTI_InitTypeDef EXTI_InitStructure;
    // 配置EXTI14
    EXTI_InitStructure.EXTI_Line = EXTI_Line14; // 对应GPIOB14
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式(区别于事件模式)
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发(遮挡时)
    EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能EXTI14
    EXTI_Init(&EXTI_InitStructure);

EXTI配置的必要性:

  • EXTI 是 "中断触发的开关",需明确触发边沿(否则无法判断何时产生中断)。
  • 必须设置为 "中断模式"(EXTI_Mode_Interrupt),才能向 CPU 发送中断请求(事件模式仅触发硬件联动,不产生 CPU 中断)。

NVIC(嵌套向量中断控制器)的配置【NVIC的作用就是 让cpu"响应中断"】

**NVIC配置的内容:**EXTI 产生中断请求后,需通过 NVIC 配置中断优先级并使能,确保 CPU 能 "感知" 并处理该中断:

  • STM32 中,EXTI10~EXTI15 共用一个中断通道(EXTI15_10_IRQn),因此需配置该通道。

    复制代码
    /*1. 先设置中断优先级分组(全局生效)
    指定抢占优先级和子优先级各占多少位例如:
    
    NVIC_PriorityGroup_2 表示:抢占优先级占 2 位,子优先级占 2 位。
    此时,抢占优先级可选值为 0~3(2 位共 4 种),子优先级可选值也为 0~3。
    抢占优先级:高抢占优先级的中断可以打断低抢占优先级的中断(实现中断嵌套)。
    子优先级:当多个中断的抢占优先级相同时,子优先级高的中断先响应(不嵌套,仅决定响应顺序)。*/
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //2. 再配置具体中断通道的优先级
    NVIC_InitTypeDef NVIC_InitStructure;
    // 配置EXTI15_10中断通道
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; // EXTI10~15共用此通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级(根据需求设置)
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能该中断通道
    NVIC_Init(&NVIC_InitStructure);

所有 NVIC 中断配置之前必须先配置NVIC_PriorityGroupConfig,它是 NVIC 配置的全局基础设置。NVIC_PriorityGroupConfig一次配置,全局统一,不可中途修改分组

其作用是:

  1. 定义抢占优先级和子优先级的位数分配;
  2. 决定后续 NVIC_IRQChannelPreemptionPriorityNVIC_IRQChannelSubPriority 的有效范围;
  3. 必须在具体中断通道配置(NVIC_Init())之前调用,否则会导致优先级配置无效。

NVIC配置的必要性若不配置 NVIC,即使 EXTI 产生了中断请求,CPU 也会忽略该请求,无法执行中断服务函数。

中断服务函数:实现计次逻辑

**中断服务函数的内容:**需编写 EXTI15_10 的中断服务函数,在中断中完成计次(需清除 EXTI 中断标志位,避免重复触发):

复制代码
uint32_t count = 0; // 计次变量

void EXTI15_10_IRQHandler(void) {
    // 检查是否是EXTI14触发的中断
    if (EXTI_GetITStatus(EXTI_Line14) =SET) {////判断是否是外部中断14号线触发的中断,因为这个函数EXTI15_10都能进来
        count++; // 遮挡次数+1
        EXTI_ClearITPendingBit(EXTI_Line14); // 清除中断标志位(必须!)
    }
}

计次函数

复制代码
uint16_t CountSensor_Get(void)
{
	return Count;
}

函数部分

countSensor.c部分

复制代码
#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;				//全局变量,用于计数

/**
  * 函    数:计数传感器初始化
  * 参    数:无
  * 返 回 值:无
  */

//整个外部中断的配置
void CountSensor_Init(void)
{
	/*开启时钟,不开启时钟,外设是没有办法进行工作的*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	//EXTI和NVIC这两个外设的时钟,是一直打开着的,不需要我们开启时钟。
	//NVIC是内核内的外设,RCC管理的是内核外的外设,管不着NVIC。
	
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//红外接到了PB14
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB14引脚初始化为上拉输入
	
	
	/*AFIO配置外部中断引脚选择*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);// 配置EXTI14映射到GPIOB14
	
	
	/*EXTI初始化,选择触发方式{上升沿、下降沿、双边沿}和触发响应方式{中断响应、事件响应}*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;					//选择配置外部中断的14号线
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			 // 中断模式(区别于事件模式)
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发,因为GPIOMode是配置的上拉输入模式
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组,给我们的中断选择一个合适的优先级,最后
	通过NVIC,外部中断信号进入cpu;
	NVIC是内核外设,所以它的库函数在misc.h中*/
	
	//先配置一下NVIC的优先级分组。
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅能选择一种
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若封装在各个模块中,需确保每个封装里选择的都是同一个
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		//STM32 中,EXTI10~EXTI15 共用一个中断通道(EXTI15_10_IRQn),因此需配置该通道。
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

/**
  * 函    数:获取计数传感器的计数值
  * 参    数:无
  * 返 回 值:计数值,范围:0~65535
  */
uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

/**
  * 函    数:EXTI15_10外部中断函数
  * 参    数:无//中断函数都是无参无返回值的
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制:从startup------stm32f10x_md.s
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)		//判断是否是外部中断14号线触发的中断,因为这个函数EXTI15_10都能进来
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;					//计数值自增一次
		}
		//EXTI_ClearITPendingBit:清除EXTI线路挂起位
		EXTI_ClearITPendingBit(EXTI_Line14);		//中断程序结束后,清除外部中断14号线的挂起位;
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

main.c部分

复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	CountSensor_Init();		//计数传感器初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Count:");	//1行1列显示字符串Count:
	
	while (1)
	{
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);		//OLED不断刷新显示CountSensor_Get的返回值
	}
}
相关推荐
Aczone288 小时前
硬件(五) 存储、ARM 架构与指令系统
arm开发·嵌入式硬件·架构
LS·Cui8 小时前
单片机按键示例功能
单片机
【ql君】qlexcel8 小时前
MCU上电到运行的全过程
单片机·嵌入式硬件·mcu·启动过程
搞一搞汽车电子8 小时前
S32K3平台eMIOS 应用说明
开发语言·驱动开发·笔记·单片机·嵌入式硬件·汽车
pQAQqa9 小时前
FreeRTOS项目(2)摇杆按键检测
stm32·单片机·嵌入式硬件·freertos
小莞尔10 小时前
【51单片机】【protues仿真】基于51单片机停车场的车位管理系统
c语言·开发语言·单片机·嵌入式硬件·51单片机
一川月白70911 小时前
51单片机---硬件学习(跑马灯、数码管、外部中断、按键、蜂鸣器)
单片机·学习·51单片机·外部中断·蜂鸣器·数码管·跑马灯
月阳羊11 小时前
【硬件-笔试面试题-69】硬件/电子工程师,笔试面试题(知识点:电机驱动电路的反馈电路)
java·经验分享·嵌入式硬件·面试
weixin_4684668514 小时前
树莓派32位与64位系统安装teamviewer远程软件
linux·单片机·自动化·树莓派·远程控制·vnc·teamviewer
沐欣工作室_lvyiyi14 小时前
2025-2026单片机物联网毕业设计题目推荐(定稿付款)
单片机·物联网·课程设计