STM32+外部中断+定时器 实现红外遥控(二)

目录

前言

硬件

软件

1、初始化外部中断IO和定时器

2、外部接口

 3、红外接收中断,基本定时中断

总结

引用


前言

        本文主要介绍如何利用外部中断和定时器实现红外遥控功能代码,在上一篇文章已经简述过原理了,所以本文着重于应用,不讲原理。
        原理部分可跳转http://t.csdnimg.cn/I28Md

硬件

        选用野火指南者开发板,本文用到的IO只需要带有外部中断功能的就行,另外配有一个基础定时器,本文为了方便写代码改为通用定时器。

引脚TIMx
PE5TIM2
软件

        本例程参考了江协科技的51红外遥控代码,利用状态机的思维,用4个8位字节来在存数据,响应外部中断后用定时器来捕获数据 ,注意配合NEC协议阅读代码会通俗易懂。

注:软件部分在看了网上很多例程,包括野火和正点的,野火采用的是外部中断+滴答定时器,在CPU足够的情况下当然没问题,但是移植到一些频率较低的芯片可能就会有掉包的情况。正点的代码看起来没问题,但是既然里面用到了位操作,为什么还会和野火一样不注意大小端,高低位反着来读。

1、初始化外部中断IO和定时器
参考时钟(pclock)自动重装载值(ARR)分频系数(PSC)
7200000020000-172-1

计数频率:CK_CNT=pclock/(PSC+1)=72000000/72=1000000次/s(计数一次用时1us)
计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=50(1s溢出50次,每次溢出中断需要20ms)

void IR_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	NVIC_InitTypeDef NVIC_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	
	/* config the extiline clock and AFIO clock */
	RCC_APB2PeriphClockCmd(IR_RCC_APB | RCC_APB2Periph_AFIO,ENABLE);
	
	/* config the NVIC */
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;  
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	
	
	/* EXTI line gpio config */	
    GPIO_InitStructure.GPIO_Pin = IR_PIN;       
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	 
    GPIO_Init(IR_PORT, &GPIO_InitStructure);
	
	/* EXTI line mode config */
    GPIO_EXTILineConfig(IRDA_GPIO_PORT_SOURCE, IRDA_GPIO_PIN_SOURCE); 
    EXTI_InitStructure.EXTI_Line = IRDA_EXTI_LINE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿中断
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure); 
}
void Timer_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量		
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	//   72/72000000=0.000001s,0.000001*20000=20ms=0.02s
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC配置*/
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}
2、外部接口
unsigned int IR_Time;          //用于记录两次中断之间的时间
unsigned char IR_State;        //记录状态机状态

unsigned char IR_Data[4];      //红外数据,4组
unsigned char IR_pData;        //指向的数据地址,一般32位

unsigned char IR_DataFlag;     //接收到数据
unsigned char IR_RepeatFlag;   //接收到重复数据
unsigned char IR_Address;      //用户码
unsigned char IR_Command;      //命令码

/**
  * @brief  红外遥控获取收到数据帧标志位
  * @param  无
  * @retval 是否收到数据帧,1为收到,0为未收到
  */
unsigned char IR_GetDataFlag(void)
{
	if(IR_DataFlag)
	{
		IR_DataFlag=0;
		return 1;
	}
	return 0;
}

/**
  * @brief  红外遥控获取收到连发帧标志位
  * @param  无
  * @retval 是否收到连发帧,1为收到,0为未收到
  */
unsigned char IR_GetRepeatFlag(void)
{
	if(IR_RepeatFlag)
	{
		IR_RepeatFlag=0;
		return 1;
	}
	return 0;
}

/**
  * @brief  红外遥控获取收到的地址数据
  * @param  无
  * @retval 收到的地址数据
  */
unsigned char IR_GetAddress(void)
{
	return IR_Address;
}

/**
  * @brief  红外遥控获取收到的命令数据
  * @param  无
  * @retval 收到的命令数据
  */
unsigned char IR_GetCommand(void)
{
	return IR_Command;
}

void IR_Scan(void)  //红外扫描
{
	unsigned char Command;
	if(IR_GetDataFlag() )  //||IR_GetRepeatFlag()
	{
		Command=IR_GetCommand();
		printf("Command=%x\r\n",Command);
		switch(Command)
		{
			case IR_CH_MINUS:
					break;			   
			case IR_CH_ADD:
					break;	    
			case IR_MODE:
					break;	    
		}
	}
}
 3、红外接收中断,基本定时中断
uint16_t timer2_cnt;
void TIM2_IRQHandler(void)  //通用定时器实现基本定时
{
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    timer2_cnt++;
}

void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(IRDA_EXTI_LINE) != RESET)
	{
		if(IR_State==0)				//状态0,空闲状态
		{
			TIM_SetCounter(TIM2,0);	//定时计数器清0
			IR_State=1;				//置状态为1
		}
		else if(IR_State==1)		//状态1,等待Start信号或Repeat信号
		{
			IR_Time=TIM_GetCounter(TIM2);	//获取上一次中断到此次中断的时间
			TIM_SetCounter(TIM2,0);	//定时计数器清0
			if(IR_Time>13500-500 && IR_Time<13500+500)
			{
				IR_State=2;			//置状态为2
			}
			else if(IR_Time>11250-500 && IR_Time<11250+500)
			{
				IR_RepeatFlag=1;	//置收到连发帧标志位为1
				TIM_SetCounter(TIM2,0);		//定时器停止
				IR_State=0;			//置状态为0
			}
			else					//接收出错
			{
				IR_State=1;			//置状态为1
			}
		}
		else if(IR_State==2)		//状态2,接收数据
		{
			IR_Time=TIM_GetCounter(TIM2);	//获取上一次中断到此次中断的时间
			TIM_SetCounter(TIM2,0);	//定时计数器清0
			if(IR_Time>1120-500 && IR_Time<1120+500)
			{
				IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//数据对应位清0
				IR_pData++;			//数据位置指针自增
			}
			else if(IR_Time>2250-500 && IR_Time<2250+500)
			{
				IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));	//数据对应位置1
				IR_pData++;			//数据位置指针自增
			}
			else					//接收出错
			{
				IR_pData=0;			//数据位置指针清0
				IR_State=1;			//置状态为1
			}
			if(IR_pData>=32)		//如果接收到了32位数据
			{
				IR_pData=0;			//数据位置指针清0
				if((IR_Data[0]==(uint8_t)~IR_Data[1]) && (IR_Data[2]==(uint8_t)~IR_Data[3]))	//数据验证
				{
					IR_Address=IR_Data[0];	//转存数据
					IR_Command=IR_Data[2];
					IR_DataFlag=1;	//置收到连发帧标志位为1
				}
				TIM_SetCounter(TIM2,0);		//定时器停止
				IR_State=0;			//置状态为0
			}
		}
		EXTI_ClearITPendingBit(IRDA_EXTI_LINE);     //清除中断标志位
	}
}

实验结果

分别按下按键打印出对应数据

总结

在前文和本文中,红外遥控代码分为两种,一种是输入捕获中断实现红外遥控,本文是用外部中断+定时器来实现,两种方式用到的方法是同样的,不同的是用到的外设不同,下面将会列举编写代码过程遇到的问题。

注意:
1、在多任务系统里:vTaskDelay(50); //200 时间太长了会导致数据还没找到就又来了一个数据,会导致按下红外按键就出现按下两次的情况。
2、在计数器计数时,需要注意最大计数值是多少,两次中断进来之间的数据不能超过最大计数值的值。
3、在需要及时把数据清除,不然会误以为有数据,出现连按两次的现象。
4、正点||野火:用1个32位data储存数据帧,注意原本是以小端模式存数据,在改回大端模式存数据后已正常,不然会导致数据高位和低位对换,可能会出现10-30分钟内自己按下红外按键的现象。

引用

        本文代码参考江协科技51红外遥控代码,模板用的是野火STM32标准库模板。
        Gitee资源下载链接(本人):https://gitee.com/huang_ze_rong/stm32_-code.git

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32外部中断红外遥控解码是指利用STM32微控制器的外部中断功能对红外遥控信号进行解码的过程。 首先,我们需要了解红外遥控信号的工作原理。红外遥控信号是通过红外线传输的,信号的编码通常采用NEC、RC5等协议。主要包括起始位码、地址码和命令码等。 解码的第一步是利用红外接收器将红外遥控信号转化为电信号。常用的红外接收器有红外管、红外模块等。电信号经过红外接收器的解调,得到了红外遥控信号的脉冲宽度调制(PWM)信号。 接下来,我们需要利用STM32外部中断功能对红外遥控信号进行解码。首先,需要配置STM32的GPIO引脚为外部中断模式,并设置中断触发方式。常用的触发方式有上升沿触发、下降沿触发和双边沿触发等。 当红外管接收到PWM信号后,触发外部中断,将中断引脚与STM32外部中断输入管脚连接。当外部中断触发时,STM32会跳转到相应的中断服务函数进行处理。 在中断服务函数中,我们可以利用定时器和输入捕获功能来测量PWM信号的高电平时间和低电平时间。利用这些时间信息,我们可以解析出红外遥控信号的起始位码、地址码和命令码。 解码完成后,我们可以根据实际需求对红外遥控信号进行相应的处理。比如,可以控制某些设备的开关状态、调节音量、切换频道等。 需要注意的是,在实际应用中,还需要考虑红外遥控干扰、多个按键同时按下等问题,以提高系统的稳定性和抗干扰能力。 综上所述,STM32外部中断红外遥控解码是一种使用STM32微控制器的外部中断功能,对红外遥控信号进行解码的技术。通过这种技术,我们可以实现红外遥控信号的解析和响应,从而控制不同设备的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值