编码格式
现有的红外遥控包括两种方式: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
为了提高遥控器的发射具体,肯定得提高红外发射管的功率,如果给发射管发以未经调制的信号,一来发射距离会非常短;二来很容易把发射管给烧掉。所以一般我们都会将发射的信号进行调制后再发送出去。
从图1,我们可以知道,在主频为455KHz下将单个脉冲调制为37.91KHz .
调制频率 f(CAR) =1 /Tc =f(osc) /12 =455KHz /12 =37.91666……. 也就是常用的红外调制频率 38KHz
其占空比为 1/3
这是发送的一个完整帧,依次为 引导码, 用户识别码, 用户识别码反码,操作码,操作码反码。
上图的一个波形代表引导码的 4.5ms高(其实里面包含更小的调制脉冲,记住只要是红外发送的所有高电平都是经过调制的,而低电平不调制,只是让发射管截止便可,这一点很重要), 接着是4.5ms 低电平。后面跟着一个 C0,其脉冲代表 0。
下图的一个波形代表引导码的 4.5ms高, 接着是4.5ms 低电平。后面跟着一个 C0,其脉冲代表 1。
上图分别为 0 和 1 的脉冲宽带形式,其中高电平其实里面包含了更小的调制脉冲(大概是 0.56ms /(12 /455KHZ) =21 个调制脉冲)
编码的一帧长度为 108ms
上图已引导码为例,说明了其高电平实际是调制信号,而低电平不调制。当接收头受到调制的红外光时,便将其解调,对应为输出低电平,而当其没有接收到红外光,便输出高电平。所以引导码的低电平,下降沿 就可以用来产生单片机中断。
大家按照上图的方法,分析数据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
在以前条件缺乏,没有数字示波器,所以调试红外超麻烦。而此红外编解码程序的解码正确率非常高。希望对大家有所帮助。
附件中将会提供完整的过程文件,直接编译便可。如有疑问,请留言。
附件中将会提供 一份常见遥控器编码格式资料。
用户1867386 2016-3-21 21:43
用户429046 2014-1-19 20:09
用户1672210 2013-12-17 08:43
用户1721727 2013-11-18 11:10
感谢分享!!!!!!!
用户1693770 2013-8-31 12:38
用户602826 2013-2-27 17:50
用户377235 2012-7-23 18:48
太复杂了吧
用户532066 2012-7-10 13:53
用户421916 2012-6-25 11:49
真的很感谢,特别需要这东西,而且嗯很精准
用户42640 2010-11-13 20:07