5 EXTI外部中断(二)

文章介绍了使用STM32微控制器通过对射式红外传感器进行计数的方法,涉及到GPIO、EXTI、NVIC中断配置以及中断服务函数的编写。同时,还展示了如何利用旋转编码器进行计数,同样基于中断处理,确保计数的准确性。在中断处理中,特别强调了避免数据冲突和抖动的问题。
摘要由CSDN通过智能技术生成

5.3 对射式红外传感器计次

在这里插入图片描述

对射式红外传感器,指示灯灭,代表现在输出高电平,拿开挡光片,输出指示灯亮。

在灭到亮之间会产生一个下降沿,这个下降沿触发单片机引脚的外部中断,然后执行数字加1的中断程序

代码讲解:

GPIO文件函数说明(这里主要学习AFIO的数据选择)

//锁定引脚配置,防止随意更改
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

//配置事件输出功能
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);

//配置引脚重映射(第一个参数:选择重映射方式,第二个参数是新的状态)
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);

//配置AFIO的数据选择器选择中断引脚
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

//跟以太网有关的外设
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

EXTI函数说明:

void EXTI_DeInit(void);	//配置清除,恢复上电默认状态
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);	//初始化EXTI,根据结构体里的参数配置EXIT外设
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);  //把参数传递的结构体变量赋一个默认值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);	//软件触发外部中断

//主程序查看和清除标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);	//获取指定的标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);	//清除指定的标志位

//中断函数查看和清除标志位
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);	//获取中断标志位是否被置1
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);	//清除中断标志位

配置NVIC:在,misc文件:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);	//用以中断分组,参数是中断分组的方式
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);	//根据结构体里面指定的参数初始化
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);	//设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);	//系统低功耗配置
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);	//

主函数:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"


int main(void)
{
	OLED_Init();
	CountSensor_Init();

	OLED_ShowString(1, 1, "Count:");

	while (1)
	{
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);
	}
}

中断函数配置过程:

//step1:配置RCC,把我们这里涉及的外设的时钟都打开(其中EXTI和NVIC两个外设是一直开着的)
//step2:配置GPIO,选择我们的端口为输入模式
//step3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
//step4:配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式(中断响应或事件响应)
//step5:配置NVIC,给我们这个中断选择一个合适的优先级
//最后通过NVIC,外部信号就能进入CPU了

中断函数:(.c)

#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;


void CountSensor_Init(void)
{
	//step1:配置RCC,把我们这里涉及的外设的时钟都打开(其中EXTI和NVIC两个外设是一直开着的)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	//step2:配置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_2MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//step3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
	
	//step4:配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式(中断响应或事件响应)
	//当前模式是:将EXIT的第14个线路配置为中断模式,下降沿触发,然后开启中断,将电平信号通过EXIT通向下一级NVIC
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStructure);
	
	//step5:配置NVIC,给我们这个中断选择一个合适的优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //此分组方式整个芯片只能用一种,必须得确保每个模块选择的都是同一个(或者把其放在主函数的最开始,这样模块就不用再分组了)
	NVIC_InitTypeDef NVIC_InitStucture;
	NVIC_InitStucture.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStucture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStucture.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStucture);
	
	//最后通过NVIC,外部信号就能进入CPU了
}

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetFlagStatus(EXTI_Line14) ==SET)
	{
		CountSensor_Count ++;
		EXTI_ClearITPendingBit(EXTI_Line14); //每次启动了中断,记得将中断清除,不然就困死在中断程序里了
	}
}

.h文件

#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);

#endif

5.4 旋转编码器计次

在这里插入图片描述

主程序:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;

int main(void)
{
	OLED_Init();
	Encoder_Init();

	OLED_ShowString(1, 1, "Num:");

	
	while (1)
	{
		Num += Encoder_Get();
		OLED_ShowSignedNum(1, 5, Num, 5);
	}
}

Encoder文件:(.c)

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;

void Encoder_Init(void)
{
//step1:配置RCC,把我们这里涉及的外设的时钟都打开(其中EXTI和NVIC两个外设是一直开着的)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	//step2:配置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);
	
	//step3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

	
	//step4:配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,选择触发响应方式(中断响应或事件响应)
	//当前模式是:将EXIT的第0和1个线路配置为中断模式,下降沿触发,然后开启中断,将电平信号通过EXIT通向下一级NVIC
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStructure);
	
	//step5:配置NVIC,给我们这个中断选择一个合适的优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //此分组方式整个芯片只能用一种,必须得确保每个模块选择的都是同一个(或者把其放在主函数的最开始,这样模块就不用再分组了)
	NVIC_InitTypeDef NVIC_InitStucture;
	NVIC_InitStucture.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStucture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStucture.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStucture);
	
	NVIC_InitStucture.NVIC_IRQChannel = EXTI1_IRQn;
	NVIC_InitStucture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStucture.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStucture);
	
	//最后通过NVIC,外部信号就能进入CPU了
}

//返回count的变化值
int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

//编写中断函数,在Start文件夹下的startup_stm32f10x_md.s文件下寻找
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET) //检查中断标注位
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)	//反转
		{
			Encoder_Count --;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}

void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET) //检查中断标注位
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)	//正转
		{
			Encoder_Count ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

注意:

1.最好不要在主程序和中断程序里操作可能产生冲突二点硬件,可以现在中断里操作变量或标志位,当中断返回时,再对整个变量进行显示操作;

2.如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动

void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
			{
				Encoder_Count --;
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}

void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
			{
				Encoder_Count ++;
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line1);c
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值