软解NEC红外编码

原创
2013/06/22 21:28
阅读数 1.2W

摘要

本文介绍了NEC红外协议及软解NEC红外协议的方法,同时为类似的低资源嵌入式设备提出了红外遥控解码的途径。

NEC红外编码

image

NEC红外协议

一个NEC键码由:header+8bit地址+8bit地址反码+8bit命令+8bit命令反码 组成

一个NEC键码的header有9ms的高和4.5ms的低组成

image 

逻辑1由560us的高和2.25ms-560us的低组成

逻辑0由560us的高和560us的低组成

在红外传输的过程中这些高低电平时被调制在38K上被传送,而从红外头出来后是被解调后的高低电平信号。

算法

从上一节的红外协议来看,要解码一个红外键值,首先要找到header,然后根据高低电平的长度解得逻辑值,最后得到一个完整的键码。

使用的硬件资源:

1个外部中断,用于探测信号电平的变化

1个硬件timer,用于测量信号电平变化的时间

1. 解码

由于使用了比较简单的单片机,中断只支持下降沿和低电平触发,因此配置为下降沿触发

同时在硬件连接上将信号电平做了反向,因此图中看到的上升沿正好是下降沿。

image

从上图可见,当收到一个红外码时,首先是由header触发中断,第一次中断到第二次中断的时间间隔为9ms+4.5ms,因此如果测量到两次中断的时间间隔为约135ms,那么表示收到了红外编码的header,需要开始解码

image

解码就是识别逻辑1和逻辑0

对于逻辑1,两次中断的时间为2.25ms

对于逻辑0,两次中断的时间为1.12ms

综上

两次中断时间为13.5ms  ==  Header

两次中断时间为2.25ms  ==  1

两次中断时间为1.12ms  ==  0

当解出一个header和连续32个逻辑电平,就完成了一次红外键值解码

2. 计时

当一中断发生后就启动timer,下一次中断发生时停止timer并,每次timer启动收,都在固定时间发生timer中断,在中断中对变量+1,当timer停止时查看变量值并结算出耗时。从上面的分析来看只要中断时间的误差在0.1ms都是可以分辨三种情况的。因此为了timer的中断不过于频繁,将timer中断设置为100us。例如两次外部中断间timer中断发生了11次,那么这就是逻辑0

3.实现

定义
#define CNT_START		//启动Timer计数,实现代码与硬件相关
#define CNT_END		//结束Timer计数,实现代码与硬件相关
#define CNT_VALUE	u8CntValue		//获取Timer计数值

#define T_CNT 	5		//用于计算时间误差范围
#define T2_CNT	9		//用于计算时间误差范围

#define LOGIC1_CNT	22	//逻辑1 22*100us约为2.25ms
#define LOGIC0_CNT	11	//逻辑0 11*100us 约为 1.12ms

#define HEAD_CNT		135	//header 135*100us 为13.5ms
Timer中断服务函数
void IRMux_ser(void) interrupt 3
{
	if(u8IntState == STATE_CNT)
	{
		u8CntValue++;
	}
}
外部中断解码函数
#define IR_CACHE_CNT	40
u8 au8IRCache[IR_CACHE_CNT];	//用于缓存计数值,是个ring buffer
u8 au8Key[4];	//用于存放红外解码值
void NEC_Ser(void)
{
	bool b8Clear = FALSE;
	CNT_END;		//结束计时
	
	au8IRCache[u8W] = CNT_VALUE;		//将计数值写入
	u8W++;

	if(u8W>=IR_CACHE_CNT)		//计数值缓存是个ring buffer
	{
		u8W = 0;
	}
	CNT_START;		//开始计数
	
	//读取一个计数值,如果是header,则开始解码, header允许有200us的误差
	if(au8IRCache[u8R]  > HEAD_CNT-T2_CNT &&  au8IRCache[u8R] < HEAD_CNT + T2_CNT)
	{
		idata u8 u8Cnt;
		//获取计数值个数
		if(u8W>=u8R)
		{
			u8Cnt = u8W -u8R;
		}
		else
		{
			u8Cnt = IR_CACHE_CNT - u8R + u8W;
		}
		//如果计数值大于33个(1 header+8 address+8 ~address+8 cmd+8 ~cmd),说明已经收到一个完整的码值,开始解码,否则退出等待接收到足够的计数值
		if(u8Cnt >= 33)
		{
			u8R++;	//Jump header
			if(u8R>=IR_CACHE_CNT)
			{
				u8R = 0;
			}
			//开始解码
			u8RevBitNum = 0;
			while(1)
			{
				
				if(au8IRCache[u8R] >= LOGIC1_CNT - T_CNT&& au8IRCache[u8R] < LOGIC1_CNT+T_CNT)	//逻辑1
				{		
					au8Key[u8RevBitNum>>3] |= (1<<(u8RevBitNum - ((u8RevBitNum >> 3)<<3)));		//按位放入红外码值数组

				}
				else if(au8IRCache[u8R] >= LOGIC0_CNT - T_CNT && au8IRCache[u8R] < LOGIC0_CNT + T_CNT)		//逻辑0
				{
					au8Key[u8RevBitNum>>3] &= (~(1<<(u8RevBitNum - ((u8RevBitNum >> 3)<<3))));		//按位放入红外码值数组
				}
				//解完一位,再解下一位
				u8RevBitNum++;
				
				//指向下一个计数值
				u8R++;	
				if(u8R>=IR_CACHE_CNT)
				{
					u8R = 0;
				}

				if(u8RevBitNum == 32)		//解完32位,一个NEC红外码解完
				{
					bKeyValid = TRUE;					
					u8RevBitNum = 0;

					break;
				}
			}
		}
	}
	else
	{
		//如果不是header,抛弃当前计数值
		u8R++;
		if(u8R>=IR_CACHE_CNT)
		{
			u8R = 0;
		}
	}
}

总结

本文针对NEC红外协议,实现了软解的NEC码的算法,采用了在中断中解码的方式,不用一直占用CPU时间,能高效实时的完成NEC解码。

本文提出的方法并未处理NEC的repeat特殊键值,不过对于目前的架构非常容易扩展实现,只用再加入repeat时间的识别即可。

在硬件的限制下,本文提出的方法并不能适用于其它红外的解码,如果适当的修改硬件,或者是外部中断本身支持同时上下沿触发,在本文的提出的软件架构下,通过记录红外编码的高低电平持续时间,并根据逻辑值的电平特性做多种红外协议的解码也是不难的。

展开阅读全文
打赏
2
2 收藏
分享
加载中
更多评论
打赏
0 评论
2 收藏
2
分享
返回顶部
顶部