摘要
本文介绍了NEC红外协议及软解NEC红外协议的方法,同时为类似的低资源嵌入式设备提出了红外遥控解码的途径。
NEC红外编码
NEC红外协议
一个NEC键码由:header+8bit地址+8bit地址反码+8bit命令+8bit命令反码 组成
一个NEC键码的header有9ms的高和4.5ms的低组成
逻辑1由560us的高和2.25ms-560us的低组成
逻辑0由560us的高和560us的低组成
在红外传输的过程中这些高低电平时被调制在38K上被传送,而从红外头出来后是被解调后的高低电平信号。
算法
从上一节的红外协议来看,要解码一个红外键值,首先要找到header,然后根据高低电平的长度解得逻辑值,最后得到一个完整的键码。
使用的硬件资源:
1个外部中断,用于探测信号电平的变化
1个硬件timer,用于测量信号电平变化的时间
1. 解码
由于使用了比较简单的单片机,中断只支持下降沿和低电平触发,因此配置为下降沿触发
同时在硬件连接上将信号电平做了反向,因此图中看到的上升沿正好是下降沿。
从上图可见,当收到一个红外码时,首先是由header触发中断,第一次中断到第二次中断的时间间隔为9ms+4.5ms,因此如果测量到两次中断的时间间隔为约135ms,那么表示收到了红外编码的header,需要开始解码
解码就是识别逻辑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时间的识别即可。
在硬件的限制下,本文提出的方法并不能适用于其它红外的解码,如果适当的修改硬件,或者是外部中断本身支持同时上下沿触发,在本文的提出的软件架构下,通过记录红外编码的高低电平持续时间,并根据逻辑值的电平特性做多种红外协议的解码也是不难的。