基于STC89C52的红外遥控器编程
硬件及原理
基于单片机的红外通信是遵循NEC协议的一种通信,本篇文章主要讲述如何遵循NEC协议设计一个简易的遥控器,在软件编程之前,先将我们的硬件设计介绍如下。
上图是一个红外发送电路,这个电路是整个遥控器的核心。我们所发送的0和1就是通过发射管TSAL6200来发送出去的。网络标号TXD接我们单片机的引脚P2^1,该引脚负责将我们所需发送的信号通过拉高或拉低实现。Ird38KHz接一个产生38KHz信号电路,38KHz信号是我们所需要的载波信号,我们的0和1(即基带信号)通过发射管将被调制成符合NEC协议下的调制信号。该38KHz信号产生电路设计如下图。
为了制作一个简易的遥控器,本文设计两个按键分别表示不同的两个按键1和2, 两个按键分别接我们单片机的引脚P1^1和P1^2。原理图如下:
下面就NEC协议做简单的分析。
NEC协议中规定,一次通信的起始是一个固定的引导码,紧接着是第一个8位的用户码,然后是第二个8位的用户码,紧跟着是我们编码后的8位的键值码,最后是键值码的反
码。如下图所示:
现在的问题是,到底单片机引脚TXD(后文代码中定义成IR)所要发送的0和1(基带信号)经过发射管后怎么表示呢(即调制信号)?
好,NEC协议是这么规定的,引导码是以持续9000us时间的低电平加上持续4500us的高电平来表示的,注意,是低电平在先。而0和1呢?是这样的,持续560us的低电平加上持续560us的高电平表示0,持续560us的低电平加上持续1680us的高电平则表示1。 注意,引导码和用户码和键值码之间是没有时间间隙的,也就是说引导码过后即是用户码,再即是键值码。 深刻的理解上面的这几点后,便可以写代码实现我们制作的遥控器了。
软件设计 我的设计思想是将发送引导码封装成一个函数,将发送一个0,发送一个1分别封装成一个函数,即实现模块化。将这三个函数写出后,剩下要做的只是对键值进行编码了。而编码是可以任意的,什么意思呢?也就是说,我们可以任意使用,比如用0xff和0x00表示我们的用户码,用0x3d表示我们的1,用0xe3表示我们的2,或者其它,这是我们的自由。用户码是为了区别不一样的遥控器而设定的。 这里,时间的控制我们用定时器0实现。电路使用12M的晶振,机器周期刚好是1us。
好,下面先将三个基本函数编写如下。 /*******发送引导码函数**********/ void SendGuideCode() { TH0 = 0xDC; //9000us TL0 = 0xD8; TR0 = 1; IR = 0; //拉低IR while(!(TF0 == 1)); //等待定时器溢出,即使IR持续低电平9000us IR = 1; TF0 = 0; TR0 = 0;
TH0 = 0xEE; //4500us TL0 = 0x6c; TR0 = 1; while(!(TF0 == 1)); //等待定时器溢出,即使IR持续高电平4500us IR = 0; TF0 = 0; TR0 = 0;
}
/*********发送0函数*********/ void SendCodeZero() / { TH0 = 0xFD; //560us TL0 = 0xD0; TR0 = 1; IR = 0; while(!(TF0 == 1)); IR = 1; TF0 = 0; TR0 = 0; TH0 = 0xFD; //560us TL0 = 0xD0; TR0 = 1; while(!(TF0 == 1)); IR = 0; TF0 = 0; TR0 = 0; }
/*********发送1函数*********/ void SendCodeOne() { TH0 = 0xFD; //560us TL0 = 0xD0; TR0 = 1; IR = 0; while(!(TF0 == 1)); IR = 1; TF0 = 0; TR0 = 0; TH0 = 0xF9; //1680us TL0 = 0x70; TR0 = 1; while(!(TF0 == 1)); IR = 0; TF0 = 0; TR0 = 0; }
完成了上面的三个基本函数,那我们就可以遵循NEC协议将每个8位一组的用户码,或者键值码进行封装了。话不多少,看代码。 /*********用户码函数*********/
void UsrCode() //发送用户码 11111111 00000000 { unsigned char i;
for(i=0;i<8;i++) { SendCodeOne(); } for(i=0;i<8;i++) { SendCodeZero(); } }
/*********键值1函数********/
void Key1() //0x3d 0011 1101 ~0x3d 1100 0010 { SendCodeZero();
SendCodeZero(); SendCodeOne(); SendCodeOne(); SendCodeOne(); SendCodeOne();
SendCodeZero(); SendCodeOne(); SendCodeOne(); SendCodeOne(); SendCodeZero();
SendCodeZero(); SendCodeZero(); SendCodeZero(); SendCodeOne(); SendCodeZero(); }
/*********键值2函数********/
void Key2() //0xe3 1110 0011 ~0xe3 0001 1100 { SendCodeOne();
SendCodeOne(); SendCodeOne();
SendCodeZero(); SendCodeZero(); SendCodeZero(); SendCodeOne(); SendCodeOne();
SendCodeZero(); SendCodeZero(); SendCodeZero(); SendCodeOne(); SendCodeOne(); SendCodeOne(); SendCodeZero(); SendCodeZero();
}
这样,红外发送相关的函数都完成了,剩下的就是按键扫描了。函数如下。 /*********按键扫描函数********/ unsigned char KeyScan() //返回值是按下按键的按键值,没按下返回100,即不发送 { unsigned char key = 100;
unsigned char keybuf [2]={ 1,1};
static unsigned char backup[2]= { 1,1};
keybuf [0]= key1; if(keybuf[0] != backup[0]) { delay(100); if(keybuf[0] == key1) {
if(backup[0] == 0)
key = 1; } } if(keybuf[1] != backup[1]) { delay(100); if(keybuf[1] == key2) {
if(backup[1] == 0)
key = 2; } } return key;