欣欣学习网,老工程师带你学习单片机技术,欢迎来坐坐。
首  页 | 学习NIOSII | 学习C51 | 学习CPLD | 51+CPLD实验板 | | | MY-RTOS

 实验板首页
 安装QuartusII软件
 安装USB-Blaster驱动
 安装CH340C驱动
 安装Keil uVision软件
 运行Hello World例程
 新建Keil uVision工程
 在Flash中运行程序
 恢复CPLD中的出厂逻辑
 安装调试代理程序
 硬件架构分析
 实现LCD驱动接口
 实现数码管驱动接口
 实现4x2键盘接口
 使用ADC采集拟量
 使用EEPROM保存数据
 使用UART通信
 红外遥控信号解码
 用作USB转232/485
 相关软件与文档


红外遥控信号解码


红外遥控是一种成熟的短距通信技术,在家电产品中有着广泛的应用。

在这种通信技术中,信号的发送与接收都是采用专门的半导体元件。发送端信号来自红外发光二极管,接收端是红外光敏二极管,在实际的产品中通常采用由红外光敏二管、放大电路、滤波电路等组成的集成电路。

图(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"位流,根据上层协议解析遥控命就不是难事了。

T2定时器初始化示例代码如下:

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;                  // 置起标志,通知上层软件处理收到的数据帧
        }
      }
    }
  }
}         

完整的范例程序可点击 这里 下载,范例程序的使用方法可参见:Hello World 程序




管理员信箱: stonewayqi@hotmail.com

欣 欣 学 习 网

粤ICP备2023138008号