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的返回值
	}
}
相关推荐
fengfuyao9853 小时前
STM32如何定位HardFault错误,一种实用方法
stm32·单片机·嵌入式硬件
keer_zu5 小时前
STM32L051 RTC闹钟配置详解
stm32·嵌入式硬件
AI精钢5 小时前
H20芯片与中国的科技自立:一场隐形的博弈
人工智能·科技·stm32·单片机·物联网
etcix8 小时前
implement copy file content to clipboard on Windows
windows·stm32·单片机
谱写秋天8 小时前
在STM32F103上进行FreeRTOS移植和配置(STM32CubeIDE)
c语言·stm32·单片机·freertos
globbo12 小时前
【嵌入式STM32】I2C总结
单片机·嵌入式硬件
玖別ԅ(¯﹃¯ԅ)13 小时前
SysTick寄存器(嘀嗒定时器实现延时)
stm32·单片机·嵌入式硬件
limitless_peter13 小时前
集成运算放大器(反向比例,同相比例)
嵌入式硬件·硬件工程
Blossom.11814 小时前
把 AI 推理塞进「 8 位 MCU 」——0.5 KB RAM 跑通关键词唤醒的魔幻之旅
人工智能·笔记·单片机·嵌入式硬件·深度学习·机器学习·搜索引擎