红外软件解码文档
王小涛 2009年6月9日
1 、引言
低速的红外无线传输是一种廉价的无线通讯/控制方案,在家电领域已经广泛使用。在嵌入式领域,只要工作环境比较优良,数据量小,传输距离较近(5米以内),同样可以采用红外无线传输。本站的ATmega16学习板上设计了红外发射管与接收管,配合站长家中闲置的“HITACHI VM101”型电视遥控器,详细分析一下此遥控器的红外编解码原理与单片机解码方法。
2 、红外遥控信号代码的格式
一个完整的红外遥控信号代码一般由引导码、系统码、系统码的反码、数据码、数据码的反码等几个部分组成。引导码是一个代码的起始部分,由时间相对较长的一段发送时间与一段停发时间构成,即时间较长的一个高电平和一个低电平组成;系统码是通过遥控器的遥控编码芯片的引脚不同接法设定的,用以区分不同型号的遥控系统;数据码则是遥控器功能按键的编码,不同的功能按键其代码不相同;系统码的反码和数据码的反码是用来纠错的。遥控信号代码总的长度因采用的编码芯片不同而有所不同,功能代码与功能代码之间的最短时间间隔一般为80ms~120ms。如图一所示是采用脉冲宽度编码调制方式的代表某一功能的一个完整的遥控代码。
图一 脉冲宽度编码调制方法的一个完整的遥控代码实例
数据码根据实际所需要表达的不同含义的数量,可以有不同的数据位数。如图三的示例为8位数据,则该遥控器最多可以有256个功能键。
参考这个格式,对序列二的数据进行解码,得到明码数据如下:
引导码,系统码00001010,系统码反码11110101,数据码10110000,数据码反码01001111
3、数据格式
数据格式包括了引导码、用户码、数据码和数据码反码,编码总占32位。数据反码是数据码反相后的编码,编码时可用于对数据的纠错。注意:第二段的用户码也可以在遥控应用电路中被设置成第一段用户码的反码。
采用37.91K的调制频率时各码元所占的时间:
位定义:用户码或数据码中的每一个位可以是“1”,也可以是“0”。区分“0”和“1”是利用脉冲的时间间隔来区分,这种编码方式称为脉冲位置调制方式,英文简称PPM。
实际上我们观察到的红外接收头的输出和上面的波形刚好反相,在空闲状态下,红外接收头输出为高电平,如下图:
因此对于我们实际的接收头来说,收到的数据格式应该是:引导码(9ms的低电平+4.5ms的高电平)+用户码(8位)+用户反码(8位)+数据码(8位)+数据反码(8位)+(停止位)。
其中位0是由0.56ms的低电平+0.565ms的高电平(总共1.125ms)组成。
位1是由0.56ms的低电平+1.69ms的高电平(总共2.25ms)组成。
而停止位则由0.56ms的低电平表示。(实测时有误差)
上图为位0和位1的脉冲时间长度,实际上在测试的时候发现,位0和位1的脉冲时间长度是存在误差,最大误差可达0.15ms~0.18ms左右(不同遥控器的误差不同),因此在软件解码的时候一定要有个范围,我们在软件中设置是这样子的:
else if(time>(1*MS) && time<(1.3*MS)) // 捕获到的是0
else if(time>(2.1*MS) && time<(2.4*MS)) // 捕获到的是1
4、软件解码
我们采用ARM7来进行软件解码,使用的芯片是LPC2134/2136。硬件上我们是将红外接收头的输出引脚连接到LPC2134/2136上的P0.17引脚,P0.17和CAP1.2(定时器1的捕获通道2)复用,我们选择CAP1.2功能,然后利用定时器1的捕获功能,设置在下降沿时捕获并允许中断。我们的解码思路如下:
(1)、初始化:
首先将P0.17选择为CAP1.2功能,
PINSEL1 = 1 << 2; // P0.17连接捕获1.2,其余连接GPIO
然后我们的定时器1设置如下:
T1PR = 00; // 分频系数,不分频
T1TC = 0; // 定时器清零
T1CCR = (1 << 7)| // 设置CAP1.2下降沿捕获
(1 << 8); // 允许产生中断
接着我们开放定时器中断:
IRQEnable(); // 使能IRQ中断
/* 设置Timer1中断 */
VICIntSelect = 0x00000000; // 设置所有的通道为IRQ中断
VICVectCntl0 = 0x20 | 5; // Timer1分配到IRQ slot0,即最高优先级
VICVectAddr0 = (uint32)Timer1_CapInt; // 设置Timer1向量地址
VICIntEnable = 1 << 5; // 使能Timer1中断
While(1); // 在此处循环等待
(2)、中断服务子程序:
初始化工作做好之后,把硬件电路搭好,就可以测试啦,我们先定义了中断服务子程序,然后程序体为空,如下:
void __irq Timer1_CapInt (void)
{
VICVectAddr = 0x00; // 向量中断处理结束
}
好了,现在可以做断点调试了,在语句VICVectAddr = 0x00;前加断点,然后运行,我们用遥控器按下一个键,可以看到程序跑到中断服务子程序中了,说明我们的捕获功能已经成功。接下来就是如何解码的工作了。
(3)、解码思路:
我们先看看我们的实际捕获到的波形:
我们可以看到,如果我们可以得到相邻两个下降沿之间的时间间隔,那么我们就可以解码了,我们设相邻两个下降沿之间的时间间隔为interval,则
若13.2ms < interval < 13.8ms,则两个下降沿之间为引导码
若 1.0ms < interval < 1.3ms,则两个下降沿之间为位“0”
若 2.1ms < interval < 2.1ms,则两个下降沿之间为位“1”
我们的程序一直等待,直到有键按下(捕获到引导码),我们接收到引导码之后就将变量code(用来存放32位编码)和bitcount(用来计算当前接收到的位数)清零,然后一个一个的捕获发送的32位编码。
我们每次在CPA1.2下降沿的时候捕获产生中断时,定时器1将计数值装入T1CR2,我们读取该值,然后复位和重启定时器,然后根据我们读到的T1CR2的值就可以知道两个下降沿之间的时间间隔,根据这个间隔来判断是引导码还是位“0”和位“1”,32位编码接收完毕后,复位并停止定时器,然后根据code值判断是哪个键按下。
对应的程序如下:
T1IR = 1 << 6; // 清除CAP1.2中断标志
interval=T1CR2; // 读取捕获到的T1TC值
// T1TCR = 0x03; // 复位并启动定时器,但不能这样做,否则程序跑不通。
T1TCR = 0x02;
T1TCR = 0x01;
// 复位定时器和启动定时器两条语句一定要分开,如果两条语句合成语句:T1TCR = 0x03;
// 程序就跑不通,而且每次定时器捕获值都是0,原因可能是复位和启动不能同时进行吧。
if(interval>(13.2*MS) && interval<(13.8*MS)) // 捕获到的是引导码
{
code=0;
bitcount=0;
}
else if(interval>(1*MS) && interval<(1.3*MS)) // 捕获到的是0
{
bitcount++;
}
else if(interval>(2.1*MS) && interval<(2.4*MS)) // 捕获到的是1
{
code |=(1<<(31-bitcount));
bitcount++;
}
if(bitcount==32)
{
T1TCR = 0x02; //复位定时器
T1TCR = 0x00; //停止定时器
check_key(code);
bitcount=0;
code=0;
}
下面给出Solam索浪SL-208型遥控器的code值。
void check_key(uint32 key_code)
{
//Solam索浪SL-208型遥控器:
if(key_code==0x00FD00FF) // 电源 键
{ }
else if(key_code==0x00FDB04F) // 上一曲 键
{ }
else if(key_code==0x00FD708F) // 下一曲 键
{ }
else if(key_code==0x00FD609F) // CH+ 键
{ }
else if(key_code==0x00FDA05F) // CH- 键
{ }
else if(key_code==0x00FD906F) // VOL- 键
{ }
else if(key_code==0x00FD50AF) // VOL+ 键
{ }
else if(key_code==0x00FD10EF) // EQ 键
{ }
else if(key_code==0x00FD7887) // S/F 键
{ }
else if(key_code==0x00FDB847) // 静音 键
{ }
else if(key_code==0x00FD40BF) // 暂停 键
{ }
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Linruin/archive/2009/10/13/4665610.aspx