4.4 51单片机-NEC红外线遥控器解码

4.4  NEC红外线遥控器解码

4.4.1 接收头原理图介绍

4.4 51单片机-NEC红外线遥控器解码

图4-4-1

实验板上的红外线接收头是接在单片机的P3.2 IO口上,要使用红外线接收功能,需要将红外线接收头的跳线帽接上。

4.4 51单片机-NEC红外线遥控器解码

图4-4-2

4.4.2 NEC红外线协议介绍

红外线遥控是目前使用最广泛的一种通信和遥控手段。由于红外线遥控装置具有体积小、功耗低、功能强、成本低等特点,因而,继彩电、录像机之后,在录音机、音响设备、空凋机以及玩具等其它小型电器装置上也纷纷采用红外线遥控。工业设备中,在高压、辐射、有毒气体、粉尘等环境下,采用红外线遥控不仅完全可靠而且能有效地隔离电气干扰。

红外线是波长介于微波和可见光之间的电磁波,波长在 760 纳米到 1 毫米之间,是波形比红光长的非可见光

家电遥控器通信距离往往要求不高,而红外的成本比其它无线设备要低的多,所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多,大概有几十种,常用的就有 ITT 协议、 NEC 协议、 Sharp 协议、 Philips RC-5 协议、 Sony SIRC 协议等。用的最多的就是 NEC 协议了,本小节以 NEC协议标准进行讲解,实验板自带的遥控器也是NEC协议的。

NEC协议,通常是使用 38K左右的载波进行调制,再发送的;这里的调制就是用待传送信号去控制某个高频信号的幅度、相位、频率等参量变化的过程,即用一个信号去装载另一个信号。比如:我们的红外遥控信号要发送的时候,先经过 38K 调制。

4.4 51单片机-NEC红外线遥控器解码

图4-4-3 信号调制示例

原始信号就是要发送的一个数据“0”位或者一位数据“1”位,而38K 载波就是频率为 38K 的方波信号,调制后信号就是最终我们发射出去的波形。使用原始信号来控制 38K 载波,当信号是数据“0”的时候, 就发送38K载波,当信号是数据“1”的时候,就不发送任何载波信号,那么数据接收方只需要检测是否收到38KHZ的载波就行判断是否收到了数据。实验板上带了一个一体化接收头HS0038B,可以识别38KHZ的载波信号,当HS0038B收到38KHZ载波就输出低电平,没有收到38KHZ载波就输出高电平;程序就可以判断HS3008的引脚电平配合指定的协议格式来分析收到的数据。

4.4 51单片机-NEC红外线遥控器解码

图4-4-4 红外线接收头

NEC协议的数据格式包括了引导码、用户码、用户码反码、按键码、按键反码

其中数据编码总共是4个字节 32 位(除了引导码外就是数据位),数据格式中的反码用于数据校验,可以判断数据在传输过程中有没有丢包。

4.4 51单片机-NEC红外线遥控器解码

图4-4-5 NEC协议传输格式

NEC协议解码总结:

引导码:9ms的低电平+4.5ms的高空闲。

逻辑 1:  560us 低+1680us 高电平

逻辑 0:  应该是 560us 低+560us 高电平。

遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平。

红外线遥控器向红外线接收头按下一个按键之后,红外线接收头先收到的是9ms的高电平脉冲,接着是4.5ms的低电平,之后就是接收8bit的用户码、8bit的用户码的反码,8bit 的按键码,8bit 的按键码的反码。如果一直按着1个键,这样遥控器发送的是以110ms为周期的重复码,就是说,发了一次按键码之后,不会再发送按键码,而是每隔110ms时间,发送一段重复码,重复码由9ms高电平和2.25ms的低电平以及560us的高电平组成。

4.4.3 采集NEC协议的信号

为了更加直观的理解NEC协议格式,可以使用逻辑分析仪或者示波器采集HS0038红外线接收头收到的数据,再通过软件分析收到的数据。

4.4 51单片机-NEC红外线遥控器解码

 图4-4-6 逻辑分析采集的NEC数据

 4.4 51单片机-NEC红外线遥控器解码

图4-4-7 逻辑分析采集过程

逻辑分析仪软件下载地址: Logic analyzer software from Saleae

4.4 51单片机-NEC红外线遥控器解码

图4-4-8 逻辑分析软件

4.4.4 NEC红外线协议解码示例

下面代码采用定时器+外部中断的方式解码红外线数据,外部中断采用外部中断0,接在P3.2口上,配置外部中断0的触发方式为下降沿触发。

解码思路: 红外线接收头没有收到38KHZ方波时,默认输出高电平,当收到38KHZ方波时输出低电平,这时就触发了下降沿,接着就进入到外部中断0 的中断服务函数;解码的过程就在中断服务函数里实现,高低电平的持续时间通过定时器0进行计数,当前实验板的晶振是12MHZ,刚好定时器的计数器+1的时间就是1us,配置定时器0的工作方式为16位模式,最大计数可以到65535,NEC协议最长的一段时间也就9000us,完全满足需求;解码完成之后设置标志位,在主函数里将解码的数据通过串口打印出去。

(硬件平台说明:CPU是STC90C516RD 、晶振频率12MHZ 、工作在12T模式下、一个机器周期为1us时间)

示例代码:

#include <reg51.h>
u8 Infrared_RX_Flag=0; //红外接收标志,收到一帧正确数据后置1
u8 Infrared_RX_Buff[4];//红外代码接收缓冲区
sbit Infrared_GPIO=P3^2;//红外接收引脚--外部中断0
/*
函数功能: 开始红外线解码之前的相关初始化
实验板的晶振频率是12MHZ
51单片机标准架构下一个机器周期是12个时钟周期,如果晶振频率是12MHZ,那一个机器周期的时间就是12/12微秒。
也就是说定时器的计数器+1的时间就是12/12=1us。
*/
void Infrared_Init(void)
{  
   Infrared_GPIO=1;//红外接收引脚默认保持高电平输出
   TMOD&=0xF0; //清除配置  
   TMOD|=0x01; //配置定时器0,工作在16位计数模式
   TR0=0;      //停止定时器0计数
   ET0=0;       //禁止定时器0中断
   IT0=1;      //开启外部中断0,下降沿触发
   EX0=1;      //允许外部中断0中断
}

/*
函数功能: 检测高电平持续的时间
*/
u16 Infrared_GetTimeH(void)
{
   TH0=0; //定时器0重装值为0
   TL0=0; //定时器0重装值为0
   TR0=1; //启动定时器0开始计数
   while(Infrared_GPIO)//等待高电平结束
   {
      if(TH0>0x40)//防止超时   
      {
         break;
      }
   }
   TR0=0;//停止定时器0计数
   return TH0<<8|TL0;//T0计数值合成为16位整数返回
}

/*
检测低电平持续的时间
*/
u16 Infrared_GetTimeL(void)
{
   TH0=0;//定时器0的高8位重装值
   TL0=0;//定时器0的低8位重装值  
   TR0=1;//开启定时器0
   while(Infrared_GPIO==0)//等待低电平结束
   {
      if(TH0>0x40)//防止超时   
      {
         break;
      }
   }
   TR0=0;//停止定时器0计数
   return TH0<<8|TL0;//T0计数值合成为16位整数返回
}

/*
外部中断0中断服务函数
*/
void EXTI0_IRQHandler() interrupt 0
{
   u8 i, j;
   u16 time;
   u8 byte;
   time=Infrared_GetTimeL();   //获取出现低电平的时间
   if((time<7800)||(time>9300))//判断低电平时间是否符合9ms范围
   {                           //超过此范围则说明为误码,直接退出
      IE0=0;                  //清除外部中断0中断标志
      return;  
   }
   time=Infrared_GetTimeH();   //获取出现高电平的时间
   if((time<3500)||(time>4700))//高电平是否符合4.5ms范围
   {                           //超过此范围则说明为误码,直接退出
      IE0=0;                //清除外部中断0中断标志
      return;
   }
   //接收32位数据位
   for(i=0;i<4;i++)
   {
      for(j=0;j<8;j++)
      {
         time=Infrared_GetTimeL();   //获取低电平持续时间,标准的间隔时间为560us范围                    
         if((time<300)||(time>700))  //判断范围是否合理
         {
            IE0=0;//清除外部中断0中断标志
            return;
         }
            //1和0是靠高电平持续的长短来区分的
         time=Infrared_GetTimeH();  //获取高电平持续时间
         if(time>300&&time<700)    //0的标准时间为560us
         {
            byte>>=1;
         }
            else if(time>1400&&time<1800) //1的标准时间是1680us
         {
            byte>>=1;
            byte|=0x80;
         }
         else //不在上面的判断范围内说明是错误码,直接退出
         {
            IE0=0;//清除外部中断0中标
            return;
         }
      }
      Infrared_RX_Buff[i]=byte;//接收完一个字节后保存到缓冲区
   }
   Infrared_RX_Flag=1;//接收完毕后设置标志
   IE0=0;//退出前清除外部中断0中断标志
}

int main()
{
    UART_Init();        //初始化串口波特率为4800
    Infrared_Init();    //初始化红外功能
    while(1)
    {
        if(Infrared_RX_Flag)          //接收到红外数据
        {
            Infrared_RX_Flag=0;     //清楚标志
            printf("user1:%d,user2:%d\r\n",(int)Infrared_RX_Buff[0],(int)((u8)(~Infrared_RX_Buff[1])));
            printf("key1:%d,key2:%d\r\n",(int)Infrared_RX_Buff[2],(int)((u8)(~Infrared_RX_Buff[3])));
        } 
    }
}
上一篇:POJ 2449 Remmarguts' Date


下一篇:Topcoder SRM568 Div1 DisjointSemicircles (二分图染色)