51_红外遥控
红外遥控器,顾名思义,是通过红外光来进行数据传输的。被广泛应用在各种家电产品上,例如电视、空调、车载MP3等。它是如此的普遍,以至于让人都快忘记了它的存在。 只要温度高于绝对零度(-273.15°C)的物体都会发出红外光,因此它无处不在。因此,使用红外光进行通信时,需要对光波有一些特殊的要求,这样才能使它能够在众多的同类信号中被识别出来。 首先是波长,一般来说红外发射管发出的光波波长以850nm和940nm两种比较常见。 然后是频率,发射端以一个固定的频率来发射红外光,一般以37.91KHZ比较常见,也有的地方直接说成是38KHZ。同时,接收端只识别该频率下的信号,只要发射频率与接收频率正负相差不超过1KHZ,都可以正常工作;如果相差超过2KHZ,会出现失灵或者距离短等情况。
第二类接收管如图所示,也是要使用的接收管。和前面几种管子的最大区别在于它有三只脚,而功能也大大增强了。它的内部电路包括红外监测二极管,放大器,限幅器,带通滤波器,积分电路,比较器等。 通过内部电路,还原出发射端的信号波形,可以直接被单片机使用。因此也被称为一体化红外接收头。
也就是说,它输出的是符合数字电路要求的数字信号了,可以直接拿来使用。所以,虽然外型上只是多了一个引脚,但实际内部功能增加了很多很多,大大简化了电路设计者的工作。另外,此类接收头的内部放大增益比较大,很容易引起干扰,因此一般厂家建议在供电脚加上4.7uf以上的电容进行滤波。 常用型号有IRM3638、HS0038和VS1838等。其接口电路如图所示
这里使用的遥控器用的是NEC协议,因此这里着重讲解该协议下信号是如何传播的。首先,遥控器内部一般会使用455KHZ的晶体做为振荡源,通过内部分频电路将其调制为频率37.91KHZ、占空比三分之一的振荡信号。 遥控器闲置时,发射端无输出。当有按键按下时,会发送一串信号驱动红外发射端发射红外线,信号格式如下:一段引导码、两个字节的用户码(由厂家定义)、一个字节的按键数据、一个字节的按键数据反码,最后跟一个停止位(编程时基本不考虑)。
有两个事情要注意: 1、该图是遥控器发出的信号格式,并不是接收端收到的信号格式,两者是不一样的; 2、图中高电平的位置并不是真的高电平,而是被频率37.91KHZ、三分之一占空比的信号填充的。 那么接收端收到的信号是什么样?首先,闲置状态下,它是高电平的。当收到37.91KHZ的红外光时,接收端调制出的信号是低电平,无红外光时,又恢复为高电平。所以,将上图中的信号全部取反,即为红外接收端输出的信号格式。
接下来,了解一下数据的传输过程中是如何定义0和1这两种状态的。以接收端的状态来考虑,空闲的状态下引脚输出高电平。 当表示一个bit位为0时,先输出一个0.56ms的低电平,然后输出一个0.565ms的高电平; 当表示一个bit位为1时,先输出一个0.56ms的低电平,然后输出一个1.69ms的高电平。
要想识别发送的指令是什么,就要先知道遥控器上每个按键对应的数据码是什么,而这部分内容,需要跟遥控器供应商索取。记住,即便是外形一模一样的遥控器,只要厂家不同,也有可能相同位置的按键数据码不一样。这里选购的遥控器用户码为0x00FF,按键对应的数据码如图所示
/******************************************************************************* * 实验名 : 1602显示红外线值实验 * 使用的IO : 电机用P1口,键盘使用P3.0、P3.1、P3.2、P3.3 * 实验效果 : LCD1602显示出读取到的红外线的值 * 注意 : *******************************************************************************/ #include<reg51.h> #include"lcd.h" sbit IRIN=P3^2; unsigned char code CDIS1[13]={" Red Control "}; unsigned char code CDIS2[13]={" IR-CODE:--H "}; unsigned char IrValue[6]; unsigned char Time; void IrInit(); void DelayMs(unsigned int ); /******************************************************************************* * 函数名 : main * 函数功能 : 主函数 * 输入 : 无 * 输出 : 无 *******************************************************************************/ void main() { unsigned char i; IrInit(); LcdInit(); LcdWriteCom(0x80); for(i=0;i<13;i++) { LcdWriteData(CDIS1[i]); } LcdWriteCom(0x80+0x40); for(i=0;i<13;i++) { LcdWriteData(CDIS2[i]); } while(1) { IrValue[4]=IrValue[2]>>4; //高位 IrValue[5]=IrValue[2]&0x0f; //低位 if(IrValue[4]>9) { LcdWriteCom(0xc0+0x09); //设置显示位置 LcdWriteData(0x37+IrValue[4]); //将数值转换为该显示的ASCII码 } else { LcdWriteCom(0xc0+0x09); LcdWriteData(IrValue[4]+0x30); //将数值转换为该显示的ASCII码 } if(IrValue[5]>9) { LcdWriteCom(0xc0+0x0a); LcdWriteData(IrValue[5]+0x37); //将数值转换为该显示的ASCII码 } else { LcdWriteCom(0xc0+0x0a); LcdWriteData(IrValue[5]+0x30); //将数值转换为该显示的ASCII码 } } } /******************************************************************************* * 函数名 : DelayMs() * 函数功能 : 延时 * 输入 : x * 输出 : 无 *******************************************************************************/ void DelayMs(unsigned int x) //0.14ms误差 0us { unsigned char i; while(x--) { for (i = 0; i<13; i++) {} } } /******************************************************************************* * 函数名 : IrInit() * 函数功能 : 初始化红外线接收 * 输入 : 无 * 输出 : 无 *******************************************************************************/ void IrInit() { IT0=1;//下降沿触发 EX0=1;//打开中断0允许 EA=1; //打开总中断 IRIN=1;//初始化端口 } /******************************************************************************* * 函数名 : ReadIr() * 函数功能 : 读取红外数值的中断函数 * 输入 : 无 * 输出 : 无 *******************************************************************************/ void ReadIr() interrupt 0 { unsigned char j,k; unsigned int err; Time=0; DelayMs(70); if(IRIN==0) //确认是否真的接收到正确的信号 { err=1000; //1000*10us=10ms,超过说明接收到错误的信号 /*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时 侯,程序死在这里*/ while((IRIN==0)&&(err>0)) //等待前面9ms的低电平过去 { DelayMs(1); err--; } if(IRIN==1) //如果正确等到9ms低电平 { err=500; while((IRIN==1)&&(err>0)) //等待4.5ms的起始高电平过去 { DelayMs(1); err--; } for(k=0;k<4;k++) //共有4组数据 { for(j=0;j<8;j++) //接收一组数据 { err=60; while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去 // while (!IRIN) { DelayMs(1); err--; } err=500; while((IRIN==1)&&(err>0)) //计算高电平的时间长度。 { DelayMs(1);//0.14ms Time++; err--; if(Time>30) { EX0=1; return; } } IrValue[k]>>=1; //k表示第几组数据 if(Time>=8) //如果高电平出现大于565us,那么是1 { IrValue[k]|=0x80; } Time=0; //用完时间要重新赋值 } } } if(IrValue[2]!=~IrValue[3]) { return; } } }