广告

原创 红外编解码彻底解析

2010-9-26 23:24 9413 0 11 分类: MCU/ 嵌入式

编码格式




    现有的红外遥控包括两种方式:PWM(脉冲宽度调制)和PPM(脉冲位置调制)。


    两种形式编码的代表分别为NEC 和 PHILIPS的RC-5、RC-6以及将来的RC-7。


     PWM(脉冲宽度调制):以发射红外载波的占空比代表“0”和“1”。为了节省能量,一般情况下,发射红外载波的时间固定,通过改变不发射载波的时间来改变占空比。例如常用的电视遥控器,TOSHIBA的TC9012,其引导码为载波发射4.5ms,不发射4.5ms,其“0”为载波发射0.56ms,不发射0.565ms,其“1”为载波发射0.56ms,不发射1.69ms。如图所示。


     PPM(脉冲位置调制):以发射载波的位置表示“0”和“1”。从发射载波到不发射载波为“0”,从不发射载波到发射载波为“1”。其发射载波和不发射载波的时间相同,都为0.68ms,也就是每位的时间是固定的。


     脉冲宽度调制方式的遥控芯片,典型代表为TC9012,其他类型的芯片大同小异的。这里就已TC9012型芯片来分析。


图 1                  


image


     为了提高遥控器的发射具体,肯定得提高红外发射管的功率,如果给发射管发以未经调制的信号,一来发射距离会非常短;二来很容易把发射管给烧掉。所以一般我们都会将发射的信号进行调制后再发送出去。


     从图1,我们可以知道,在主频为455KHz下将单个脉冲调制为37.91KHz .


          调制频率   f(CAR) =1 /Tc =f(osc) /12 =455KHz /12 =37.91666…….     也就是常用的红外调制频率 38KHz


          其占空比为  1/3


 


image


         这是发送的一个完整帧,依次为  引导码,  用户识别码, 用户识别码反码,操作码,操作码反码。


 


image


          上图的一个波形代表引导码的 4.5ms高(其实里面包含更小的调制脉冲,记住只要是红外发送的所有高电平都是经过调制的,而低电平不调制,只是让发射管截止便可,这一点很重要), 接着是4.5ms 低电平。后面跟着一个 C0,其脉冲代表 0。


           下图的一个波形代表引导码的 4.5ms高, 接着是4.5ms 低电平。后面跟着一个 C0,其脉冲代表 1。


image


                              上图分别为 0 和 1 的脉冲宽带形式,其中高电平其实里面包含了更小的调制脉冲(大概是 0.56ms /(12 /455KHZ) =21 个调制脉冲)


 


image


                                                                   编码的一帧长度为 108ms


 


image
  上图已引导码为例,说明了其高电平实际是调制信号,而低电平不调制。当接收头受到调制的红外光时,便将其解调,对应为输出低电平,而当其没有接收到红外光,便输出高电平。所以引导码的低电平,下降沿 就可以用来产生单片机中断。


          大家按照上图的方法,分析数据0, 和 1  其实也是一样的原理,一个新的数据为开始总是以以个下降沿开始的,所以这就为我们的程序编写提供了便利。采用外部中断 + 定时器,便可以完整无误的刻画出遥控器发来的任何编码序列。


 


       下面我将以一个实际的解码程序来具体分析其原理。主要是在AT89S52单片机上开发,代码非常简单,解码的核心代码仅是蓝色部分,小巧精悍。


//++++++++++++++++++++++++++++++++++//YangYong_Infrared.h


#ifndef __YangYong_Infrared_H__
#define __YangYong_Infrared_H__

#ifndef uchar
#define  uchar unsigned char
#endif

#ifndef uint
#define  uint unsigned int
#endif

#ifndef InfraredDatas
typedef struct InfraredDatas
{
   uchar Address0;
   uchar Address1;
   uchar Code;
   uchar UnCode;

} InfraredDatas;
#endif

#define  ON                             0xff
#define  OFF                            0x00

/*#define  Lead_Code                      0x01
#define  ZERO_Code                      0x02
#define  ONE_Code                       0x03
#define  Data_Code                      0x04 */

//#define  Debug_Mode                     ON
//#define  DEBUG                          ON
#define  Debug_Mode                     OFF
#define  DEBUG                          OFF

/*#define  Timer0_TH0                        0xff //11.0592MHz晶振
#define  Timer0_TL0                        0x49   */
#define  Timer0_TH0                        0xfe    //22.1184MHz晶振
#define  Timer0_TL0                        0x92

#define  Time_Counter_MAX                400
#define  Infrarde_Lead_Counter_MAX        75        //42    调整此处的引导码计数区间 提高准确度
#define  Infrarde_Lead_Counter_MIN        25               
#define  Infrarde_ZERO_Counter_MAX        8        //5
#define  Infrarde_ZERO_Counter_MIN        2       
#define  Infrarde_ONE_Counter_MAX        17        //12

#endif

//++++++++++++++++++++++++++++++++++//YangYong_Infrared.h


 


//++++++++++++++++++++++++++++++++++//main.c


#include "reg52.h"

#define uchar unsigned char
#define uint  unsigned int      
extern void InitInterrupt_INT0(void);
//extern void InitInterrupt_INT1(void);
extern void InitTimer0(void);
extern void InitUart(void);

extern void InfraredDatas_Judge(void);
extern void Uart_Send_Byte(uchar Data);

//the main fun
void main(void)
{
  P2 =0;
  //InitInterrupt_INT1();
  InitInterrupt_INT0();
  InitTimer0();
  InitUart();

  EA=1;             //开总中断

  Uart_Send_Byte(0xc7);
  Uart_Send_Byte('\n');

  while(1)
  {
    InfraredDatas_Judge();

    //if you have other code ,add your other code here

  }   

}







//++++++++++++++++++++++++++++++++++//main.c


 


 


//++++++++++++++++++++++++++++++++++//Interrupt.c


#include "reg52.h"


#define uchar unsigned char
#define uint  unsigned int      
extern void Receive_Infrared_Datas(void);

void InitInterrupt_INT0(void)
{

   IT0=1;     //INT1下降沿中断
   EX0=1;     //允许INT1中断
}

//int0 interrupt
void int0(void) interrupt 0
{
//add your code here
   Receive_Infrared_Datas();

}   

/++++++++++++++++++++++++++++++++++//Interrupt.c

 

/++++++++++++++++++++++++++++++++++//InfraredDatas_Judge.c

#include "YangYong_Infrared.h"

//extern uchar DataBuffer[4];

extern InfraredDatas Infrared_Datas;

extern uchar ReceiveDataSucessful;

extern void Private_Function0 (void);
extern void Private_Function1 (void);
extern void Private_Function2 (void);
extern void Private_Function3 (void);
extern void Private_Function4 (void);
extern void Private_Function5 (void);
extern void Private_Function6 (void);
extern void Private_Function7 (void);
extern void Private_Function8 (void);
extern void Private_Function9 (void);
extern void Private_Function10(void);
extern void Private_Function11(void);
extern void Private_Function12(void);
extern void Private_Function13(void);
extern void Private_Function14(void);
extern void Private_Function15(void);
extern void Private_Function16(void);
extern void Private_Function17(void);
extern void Private_Function18(void);
extern void Private_Function19(void);
extern void Private_Function20(void);
extern void Private_Function21(void);
extern void Private_Function22(void);
extern void Private_Function23(void);
extern void Private_Function24(void);
extern void Private_Function25(void);
extern void Private_Function26(void);
extern void Private_Function27(void);
extern void Private_Function28(void);
extern void Private_Function29(void);
extern void Private_Function30(void);
extern void Private_Function31(void);

void InfraredDatas_Judge(void)
{
  if(ReceiveDataSucessful ==0xff)
  {
     ReceiveDataSucessful =0x00;

     /*if( Infrared_Datas.Address0 != 0x0e )
        return;   
       if( Infrared_Datas.Address1 != 0x0e )
        return;    */
     if( Infrared_Datas.Code !=  ~Infrared_Datas.UnCode )
       return;

     switch( Infrared_Datas.Code )
     {
        case 0x00: Private_Function0();
                   break;

        case 0x01: Private_Function1();
                   break;

        case 0x02: Private_Function2();
                   break;

        case 0x03: Private_Function3();
                   break;

        case 0x04: Private_Function4();
                   break;

        case 0x05: Private_Function5();
                   break;

        case 0x06: Private_Function6();
                   break;

        case 0x07: Private_Function7();
                   break;

        case 0x08: Private_Function8();
                   break;

        case 0x09: Private_Function9();
                   break;

        case 0x0a: Private_Function10();
                   break;

        case 0x0b: Private_Function11();
                   break;

        case 0x0c: Private_Function12();
                   break;

        case 0x0d: Private_Function13();
                   break;

        case 0x0e: Private_Function14();
                   break;

        case 0x0f: Private_Function15();
                   break;

        case 0x10: Private_Function16();
                   break;

        case 0x11: Private_Function17();
                   break;

        case 0x12: Private_Function18();
                   break;

        case 0x13: Private_Function19();
                   break;

        case 0x14: Private_Function20();
                   break;

        case 0x15: Private_Function21();
                   break;

        case 0x16: Private_Function22();
                   break;

        case 0x17: Private_Function23();
                   break;
        case 0x18: Private_Function24();
                   break;

        case 0x19: Private_Function25();
                   break;

        case 0x1a: Private_Function26();
                   break;

        case 0x1b: Private_Function27();
                   break;

        case 0x1c: Private_Function28();
                   break;

        case 0x1d: Private_Function29();
                   break;

        case 0x1e: Private_Function30();
                   break;

        case 0x1f: Private_Function31();
                   break;

        default :  break;

     }

  }

}

 

/++++++++++++++++++++++++++++++++++//InfraredDatas_Judge.c

 

 

/++++++++++++++++++++++++++++++++++//Time.c

#include "reg52.h"
#include "YangYong_Infrared.h"

extern uint TimeCounter ;

extern void ReceiveFail(void);

//timer init
void InitTimer0(void)    
{
   TMOD=0x1;
   TH0 =Timer0_TH0;
   TL0 =Timer0_TL0;
   //TR0=1;
   //ET0=1;            
}

//timer0/counter0 interrupt
void timer0(void) interrupt 1
{
   TH0 =Timer0_TH0;
   TL0 =Timer0_TL0;
//add your code here.
   //P0 = ~P0;
   TimeCounter++;
   if(TimeCounter >Time_Counter_MAX) ReceiveFail();

}

/++++++++++++++++++++++++++++++++++//Time.c

 

 

/++++++++++++++++++++++++++++++++++//Infrared.h

#define uchar unsigned char
#define uint  unsigned int

typedef struct InfraredDatas
{
   uchar Address0;
   uchar Address1;
   uchar Code;
   uchar UnCode;

} InfraredDatas

/++++++++++++++++++++++++++++++++++//Infrared.h

 

 

/++++++++++++++++++++++++++++++++++//Infrared.c

#include "reg52.h"
#include "YangYong_Infrared.h"

#define uchar unsigned char
#define uint  unsigned int

//uchar DataBuffer[4];
uchar ReceiveData;
uint TimeCounter =0;
uchar LeadCode =0;
uchar ReceiveBit =0;
uchar ReceiveDataSucessful =0x00;
uchar Debug =0x00;

InfraredDatas Infrared_Datas;

extern void Uart_Send_Byte(uchar Data);

void Receive_Infrared_Datas(void)
{   
#if Debug_Mode  == ON                        
   Debug =DEBUG;
#endif    

  if(LeadCode ==0)
  {   
     if( (TimeCounter > Infrarde_Lead_Counter_MIN) && (TimeCounter < Infrarde_Lead_Counter_MAX) )   //42
   //这里判断引导码的长度区间是否合法
     {

#if Debug_Mode  == ON
  // #if Debug_Number  == Lead_Code
    if(Debug !=0x00)
      Uart_Send_Byte( TimeCounter );
      //向串口发送此遥控器的引导码长度计数,发布调整最大,最小区间,进一步提高接收准确性
//  #endif

#endif

       LeadCode =0xff;
       TimeCounter =0;
       return;         //接收引导码成功
     }
    else   
    {
       TR0=1;
       ET0=1;          //开定时器0
       return;         //接收引导码失败
    }
  }
  else
  {
    ReceiveData >>=1;
    if( (TimeCounter > Infrarde_ZERO_Counter_MIN) && (TimeCounter < Infrarde_ZERO_Counter_MAX) )      //5
   //判读数据位是否 符合 0
    {

#if Debug_Mode  == ON
//  #if Debug_Number  == ZERO_Code
    if(Debug !=0x00)
      Uart_Send_Byte( TimeCounter ); 
//  #endif

#endif

      ReceiveData &= 0x7f;
    }
    else
    {
      if(TimeCounter < Infrarde_ONE_Counter_MAX)       //12
      //判读数据位是否 符合 1
     {

#if Debug_Mode  == ON
  // #if Debug_Number  == ONE_Code
    if(Debug !=0x00)
      Uart_Send_Byte( TimeCounter );   
//  #endif

#endif

        ReceiveData |= 0x80; 
      }
    }        
     ReceiveBit++;
     TimeCounter =0;
  }
  switch(ReceiveBit)  
//判断已接收的数据位,保存相应的数据
  {
    case 8:  //DataBuffer[0] = ReceiveData;
             Infrared_Datas.Address0 = ReceiveData;        
             break;       
    case 16: //DataBuffer[1] = ReceiveData;
             Infrared_Datas.Address1 = ReceiveData;   
             break;
    case 24: //DataBuffer[2] = ReceiveData;
              Infrared_Datas.Code = ReceiveData;
             break;       
    case 32: {
               //DataBuffer[3] = ReceiveData;
               Infrared_Datas.UnCode = ReceiveData;
               ReceiveBit =0;
               TimeCounter =0;
               LeadCode =0;
               ReceiveDataSucessful =0xff;
               TR0 =0;     //关定时器0
               ET0 =0;
#if Debug_Mode  == ON
//   #if Debug_Number  == Data_Code
     if(Debug !=0x00)
                   //调试用,向串口发送接收的数据
    {
       Uart_Send_Byte(Infrared_Datas.Address0);
       Uart_Send_Byte(Infrared_Datas.Address1);
       Uart_Send_Byte(Infrared_Datas.Code);
       Uart_Send_Byte(Infrared_Datas.UnCode);
       Uart_Send_Byte('\n');
     }
//  #endif

#endif        
             }
             break;                                                                    
    default: break;
  }
}



void ReceiveFail(void)    //接收数据失败
{
  ReceiveBit =0;
  TimeCounter =0;
  LeadCode =0; 


  TR0 =0;     //关定时器0
  ET0 =0;
}


 


/++++++++++++++++++++++++++++++++++//Infrared.c


 


 


 


/++++++++++++++++++++++++++++++++++//Uart.c


#include "reg51.h"

#define uchar unsigned char
#define uint  unsigned int   

uchar SendBusy =0x00;

void Uart_Send_Byte(uchar Data);

void Uart_Send_Byte(uchar Data)
{

  while(SendBusy !=0 );
  SBUF = Data;
  SendBusy =0xff;

}

//UART init
void InitUart(void)
  {
    TMOD|=0x20;
    SCON=0x50;
    //TH1=0xfd;        //11.0592MHz晶振
    //TL1=0xfd;
    TH1=0xfa;        //22.1184MHz晶振
    TL1=0xfa;

    TR1=1;
    ES=1;

    PS =1;
  }

//uart interrupt
void uart(void) interrupt 4
{
   if(RI)
    {
      RI=0;
      //add your code here.

    }
   else
   {
     TI=0;
     SendBusy =0;
   }
}

/++++++++++++++++++++++++++++++++++//Uart.c

 

 

          在以前条件缺乏,没有数字示波器,所以调试红外超麻烦。而此红外编解码程序的解码正确率非常高。希望对大家有所帮助。

          附件中将会提供完整的过程文件,直接编译便可。如有疑问,请留言。


          附件中将会提供 一份常见遥控器编码格式资料。




 


 

广告

文章评论 11条评论)

登录后参与讨论

1228265596_742131779 2016-3-21 21:43

感谢!!

755909667_788516658 2014-1-19 20:09

不错,赞一个

553463505_376033802 2013-12-17 08:43

不能下载啊!!!!

wm360459196_324633733 2013-11-18 11:10

感谢分享!!!!!!!

wml19861003_715195148 2013-8-31 12:38

最近在学习红外,谢谢楼主分享资料

1838471670_236114197 2013-2-27 17:50

最近用到红外解码,自己的程序有点瑕疵。

billzhu_345737527 2012-7-23 18:48

太复杂了吧

mousebat0000_275491424 2012-7-10 13:53

太感谢楼主了。

tuzi2088_241537477 2012-6-25 11:49

真的很感谢,特别需要这东西,而且嗯很精准

cat_li 2010-11-13 20:07

收藏了,呵呵,一直对这个搞得不是很清楚,多谢楼主分享
相关推荐阅读
singlechip 2013-07-03 14:54
[博客大赛]四层板搞定--AT91SAM9X35 第二版
    花了一个月时间,又重新设计了一款AT91SAM9X35的板子。针对第一版做了以下改进。   1 200PIN1.27MM的双排插针换成了更可靠的BTB-80 插针   ...
singlechip 2013-04-25 09:48
[博客大赛]四层板搞定--AT91SAM9X35
  1月15号从零开始设计原理图,到今天4月15号,刚好3个月,终于用4层板设计完成了AT...
singlechip 2013-04-09 18:06
准备花3个月时间用四层板画--AT91SAM9X35
  最近终于腾出手来,比较看好AT91SAM9X35这颗芯片,打算好好弄弄。先从画板开始吧?虽然手上有芯片商提供的开发板,但总觉得不能自己画出一块板出来,技术永远不会属于自己。所以准...
singlechip 2013-02-01 15:01
PCB设计资料汇总
http://pan.baidu.com/share/link?shareid=202013&uk=3826038294#dir/path=%2FPCB%E8%AE%BE%E8%AE%A1...
singlechip 2012-12-12 16:00
[ti博客大赛]基于MSP430装备监控型冲击震动记录仪
概述 装备监控型冲击振动记录仪(以下简称记录仪)用于长时间监视装备在储存过程中受到的冲击振动,对超过阈值的冲击事件进行记录和报警。记录的事件包括对事件的发生强度和发生时间进行实时的记录。当需要...
singlechip 2012-12-12 16:00
[ti博客大赛]当年纯手工打造的---基于MSP430姿态调整仪
当年纯手工打造的---基于MSP430姿态调整仪。。。。采用三轴加速度传感器。。。 感兴趣的朋友可以到我博客探讨。。。。 http://bbs.ednchina.com/BLOG_ARTI...
我要评论
11
0
广告