|
红外遥控信号解码
|
|
红外遥控是一种成熟的短距通信技术,在家电产品中有着广泛的应用。 |
|
在这种通信技术中,信号的发送与接收都是采用专门的半导体元件。发送端信号来自红外发光二极管,接收端是红外光敏二极管,在实际的产品中通常采用由红外光敏二管、放大电路、滤波电路等组成的集成电路。 |
|
图(1) |
|
家用电器领域使用的红外光线的波长是940nm,不同的产品之间通过上层编码加以区别。 |
|
在使用红外光线传递数据的通信中,通常不会直接使用所传递的数据来调制红外光线,而是先把数据加载到一个载波上,再用载波调制红外光线。这样做的好处是接收端可以根据载波的频率设置滤波器,消除环境中的红外背景产生的干扰,也可以根据不同的载波频率区分不同的信道。通常家电领域采用38KHz的载波。 |
|
图(2) |
|
红外光线被接收到后通过放大、滤波、解调等处理,还原出的数据信号由接收头送出。数据信号是由一连串数位流组成的,单片机要根据位的编码规则进行解码,将其还原成"0"和"1"的数据。数位的位编码规则是可以自己定义的,比如归零码、不归零码等等,只要收、发双方约定好即可。当然,从产品兼容的方面考虑,采用大家普遍使的编码规则更有利于与现有的产品对接。图(3)所示,是家电中普遍采用的编码方式。 |
|
图(3) |
|
这一编码规则中,包含了4种位编码:起始位、重复位、逻辑1位、逻辑0位,以及空闲状态编码:高电平。位解码就是根据各种位编码的特征,将它们区分开来。从图上可以看出,起始位和重复位最显著的特征就是有个特别长的低电平,这一特征将他们与逻辑1和逻辑0区别开来。而逻辑1和逻辑0之间的区别则是高电平时间的长度不一样。所以,要区分这些位编码,就归结到一个问题上:测量脉冲的宽度。 |
|
具体来说,我们可以以一个下降沿作为起点,来度量到下一个上升沿或到下一个下降沿的时长: |
|
1. 如果下一个上升沿在8mS - 10mS之后到来,则可以认为是一个起始位或重复位; |
|
2. 如果下一个下降沿在1mS - 1.2mS之后到来,则可以认为是一个逻辑0位; |
|
3. 如果下一个下降沿在2.1mS - 2.3mS之后到来,则可以认为是一个逻辑1位; |
|
首先,我们如何得到第1个下降沿发生的时刻?因为必须有一个准确的参考点,才能得到准确的测量值。这个问题我们可以借助T2定时器的自动重装机制来解决。 |
|
图(4) |
|
89C52的T2定时器可以工作在自动重装方式,其逻辑功能如图(4)所示。T2内含的边沿检测电路可以实时的监测T2EX信号。一旦T2EX信号上有下降沿发生,便会触发T2的重装机制,将RCAP2L和RCAP2H的值装载到TL2和TH2中。T2就会以这个重装的数值为起点开始计数,这就实现了计时起点对齐的作用。同时,T2EX的下降沿会触发1次T2的中断,软件可以在中断处理程序中做好重新计量的准备工作。 |
|
T2定时器向上计数发生溢出时也会重新装载计数初值,以及触发中断。软件可以在中断处理程序中记录溢出的次数,以这个溢出的次数作为计量时长的依据。直到下一个上升沿或下降沿到来,检测一下记录的溢出次数,便可知道当前是哪个位编码。 |
|
有了解码出的"0"、"1"位流,根据上层协议解析遥控命就不是难事了。 |
|
RCAP2L = 0x3e; // 设定T2的重装值,10000H - FA3EH = 05C2H = 1474
RCAP2H = 0xfa; // 22.1184MHz/12 = 1.8432MHz
// 1474/1.8432MHz = 0.000799696秒 = 0.8mS
// T2每0.8mS发生一次溢出中断,
// 所以,软件以0.8mS作为计时单位
C_T2 = 0; // 选择T2的定时器方式工作
CP_RL2 = 0; // 工作在自动重装方式下
TR2 =1; // 启动T2计数
EXEN2 = 1; // 允许T2EX检测下降沿
IE |= 0xa2; // 使能T2的中断,这里使用"|="赋值,而不是"="赋值
// 是为了不影响IE中其他的使能位
|
| |
|
在T2的中断处理程序中进行位编码识别的示例代码如下: |
|
void T2IntSer(void) interrupt 5
{
if(EXF2 == 1) // 中断源是T2EX的下降沿?
{
EXF2 = 0; // 清除标志
if(T2Cnt == 1) // 溢出次数是1,则表明当前收到的是"0"位
{
RxShift <<= 1; // 将新收到的"0"位移入字节中
BitCnt ++; // 位计数加1
}
else if(T2Cnt == 2) // 溢出次数是2,则表明当前收到的是"1"位
{
RxShift <<=1; // 将新收到的"1"位移入字节
RxShift += 1; // 先移位,再加1,将1放入字节的最低位
BitCnt ++; // 位计数加1
}
if(BitCnt == 8) // 位计数是8,则表明已收满1个字节
{
BitCnt = 0; // 位计数清0,准备接收1个新字节
RxBuf[RxBufPtr] = RxShift; // 将之前接收到的字节保存到缓冲区
RxShift = 0; // 将接收字节清0
RxBufPtr = (RxBufPtr+1)&0x1f; // 向前调整接收缓冲区指针
}
T2Cnt = 0; // 将溢出次数清0,开始新的计时
}
if(TF2 == 1) // 中断源是计数溢出?
{
TF2 = 0; // 清除标志
if(T2Cnt != 0xff) T2Cnt ++; // 溢出计数值加1,设定最大值避免溢出
if(T2EX == 0) // 检测T2EX是否是低电平,
{ // 因为上升沿需要用软件检测
if(T2Cnt > 9) // 低电平已持续了10个以上的时间单位
{ // 可以当成是1个起始位或重复位来处理
RxBufPtr = 0; // 清空接收冲区
BitCnt = 0; // 清0位计数
RxShift = 0; // 清0接收字节
} // 准备接收1个新的数据帧
}
else // T2EX发生了上升沿跳变
{
if(T2Cnt == 3) // 当前位的高电平持续时长符合"1"位的标准
{
RxShift <<= 1; // 将收到的"1"位移入字节
RxShift += 1;
BitCnt ++;
if(BitCnt == 8) // 若已收满1个字节,
{ // 将该字节保存到接收缓冲区
BitCnt = 0;
RxBuf[RxBufPtr] = RxShift;
RxShift = 0;
RxBufPtr = (RxBufPtr+1)&0x1f;
}
}
if(T2Cnt == 10) // 若低电平持续时间不符合起始位、数据位
{ // 可认为回到了空闲状态,本次通信结束
if(BitCnt != 0) // 保存最后接收的字节,最后字节没收满
{ // 是有可能的
BitCnt = 0;
RxBuf[RxBufPtr] = RxShift;
RxShift = 0;
RxBufPtr = (RxBufPtr+1)&0x1f;
IRFrm = 1; // 置起标志,通知上层软件处理收到的数据帧
}
}
}
}
} |
| |
|
|
|