首页 > 其他分享 >stm32 EXTI外部中断(标准库)(旋转编码器计次&对射式红外传感器计次)

stm32 EXTI外部中断(标准库)(旋转编码器计次&对射式红外传感器计次)

时间:2024-08-29 20:52:36浏览次数:5  
标签:计次 NVIC 编码器 优先级 中断 void stm32 GPIO EXTI

1.理论

中断系统

1.中断

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

2.中断优先级

中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源

3.中断嵌套

中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设

使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

 NVIC基本结构

NVIC优先级分组

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

EXTI(外部中断)简介

EXTI(Extern Interrupt)外部中断

EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序

支持的触发方式:上升沿/下降沿/双边沿/软件触发

支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断

通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒

触发响应方式:中断响应/事件响应

注意

事件响应:是指触发别的外设  比如:ADC,DMA;

EXTI基本结构

EXTI框图

AFIO介绍

 旋转编码器简介

旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向 类型:机械触点式/霍尔传感器式/光栅式

2.对射式红外传感器计次代码

 GPIO采用上拉输入模式

 CountSensor.c

#include "stm32f10x.h"  

uint16_t CountSensor_Count;				//全局变量,用于计数
  
/**
* 函    数:计数传感器初始化
* 参    数:无
* 返 回 值:无
* 注    意:每一个元器件都要开启时钟(其中EXTI与NVIC是默认打开的所以不需要开启)
*/
void CountSensor_Init(void)
{
//第1步:
	
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟
	
	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);
	
//第2步:
	
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚

//第3步:	
	
	EXTI_InitTypeDef EXTI_InitStruct;//定义结构体变量
	EXTI_InitStruct.EXTI_Line=EXTI_Line14;//选择配置外部中断的14号线
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;//指定外部中断线使能
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//指定外部中断线为中断模式
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStruct);//将结构体变量交给EXTI_Init,配置EXTI外设
	
//第3步:
   
   /*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线(芯片使用的是中等密度的条件编译STM32F10X_MD)
	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外设

}


/**
  * 函    数:EXTI15_10外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */

//选择配置NVIC的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号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

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

 CountSensor.h

#ifndef __COUNTSENSOR_H
#define __COUNTSENSOR_H
void CountSensor_Init(void);
uint16_t CountSensor_Get(void);
#endif

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的返回值
	}
}

3.旋转编码器计次代码

以上两个图是旋转编码器的工作原理

 Encoder.c

#include "stm32f10x.h"   // Device header
uint16_t Encoder_Count;
void Encoder_Init(void)
{
//第1步:
	
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟
	
	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);
	
//第2步:
	
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOA,即选择PB0为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOA,即选择PB1为外部中断引脚

//第3步:	
	
	EXTI_InitTypeDef EXTI_InitStruct;//定义结构体变量
	EXTI_InitStruct.EXTI_Line=EXTI_Line1 | EXTI_Line0;//选择配置外部中断的1,0号线
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;//指定外部中断线使能
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//指定外部中断线为中断模式
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStruct);//将结构体变量交给EXTI_Init,配置EXTI外设
	
//第3步:
   
   /*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
	                                                            //即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置

	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	
	/*PB0  NVIC配置*/
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;		//选择配置NVIC的EXTI15_10线(芯片使用的是中等密度的条件编译STM32F10X_MD)
	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外设
	
	
	/*PB1  NVIC配置*/
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;		//选择配置NVIC的EXTI15_10线(芯片使用的是中等密度的条件编译STM32F10X_MD)
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设

}



/**
  * 函    数: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号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}




/**
  * 函    数:旋转编码器获取增量值
  * 参    数:无
  * 返 回 值:自上此调用此函数后,旋转编码器的增量值
  */
int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/
	/*在这里,也可以直接返回Encoder_Count
	  但这样就不是获取增量值的操作方法了
	  也可以实现功能,只是思路不一样*/
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

 Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H
int16_t Encoder_Get(void);
void Encoder_Init(void);

#endif

main.c

#include "stm32f10x.h"   // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.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
	}
}

 接线图

旋转编码器计次
对射式红外传感器计次

 相关API

1.GPIO_AFIODeInit

void GPIO_AFIODeInit(void)(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
功能:
	用来复位AFIO外设 调用该函数后AFIO外设的配置就会全部清除
参数:
    无    
返回值:
	无    

2. GPIO_PinLockConfig

void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:
	锁定GPIO引脚配置寄存器
参数:
    GPIOx:GPIOx:其中x可以为(A..G)选择GPIO外设
    GPIO_Pin:要写的端口位,该参数可以是GPIO_Pin_x的任意组合,其中x可以是(0..15)
返回值:
	无    

3. GPIO_PinRemapConfig

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
功能:
	更改指定引脚的映射
参数:
    GPIO_Remap: 选择要重新映射的引脚
    NewState:端口引脚重新映射的新状态,取值为:ENABLE或DISABLE 
返回值:
	无    

4. GPIO_EXTILineConfig

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
功能:
	选择 GPIO 管脚用作外部中断线路
参数:
    GPIO_PortSource: 选择用作外部中断线源的 GPIO 端口,取值为GPIO_PortSourceGPIOx,其中x为(A..G)
    GPIO_PinSource:待设置的外部中断线路,该参数可以是GPIO_PinSourcex,其中x可以是(0..15)  
返回值:
	无    

5.EXTI_Init

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
功能:
	根据 EXTI_InitStruct 中指定的参数初始化外设 EXTI 寄存器
参数:
    EXTI_InitStruct:指向结构 EXTI_InitTypeDef 的指针,包含了外设 EXTI 的配置信息 
返回值:
	无    

6. EXTI_GetITStatus

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
功能:
	检查指定的 EXTI 线路触发请求发生与否
参数:
    EXTI_Line:待检查 EXTI 线路的挂起位 
返回值:
	EXTI_Line 的新状态(SET 或者 RESET) 
        
    

7. EXTI_ClearITPendingBit

void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
功能:
	清除 EXTI 线路挂起位
参数:
    EXTI_Line:待清除 EXTI 线路的挂起位 
返回值:
	无 
        
       

8. NVIC_PriorityGroupConfig

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
功能:
	设置优先级分组:先占优先级和从优先级(先占就是抢先优先级,从站就是响应优先级)
参数:
    NVIC_PriorityGroup:优先级分组位长度(本次代码选择的是2为抢占2位响应 比较平均)
返回值:
	无


PriorityGroup类型
/** @defgroup Preemption_Priority_Group 
  * @{
  */

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */


9. NVIC_Init

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
功能:
	根据 NVIC_InitStruct 中指定的参数初始化外设 NVIC 寄存器
参数:
    NVIC_InitStruct:指向结构 NVIC_InitTypeDef 的指针,包含了外设 GPIO 的配置信息     
返回值:
	无    

标签:计次,NVIC,编码器,优先级,中断,void,stm32,GPIO,EXTI
From: https://blog.csdn.net/2302_79504723/article/details/141675841

相关文章

  • 关于关于STM32F103芯片RTC模块的一些注意事项
    1、首先是晶振的问题,只有外部低速晶振LSE支持VBAT供电时持续运行,LSI或者HSE均不行,所以若要求设备断电后,RTC时钟可以继续运行,一定要使用LSE晶振。2、关于LSE晶振的干扰问题,本次调试设备的过程中发现,LSE虽然正常起振,RTC也正常走时,但刚开机的时候会走的比较慢,之后逐渐稳定,通过抓取LS......
  • 我在高职教STM32——ADC电压采集与光敏电阻(3)
            大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课件......
  • 我在高职教STM32——ADC电压采集与光敏电阻(4)
            大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课......
  • 基于ST STM32G474的400W Microinverter之数位电源方案
    微型逆变器(Microinverter)是一种独立的太阳能模组,能将太阳能板产生的直流电(DC)转换为交流电(AC)。这种设备在模组阶段就能进行直流电转换和发电功率最佳化,所产生的电力以交流电形式传输,具备更高的安全性和扩充性。每个微型逆变器的输出可以合并回馈至电网或储能设备,特别适合面积较......
  • OLED显示屏详解(IIC协议0.96寸 STM32)
     目录 一、介绍 二、模块原理1.原理图2.工作原理:SSD1306显存与命令三、程序设计main.c文件oled.h文件oled.c文件四、实验效果 五、资料获取项目分享一、介绍        OLED是有机发光二极管,又称为有机电激光显示(OrganicElectroluminescenceDisplay......
  • 【STM32】IWDG独立看门狗与WWDG窗口看门狗
    本篇博客重点在于标准库函数的理解与使用,搭建一个框架便于快速开发目录WDG简介IWDGIWDG特性独立看门狗时钟键寄存器超时时间 IWDG代码WWDGWWDG特性窗口看门狗时钟超时时间WWDG时序WWDG代码 IWDG和WWDG对比 WDG简介WDG(Watchdog)看门狗,可以监控程序的运......
  • 嵌入式STM32单片机开发环境配置教学Win/Mac
    合集-环境配置(2)1.最全!嵌入式STM32单片机开发环境配置教学Win/Mac!!!08-282.最简最速!C++版OpenCV安装配置教程Win/Mac!!!08-28收起嵌入式STM32单片机开发环境配置教学Win/Mac·本教程支持Windows和Mac·Windows可选的开发软件为Keil、Clion、STM32CubeMX,可自由选择开发方式·M......
  • 基于STM32的智能小宠喂养兼检测系统设计(214)
    文章目录一、前言1.1项目介绍【1】项目功能介绍【2】设计实现的功能【3】项目硬件模块组成1.2设计思路【1】整体设计思路【2】ESP8266工作模式配置1.3项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】摘要【5】选题背景【6......
  • 基于STM32设计的智能饮水机_升级版(微信小程序)(213)
    文章目录一、前言1.1项目介绍【1】项目功能介绍【2】设计实现的功能【3】项目硬件模块组成1.2设计思路【1】整体设计思路【2】ESP8266工作模式配置1.3项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】摘要【5】选题背景【6......
  • STM32F4 timer定时器触发ADC采集,DMA转运数据 (标准库)
    硬件平台:STM32F401RCT6项目需求:需要实现100hzADC采集用于FFT频谱分析,同时要支持切换采集通道,每次采集之前改变数据存储地址与buff长度直接说配置过程的重点在DMA和ADC初始化之后,要处于disable状态,每次采集之前enable。如果一开始处于enable状态,没有采集,执行了disable再enabl......