STM32红外解码(NEC)

本实验用STM32F4来实现
红外遥控不能隔墙、抗干扰强。
对流程不感兴趣可以直接看代码
NEC码的时序在这里插入图片描述
大致是接收到引导码(9ms 低电平+ 4.5ms 高电平) + 地址码+反码(校验用)+数据+数据反码,这时已经接收到了完整的数据了,之后如果不松手,就会有9ms低电平+2.5ms的高电平+0.56ms低电平+97ms左右的高电平,如果还是按下的状态,同样会有这一周期的时序发生。以此判断按下几次
注意一点!这是发送方的时序,接收方的时序是反向的,也就是他发高电平而我们收到的是低电平!而且我们只要测高电平就行了。
一个码是0还是1,是通过一个载波电平+不同的信号电平来表达的,给上图
高低电平
例如位0是0.56msLow+0.56msHigh组成的,供1.125ms,位1是0.56ms L+1.68ms H 这就表达一个位的信号,切记!接收到的是反向的,接下来通过测量高电平脉宽就可以获得信号了。
这里STM32使用输入捕获测量脉宽(单定时器输入模式),还有个PWM双输入模式。
先通过流程图(参考正点原子)更形象地表达给大家表达一下程序实现吧!
在这里插入图片描述
这里大致说一下,定义一个状态字节来存取当前解码的状态。通过捕获CC中断和UP溢出中断进行操作状态位,其实CC中断才是用来接收数据的,而UP中断用来判断重复按下时间是否超时,已经数据是否接收完成。
进入CC中断后,判断脉宽时间,是0?是1?是引导码还是重复码

捕获中断

//<读取高电平时间来获取数据>
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留	
//[4]:标记上升沿是否已经被捕获								   
//[3:0]:溢出计时器
u8 RmtSta = 0;//遥控状态位
u16 CntVal;	//计数器的值 0-10000
u32 RmtDat = 0;	//接收到的数据	地址码-地址反码-数据码-数码反码
u8 RmtCnt = 0;	//重复按下次数

外部变量定义如上

#define	REMOTE_IC_TIMx TIM1
#define	RMT_IN PAin(8)
//捕获中断--检测按键值
void TIMx_CC_IRQHandler(void)
{
	if(TIM_GetITStatus(REMOTE_IC_TIMx,TIM_IT_CC1))
	{
		if(RMT_IN)	//此时处于高电平
		{
			REMOTE_IC_TIMx->CCER |= (1<<1);		//等下降沿
			REMOTE_IC_TIMx->CNT = 0;			//计数器清0开始计数--1us一次
			RmtSta |= (1<<4);					//上升沿已捕获,开始捕获下降沿
		}
		else	//此时处于低电平
		{
			CntVal = REMOTE_IC_TIMx->CCR1;		//读取捕获寄存器-获取高电平时间
			REMOTE_IC_TIMx->CCER &= (~(1<<1));	//转上升沿
			if(RmtSta & (1<<4))					//已经有过上升沿
			{
				if(RmtSta & (1<<7))				//引导码接收到
				{
					//三种码的判断
					if((CntVal > 300)&&(CntVal < 800))		//560us阈值 --0
					{
						RmtDat <<= 1;
					}
					else if((CntVal > 1400)&&(CntVal < 1800))//1680us阈值 --1
					{
						RmtDat <<= 1;
						RmtDat++;
					}
					else if((CntVal > 2200)&&(CntVal < 2600))//2500us阈值 --重复按下
					{
						RmtCnt++;
						RmtSta &= 0xF0;//清空溢出次数,等待下次重复按下
					}
				}
				else if((CntVal > 4200) && (CntVal < 4700))	//4.5ms的引导码,开始接收数据
				{
					RmtSta |= (1<<7);
					RmtCnt = 0;//重复次数清空
				}
			}
			RmtSta &= (~(1<<4));//清空上升沿标志
		}
	}
	TIM_ClearITPendingBit(REMOTE_IC_TIMx,TIM_IT_CC1);
}

只有在引导码收到才能判断是0是1还是重复码。由于捕获中断是单输入模式,无法判断是上升沿还是下降沿。但是如果当前引脚为高电平,那么刚才接收到的肯定是上升沿,以此判断。在接收引导码后,判断计数器阈值(计数器+1 = 1us),典型的1.68是1,0.56是0,2.5是重复按下。数据保存于RmtDat(32bit)中.

溢出中断

//更新中断--检测重复按下
void TIMx_UP_IRQHandler(void)
{
	if(TIM_GetITStatus(REMOTE_IC_TIMx,TIM_IT_Update))
	{
		if(RmtSta & (1<<7))	//在收到引导码的前提下,检测是否重复按下
		{
			//超过10ms后,可能进入重复按下事件,停止检验按键时间,
			//等待下次上升沿计算是引导码还是重复码
			RmtSta &= (~(1<<4));
			if((RmtSta & 0x0F )== 0x00)	RmtSta |= (1<<6);//如果前面从未溢出,说明得到的是数据码
			if((RmtSta & 0x0F) < 14) //小于14*10ms,说明可能还会有重复码到来
			{
				RmtSta++;
			}
			else //超时了,不可能有重复码到来
			{
				RmtSta &= 0b01110000;//清除引导位和溢出值,重新接收
			}				
		}
	}
	TIM_ClearITPendingBit(REMOTE_IC_TIMx,TIM_IT_Update);
}

溢出一次是10ms,所以如果红外遥控按下,是不可能溢出的!因为按下就会有脉冲,有脉冲就会清0,最大计数到4.5ms<10ms,所以如果到了溢出中断,那就是接收完成或者压根没按下或者是进入重复模式了。
为节省篇幅,上面每一句都有很详细的解释。

校验数据进行输出

//扫描遥控器按下
//return 键值
u8 Remote_Scan(void)
{
	u8 sta = 0;
	u8 tmp1 = 0,tmp2 = 0;

	if(RmtSta & (1<<6))	//接收到了完整按键值,开始校验数据
	{
		tmp1 = RmtDat >> 24;	//地址码
		tmp2 = RmtDat >> 16;	//地址反码
		if((tmp1 == (u8)~tmp2) && (tmp1 == REMOTE_ID))
		{
			tmp1 = RmtDat >> 8;
			tmp2 = RmtDat;
			if(tmp1 == (u8)~tmp2) sta = tmp1; //返回键值
		}
		if((sta == 0) || ((RmtSta & 0x80) == 0)) RmtSta &= ~(1<<6),RmtCnt=0;//此时已经松开了	
	}
	return sta;
}

校验很简单,就是地址码=地址反码取反,数据码=数据码取反。特别记得在取反符号前定要加强制类型转化。
对于地址码,几乎每个遥控器都是固定的,而键值码(数据码)是不固定的,从0~255.

部分其他代码

	TIM_TimeBaseInitTypeDef				TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_Period = (10000-1);
	TIM_TimeBaseInitStructure.TIM_Prescaler = (168-1);	//1us
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(REMOTE_IC_TIMx,&TIM_TimeBaseInitStructure);
	TIM_ICInitTypeDef					TIM_ICInitStructure;
	//输入捕获
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
	TIM_ICInitStructure.TIM_ICFilter = 3;
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

这部分代码也分享一下。GPIO就是复用成定时器x。然后使能CC1(注意看清楚引脚)和UP中断。中断向量就是CC>UP的优先级
我的测试代码:

		p1:
		RemoteData = Remote_Scan();
		if(RemoteData)	
		{
			if(last == RemoteData) goto p1;
			printf("\r\nRemoteData:%d",RemoteData);
			printf("\r\nCNT:%d",RmtCnt);
			last = RemoteData;
		}

红外解码当初看了下原理感觉没啥,做起来也是够折腾的- -!!但是也不难。

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值