红外线遥控是目前使用最广泛的一种通信和遥控手段。由于红外线遥控装置具有体积小、功耗低、功能强、成本低等特点,因而,继彩电、录像机之后,在录音机、音响设备、空凋机以及玩具等其它小型电器装置上也纷纷采用红外线遥控。工业设备中,在高压、辐射、有毒气体、粉尘等环境下,采用红外线遥控不仅完全可靠而且能有效地隔离电气干扰。
在做单片机作品的时候如果对这红外方面的知识理解了很容易把红外的功能加上去!我就觉得这是一个很不错的工具,所以拿出来跟大家分享。当然网上是很多资料的,我也参考过不少人的程序,现在也贡献出我的一点,也是我自己最常用的,适不适合用就请多多包涵了。
遥控发射器专用芯片很多,根据编码格式可以分成两大类,这里我们以运用比较广泛,解码比较容易的一类来加以说明,现以日本NEC的uPD6121G组成发射电路为例说明编码原理。当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:
采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”。
上述“0”和“1”组成的32位二进制码经38kHz的载频进行二次调制以提高发射效率,达到降低电源功耗的目的。然后再通过红外发射二极管产生红外线向空间发射,UPD6121G产生的遥控编码是连续的32位二进制码组,其中前16位为用户识别码,能区别不同的电器设备,防止不同机种遥控码互相干扰。该芯片的用户识别码固定为十六进制01H;后16位为8位操作码(功能码)及其反码。UPD6121G最多额128种不同组合的编码。
遥控器在按键按下后,周期性地发出同一种32位二进制码,周期约为108ms。一组码本身的持续时间随它包含的二进制“0”和“1”的个数不同而不同,大约在45~63ms之间。
当一个键按下超过36ms,振荡器使芯片激活,将发射一组108ms的编码脉冲,这108ms发射代码由一个起始码(9ms),一个结果码(4.5ms),低8位地址码(9ms~18ms),高8位地址码(9ms~18ms),8位数据码(9ms~18ms)和这8位数据的反码(9ms~18ms)组成。如果键按下超过108ms仍未松开,接下来发射的代码(连发代码)将仅由起始码(9ms)和结束码(2.5ms)组成。
代码格式(以接收代码为准,接收代码与发射代码反向)
①位定义
②单发代码格式
③连发代码格式
代码宽度算法:
16位地址码的最短宽度:1.12×16=18ms 16位地址码的最长宽度:2.24ms×16=36ms
易知8位数据代码及其8位反代码的宽度和不变:(1.12ms+2.24ms)×8=27ms
∴32位代码的宽度为(18ms+27ms)~(36ms+27ms)
1. 解码的关键是如何识别“0”和“1”,从位的定义我们可以发现“0”、“1”均以0.56ms的低电平开始,不同的是高电平的宽度不同,“0”为0.56ms,“1”为1.68ms,所以必须根据高电平的宽度区别“0”和“1”。如果从0.56ms低电平过后,开始延时,0.56ms以后,若读到的电平为低,说明该位为“0”,反之则为“1”,为了可靠起见,延时必须比0.56ms长些,但又不能超过1.12ms,否则如果该位为“0”,读到的已是下一位的高电平,因此取(1.12ms+0.56ms)/2=0.84ms最为可靠,一般取0.84ms左右均可。
2. 根据码的格式,应该等待9ms的起始码和4.5ms的结果码完成后才能读码。
一体化红外线接收器是一种集红外线接收和放大于一体,不需要任何外接元件,就能完成从红外线接收到输出与TTL电平信号兼容的所有工作,而体积和普通的塑封三极管大小一样,它适合于各种红外线遥控和红外线数据传输。
下面是解码程序:
/******************************************************************
红外解码程序
1602液晶显示
程序注释:傅春平
1602的注释和显示的注释我就没写上去了
希望对学习者有所帮助
挺好用的一个解码程序,当然肯定有比这个更好的,用与不用就看自己的了
我的好几件作品都用到了红外遥控 比如我的32*64双色多功能点阵钟
旋转POV数字钟的调时 大功率发光二级管可调7彩灯 圆形数字时钟
遥控车等 有了这个程序就可以很方便的移植到你所需要的作品上
适合范围:本程序经过测试,可以用到很多种遥控器上,凡是NEC格式的都差不多是这样
什么DVD 电视遥控器和空放遥控器都是这个格式的.空调有些不是,试一下就知道了
晶振11.0592M 单片机STC89C52 AT89S52都可以
使用不同晶振时应该算一下数值,怎么算的程序里有注释
其中11.0592M跟12M差不多的 不用改什么数值
红外接收自然是一体化接收头了
记得接线的时候把红外接收头的数据口接上拉电阻!这个很重要,千万别忘了!
*******************************************************************/
#include
#include
#include
#define TURE 1
#define FALSE 0
sbit IR=P3^2;//红外接口标志
sbit RS = P2^3;//Pin4
sbit RW = P2^4; //Pin5
sbit E = P2^5;//Pin6
#define Data P0 //数据端口
unsigned int hour,minute,second,count;
char code Tab[16]="0123456789ABCDEF";
char data TimeNum[]=" ";
char data Test1[]=" ";
unsigned char irtime;//红外用全局变量
bit irpro_ok,irok;
unsigned char IRcord[4];
unsigned char irdata[33];
void ShowString (unsigned char line,char *ptr);
void Delay(unsigned char mS);
void Ir_work(void);
void Ircordpro(void);
/**************************************************************/
void tim0_isr (void) interrupt 1 using 1//定时器0中断服务函数 定时器0的最大定时时间是
{
irtime++;
}
void ex0_isr (void) interrupt 0 using 0//外部中断0服务函数
{
static unsigned char i;
static bit startflag;
if(startflag)
{
/*****************************************************************************
我们知道定时器0方式2最大的定时时间为256us(此时的晶振是11.0592M的)所以算一下256*33=8.448ms跟9ms差不多接近就可以了
256*63=16.128ms 所以小于13.5<16.128 这些数据大概就可以了 没必要那么精确 但也不能太离谱
所以有了这样判断if(irtime<63&&irtime>=33)
*******************************************************************************/
if(irtime<63&&irtime>=33)//引导码 TC9012的头码,根据码的格式,应该等待9ms的起始码和4.5ms的结果码完成后才能读码
i=0; //引导码过后就是接受数据了,赋初值i=0
irdata[i]=irtime; //把接下来的总长32位地址码+数据码先保存在数组里
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
}
} //32位数据完成后就把标志位irok置1 表示数据接收完成
else
{irtime=0;startflag=1;}
}
void TIM0init(void)//定时器0初始化
{
TMOD=0x02;//定时器0工作方式2,TH0是重装值,TL0是初值
TH0=0x00;//reload value
TL0=0x00;//initial value
ET0=1;//开中断
TR0=1;
}
void EX0init(void)
{
IT0 = 1; // Configure interrupt 0 for falling edge on /INT0 (P3.2)
EX0 = 1; // Enable EX0 Interrupt
EA = 1;
}
void Ir_work(void)//红外键值散转程序
{
TimeNum[5] = Tab[IRcord[0]/16];
TimeNum[6] = Tab[IRcord[0]%16];
TimeNum[8] = Tab[IRcord[1]/16];
TimeNum[9] = Tab[IRcord[1]%16];
TimeNum[11] = Tab[IRcord[2]/16];
TimeNum[12] = Tab[IRcord[2]%16];
TimeNum[14] = Tab[IRcord[3]/16];
TimeNum[15] = Tab[IRcord[3]%16];
ShowString(1,TimeNum);
irpro_ok=0;
}
void Ircordpro(void)//红外码值处理函数
{
unsigned char i, j, k;
unsigned char cord,value;
k=1;
for(i=0;i<4;i++)//处理4个字节
{
for(j=1;j<=8;j++) //处理1个字节8位
{
cord=irdata[k];
//解码的关键是如何识别“0”和“1”,从位的定义我们可以发现“0”、“1”均以0.56ms的低电平开始,不同的是高电平的宽度不同,“0”为0.56ms,“1”为1.68ms
if(cord>7)//大于某值为1 //这里也可以算一下 256*7=1.792ms 我们从原理中知道1”为1.68ms的时间长度
{
value=value|0x80; //或上0x80 就为1
}
else
{
value=value; //否则就为0
}
if(j<8)
{
value=value>>1; //8为数据移位
}
k++; //字节数加1
}
IRcord[i]=value;
value=0;
}
//很多时候我没用下面这句来判断,我发觉直接把处理完毕标志位置1不比较了也可以的.效果不错
if((IRcord[2])==~IRcord[3]) //检测后两位是不是反码 如果是标志位置1
{
irpro_ok=1;//处理完毕标志位置1
}
}
void DelayUs(unsigned char us)//delay us
{
unsigned char uscnt;
uscnt=us>>1;
while(--uscnt);
}
void DelayMs(unsigned char ms)//delay Ms
{
while(--ms)
{
DelayUs(250);
DelayUs(250);
DelayUs(250);
DelayUs(250);
}
}
void WriteCommand(unsigned char c)
{
DelayMs(5);//short delay before operation
E=0;
RS=0;
RW=0;
_nop_();
E=1;
Data=c;
E=0;
}
void WriteData(unsigned char c)
{
DelayMs(5); //short delay before operation
E=0;
RS=1;
RW=0;
_nop_();
E=1;
Data=c;
E=0;
RS=0;
}
void ShowChar(unsigned char pos,unsigned char c)
{
unsigned char p;
if (pos>=0x10)
p=pos+0xb0; //是第二行则命令代码高4位为0xc
else
p=pos+0x80; //是第二行则命令代码高4位为0x8
WriteCommand (p);//write command
WriteData (c); //write data
}
void ShowString (unsigned char line,char *ptr)
{
unsigned char l,i;
l=line<<4;
for (i=0;i<16;i++)
ShowChar (l++,*(ptr+i));//循环显示16个字符
}
void InitLcd()
{
DelayMs(15);
WriteCommand(0x38); //display mode
WriteCommand(0x38); //display mode
WriteCommand(0x38); //display mode
WriteCommand(0x06); //显示光标移动位置
WriteCommand(0x0c); //显示开及光标设置
WriteCommand(0x01); //显示清屏
}
void main(void)
{
EX0init(); // Enable Global Interrupt Flag
TIM0init();
InitLcd();//
DelayMs(15);
sprintf(Test1,"fuchunpingzhizuo ");//the first line
ShowString(0,Test1);
sprintf(TimeNum,"Code ");//the second line
ShowString(1,TimeNum);
while(1)//主循环
{
if(irok)
{
Ircordpro();
irok=0;
}
if(irpro_ok)//step press key
{
Ir_work();
}
}
}
硬件接线图:
实物图