【STM32】外部中断(下)
【STM32】外部中断(下)
- 前言
- 一、旋转编码器计次(附源码)
- 1.1 为什么要使用外部中断
- 1.2 旋转编码器定义
- 1.3 GPIO、EXTI、NVIC标准库函数
- 1.4 旋转编码器计次程序
- 二、对射式红外传感器计次(附源码)
- 2.1 对射式红外传感器
- 2.2 对射式红外传感器程序
前言
本篇文章是接【STM32】外部中断(上)继续讲解外部中断,主要是使用旋转编码器与对射红外传感器运用外部中断的方式进行计次。
一、旋转编码器计次(附源码)
1.1 为什么要使用外部中断
- Q: 什么样的设备需要用到外部中断,使用外部中断的好处?
- A: 对于STM32,想要获取的信号是外部驱动,很快的突发信号,比如旋转编码器的输出信号,它产生的信号是突发的,32只能被动读取,如果不及时就会错过许多信号,所以要有脉冲过来,32立即进入中断函数处理。
1.2 旋转编码器定义
定义:用于测量位置、速度或旋转方向的装置,当其旋转轴被旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取对方波信号的频率和相位信息即可得知旋转轴的速度与方向。
1.3 GPIO、EXTI、NVIC标准库函数
配置过程
- ① 配置 RCC,把涉及的外设时钟都打开 GPIO B,AFIO,(NVIC,EXTI无需)
- ② 配置 GPIO,选择端口为输入模式
- ③ 配置 AFIO,选择我们用的 GPIO 连接 EXTI
- ④ 配置 EXTI,选择边沿触发方式,以及响应方式(中断响应)
- ⑤ 配置 NVIC,选择合适的优先级
最后,通过 NVIC,外部中断信号进入 CPU
1.4 旋转编码器计次程序
旋转编码器计次接线图
源代码
- Encoder.h模块代码
#ifndef __ENCODER_H
#define __ENCODER_Hvoid Encoder_Init(void);
int16_t Encoder_Get(void);#endif
- Encoder.c模块代码
#include "stm32f10x.h" // Device headerint16_t Encoder_Count; //全局变量,用于计数旋转编码器的增量值void Encoder_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB0和PB1引脚初始化为上拉输入/*AFIO选择中断引脚*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚/*EXTI初始化*/EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1; //选择配置外部中断的0号线和1号线EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //选择配置NVIC的EXTI0线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //选择配置NVIC的EXTI1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //指定NVIC线路的响应优先级为2NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
}/*** 函 数:旋转编码器获取增量值* 参 数:无* 返 回 值:自上此调用此函数后,旋转编码器的增量值*/
int16_t Encoder_Get(void)
{/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*//*在这里,也可以直接返回Encoder_Count但这样就不是获取增量值的操作方法了也可以实现功能,只是思路不一样*/int16_t Temp;Temp = Encoder_Count;Encoder_Count = 0;return Temp;
}/*** 函 数:EXTI0外部中断函数* 参 数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行* 函数名为预留的指定名称,可以从启动文件复制* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void EXTI0_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line0) == SET) //判断是否是外部中断0号线触发的中断{/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向{Encoder_Count --; //此方向定义为反转,计数变量自减}}EXTI_ClearITPendingBit(EXTI_Line0); //清除外部中断0号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}
}/*** 函 数:EXTI1外部中断函数* 参 数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行* 函数名为预留的指定名称,可以从启动文件复制* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void EXTI1_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断是否是外部中断1号线触发的中断{/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向{Encoder_Count ++; //此方向定义为正转,计数变量自增}}EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}
}
- main.c模块代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"int16_t Num; //定义待被旋转编码器调节的变量int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化Encoder_Init(); //旋转编码器初始化/*显示静态字符串*/OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:while (1){Num += Encoder_Get(); //获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上OLED_ShowSignedNum(1, 5, Num, 5); //显示Num}
}
二、对射式红外传感器计次(附源码)
2.1 对射式红外传感器
对射式红外传感器是一种利用红外线发射与接收的遮挡关系实现检测功能的电子元件,核心作用是判断“目标是否挡住特定路径”,广泛用于计数、避障、位置检测等场景(如打印机纸张检测、自动门感应、单片机项目避障等)。
原理核心是“光线是否被遮挡”,流程仅3步:
- 通路建立:发射器通电发射红外光,接收器正对发射器,此时接收器能稳定接收到光线,输出“无遮挡信号”(如低电平)。
- 遮挡触发:当有物体(如纸张、金属、手等)从发射器与接收器之间穿过时,红外光线被挡住,接收器接收不到光线。
- 信号输出:接收器检测到“光线消失”后,立即改变输出电平(如从低电平变为高电平),这个电平变化可传递给单片机、PLC等控制器,触发后续动作(如计数+1、设备暂停等)。
2.2 对射式红外传感器程序
对射式红外传感器接线图
源代码
- CountSensor.h模块代码
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_Hvoid CountSensor_Init(void);
uint16_t CountSensor_Get(void);#endif
- CountSensor.c模块代码
#include "stm32f10x.h" // Device headeruint16_t CountSensor_Count; //全局变量,用于计数/*** 函 数:计数传感器初始化* 参 数:无* 返 回 值:无*/
void CountSensor_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB14引脚初始化为上拉输入/*AFIO选择中断引脚*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚/*EXTI初始化*/EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量EXTI_InitStructure.EXTI_Line = EXTI_Line14; //选择配置外部中断的14号线EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设/*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; //选择配置NVIC的EXTI15_10线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
}/*** 函 数:获取计数传感器的计数值* 参 数:无* 返 回 值:计数值,范围:0~65535*/
uint16_t CountSensor_Get(void)
{return CountSensor_Count;
}/*** 函 数:EXTI15_10外部中断函数* 参 数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行* 函数名为预留的指定名称,可以从启动文件复制* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void EXTI15_10_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line14) == SET) //判断是否是外部中断14号线触发的中断{/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0){CountSensor_Count ++; //计数值自增一次}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的返回值}
}
关于【STM32】外部中断(下)的讲解就到这里,希望对你有所帮助,感谢观看ovo!