红外遥控小车.docx
《红外遥控小车.docx》由会员分享,可在线阅读,更多相关《红外遥控小车.docx(16页珍藏版)》请在冰豆网上搜索。
![红外遥控小车.docx](https://file1.bdocx.com/fileroot1/2023-1/4/3fb8ea8c-2f57-4f7b-9a4e-de402d78fd28/3fb8ea8c-2f57-4f7b-9a4e-de402d78fd281.gif)
红外遥控小车
红外遥控小车的制作
指示灯
51单片机
一体化红外接收头VS1838B
红外遥控器
电机驱动模块
第一部分:
红外遥控的解码
红外线遥控是目前使用最广泛的一种通信和遥控手段。
由于红外线遥控装置具有体积小、功耗低、功能强、成本低等特点,因而,继彩电、录像机之后,在录音机、音响设备、空凋机以及玩具等其它小型电器装置上也纷纷采用红外线遥控。
工业设备中,在高压、辐射、有毒气体、粉尘等环境下,采用红外线遥控不仅完全可靠而且能有效地隔离电气干扰。
通用红外遥控系统由发射和接收两大部分组成,应用编/解码专用集成电路芯片来进行控制操作,如图1所示。
发射部分包括键盘矩阵、编码调制、LED红外发送器;接收部分包括光、电转换放大器、解调、解码电路。
遥控发射器及其编码:
遥控发射器专用芯片很多,根据编码格式可以分成两大类,这里我们以运用比较广泛,解码比较容易的一类来加以说明,现以兼容NEC的uPD6121G芯片发射码格式的芯片组成发射电路为例说明编码原理。
当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。
这种遥控码具有以下特征:
采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图2所示。
上述“0”和“1”组成的32位二进制码经38kHz的载频进行二次调制以提高发射效率,达到降低电源功耗的目的。
然后再通过红外发射二极管产生红外线向空间发射,如图。
UPD6121G产生的遥控编码是连续的32位二进制码组,其中前16位为用户识别码,能区别不同的电器设备,防止不同机种遥控码互相干扰。
芯片厂商把用户识别码固定为十六进制的一组数;后16位为8位操作码(功能码)及其反码。
UPD6121G最多额128种不同组合的编码。
遥控器在按键按下后,周期性地发出同一种32位二进制码,周期约为108ms。
一组码本身的持续时间随它包含的二进制“0”和“1”的个数不同而不同,大约在45~63ms之间,图4为发射波形图。
当一个键按下超过36ms,振荡器使芯片激活,将发射一组108ms的编码脉冲,
这108ms发射代码由一个起始码(9ms),一个结果码(4.5ms),低8位地址码(9ms~18ms),高8位地址码(9ms~18ms),8位数据码(9ms~18ms)和这8位数据的反码(9ms~18ms)组成。
如果键按下超过108ms仍未松开,接下来发射的代码(连发代码)将仅由起始码(9ms)和结束码(2.5ms)组成。
接收器及解码:
一体化红外线接收器是一种集红外线接收和放大于一体,不需要任何外接元件,就能完成从红外线接收到输出与TTL电平信号兼容的所有工作,而体积和普通的塑封三极管大小一样,它适合于各种红外线遥控和红外线数据传输。
实物如下图所示:
电源端
接地端
信号输出端
注意:
一体化红外接收头在使用时,应通过一个限流电阻与电源连接。
红外接收头将38K载波信号过虑,得到与发射代码反向接收代码。
也就是说
若发射端发射的是:
则接受端解析输出的是:
解码的关键是如何识别“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”,读到的已是下一位的高电平,
因此取0.56ms+(1.12-0.56)/2=0.84ms最为可靠,一般取0.84ms左右均可。
根据码的格式,应该等待9ms的起始码和4.5ms的结果码完成后才能读码。
第二部分:
程序中的子函数
1、指示灯函数:
//当一体化接收头接收到信号并解析成功后,指示灯闪烁一下
sbitzhishideng=P1^0;
voidzhishi()
{
unsignedchari=200,j=125;
zhishideng=0;
while(i--)
while(j--);
zhishideng=1;
}
2、中断控制函数:
//当有按键按下时,触发外部中断0,中断函数先对红外的引导码进行识别,若识别正确,则调用后面的解码函数对红外进行解码。
voidInt0(void)interrupt0
{
EX0=0;//关闭外中断0,不再接收二次红外信号的中断,只解码当前红外信号
TH0=0;//定时器T0的高8位清0
TL0=0;//定时器T0的低8位清0
TR0=1;//开启定时器T0
while(IR==0);//如果是低电平就等待,给引导码低电平计时
TR0=0;//关闭定时器T0
LowTime=TH0*256+TL0;//保存低电平时间
TH0=0;//定时器T0的高8位清0
TL0=0;//定时器T0的低8位清0
TR0=1;//开启定时器T0
while(IR==1);//如果是高电平就等待,给引导码高电平计时
TR0=0;//关闭定时器T0
HighTime=TH0*256+TL0;//保存引导码的高电平长度
if((LowTime>8500)&&(LowTime<9500)&&(HighTime>4200)&&(HighTime<4800))//如果是引导码,就开始解码,否则放弃。
{
if(DeCode()==1)//执行遥控解码功能
{
//此处是小车的功能实现代码
}
zhishi();
}
EX0=1;//开启外中断EX0
}
3、单片机解码红外函数:
//当正确识别后,函数返回1。
(注:
本小车的单片机使用的是12M的晶振)
bitDeCode(void)
{
unsignedchari,j;
unsignedchartemp;//储存解码出的数据
for(i=0;i<4;i++)//连续读取4个用户码和键数据码
{
for(j=0;j<8;j++)//每个码有8位数字
{
temp=temp>>1;//temp中的各数据位右移一位,因为先读出的是高位数据
TH0=0;//定时器清0
TL0=0;//定时器清0
TR0=1;//开启定时器T0
while(IR==0);//如果是低电平就等待,低电平计时
TR0=0;//关闭定时器T0
LowTime=TH0*256+TL0;//保存低电平宽度
TH0=0;//定时器清0
TL0=0;//定时器清0
TR0=1;//开启定时器T0
while(IR==1);//如果是高电平就等待
TR0=0;//关闭定时器T0
HighTime=TH0*256+TL0;//保存高电平宽度
if((LowTime<460)||(LowTime>660))return0;//如果低电平长度不在合理范围,则认为出错,停止解码
if((HighTime>460)&&(HighTime<660))temp=temp&0x7f;//如果高电平时间在560微秒左右,则该位是0。
if((HighTime>1500)&&(HighTime<1900))temp=temp|0x80;//如果高电平时间在1680微秒左右,则该位是1。
}
a[i]=temp;//将解码出的字节值储存在a[i]
}
if(a[2]==~a[3])return1;//验证键数据码和其反码是否相等,一般情况下不必验证用户码。
}
第三部分:
程序整体
1、首先从实现最简单的功能开始:
前进、后退和停止。
#include//包含单片机寄存器的头文件
sbitIR=P3^2;//将IR位定义为P3.2引脚
sbitzhishideng=P1^0;
sbitdianji1=P1^1;
sbitdianji2=P1^2;
unsignedchara[4];//储存用户码、用户反码与键数据码、键数据反码
unsignedintLowTime,HighTime;//储存高、低电平的宽度
voidzhishi()//指示灯函数
{
unsignedchari=200,j=125;
zhishideng=0;
while(i--)
while(j--);
zhishideng=1;
}
bitDeCode(void)//单片机解码函数
{
unsignedchari,j;
unsignedchartemp;//储存解码出的数据
for(i=0;i<4;i++)//连续读取4个用户码和键数据码
{
for(j=0;j<8;j++)//每个码有8位数字
{
temp=temp>>1;//temp中的各数据位右移一位,因为先读出的是高位数据
TH0=0;//定时器清0
TL0=0;//定时器清0
TR0=1;//开启定时器T0
while(IR==0);//如果是低电平就等待,低电平计时
TR0=0;//关闭定时器T0
LowTime=TH0*256+TL0;//保存低电平宽度
TH0=0;//定时器清0
TL0=0;//定时器清0
TR0=1;//开启定时器T0
while(IR==1);//如果是高电平就等待
TR0=0;//关闭定时器T0
HighTime=TH0*256+TL0;//保存高电平宽度
if((LowTime<460)||(LowTime>660))return0;//如果低电平长度不在合理范围,则认为出错,停止解码
if((HighTime>460)&&(HighTime<660))temp=temp&0x7f;//如果高电平时间在560微秒左右,则该位是0
if((HighTime>1500)&&(HighTime<1900))temp=temp|0x80;//如果高电平时间在1680微秒左右,则该位是1
}
a[i]=temp;//将解码出的字节值储存在a[i]
}
if(a[2]==~a[3])return1;//验证键数据码和其反码是否相等,一般情况下不必验证用户码
}
voidmain()
{
EA=1;//开启总中断
EX0=1;//开外中断0
ET0=1;//定时器T0中断允许
TMOD=0x01;//使用定时器T0的模式1
TR0=0;//定时器T0关闭
while
(1);//等待用户的按键操作
}
voidInt0(void)interrupt0
{
EX0=0;//关闭外中断0,不再接收二次红外信号的中断,只解码当前红外信号
TH0=0;//定时器T0的高8位清0
TL0=0;//定时器T0的低8位清0
TR0=1;//开启定时器T0
while(IR==0);//如果是低电平就等待,给引导码低电平计时
TR0=0;//关闭定时器T0
LowTime=TH0*256+TL0;//保存低电平时间
TH0=0;//定时器T0的高8位清0
TL0=0;//定时器T0的低8位清0
TR0=1;//开启定时器T0
while(IR==1);//如果是高电平就等待,给引导码高电平计时
TR0=0;//关闭定时器T0
HighTime=TH0*256+TL0;//保存引导码的高电平长度
if((LowTime>8500)&&(LowTime<9500)&&(HighTime>4200)&&(HighTime<4800))//如果是引导码,就开始解码,否则放弃。
{
if(DeCode()==1)//执行遥控解码功能
{
if(a[2]==0x09){P0=0x05;}//如果按下后退键,则电机反转
if(a[2]==0x15){P0=0x0a;}//如果按下前进键,则电机正转
if(a[2]==0x47){dianji1=dianji2=0;}//如果按下停止键,则电机不使能
}
zhishi();//指示灯闪烁,表示正确解析红外信号并进行相关操作,且有适当延时效用
}
EX0=1;//开启外中断EX0
}
2、在实现了前进、后退和停止的功能后,我们可对程序进行适当扩充,使小车具有除前进、后退和停止功能外,还具有开启、加速、减速、左转、右转和原地打转的功能。
代码如下:
#include
sbitjieshou=P3^2;
sbitzhishideng=P1^0;
unsignedcharflag1=0,flag2=0;
unsignedchara[4];
unsignedinthightime,lowtime;
voiddelay(unsignedchari)//延时函数
{
unsignedcharj=124;
while(i--)
while(j--);
}
voidzhishi()//指示灯函数
{
zhishideng=0;
delay(200);
zhishideng=1;
}
intshibie()//单片机解码红外函数
{
unsignedchari,x,temp;
for(i=0;i<4;i++)
{
for(x=0;x<8;x++)
{
temp=temp>>1;
TH0=0;
TL0=0;
TR0=1;
while(jieshou==0);
TR0=0;
lowtime=TH0*256+TL0;
TH0=0;
TL0=0;
TR0=1;
while(jieshou==1);
TR0=0;
hightime=TH0*256+TL0;
if(lowtime<400||lowtime>700)return0;
if(hightime>450&&hightime<675)temp=temp&0x7f;
if(hightime>1410&&hightime<1955)temp=temp|0x80;
}
a[i]=temp;
}
if(a[2]==~a[3])return1;
}
voidmain()
{
EA=1;//开启总中断
EX0=1;//开外中断0
ET0=1;//定时器T0中断允许
TMOD=0x01;//使用定时器T0的模式1
TR0=0;//定时器T0关闭
while
(1)
{
if(flag1=='+')//前进时,把速度分为5挡,通过占空比来控制
{
if(flag2==1){P0=0xf5;delay
(2);P0=0xf0;delay(8);}
if(flag2==2){P0=0xf5;delay(4);P0=0xf0;delay(6);}
if(flag2==3){P0=0xf5;delay(6);P0=0xf0;delay(4);}
if(flag2==4){P0=0xf5;delay(8);P0=0xf0;delay
(2);}
if(flag2==5){P0=0xf5;}
}
if(flag1=='-')//后退时,分6挡,其中0挡是停止挡,是避免小车停止这一功能另写一个子函数而进行的结合;另外5个挡才是真正通过占空比来进行后退调速的
{
if(flag2==0){P0=0xf0;}
if(flag2==1){P0=0xfa;delay
(2);P0=0xf0;delay(8);}
if(flag2==2){P0=0xfa;delay(4);P0=0xf0;delay(6);}
if(flag2==3){P0=0xfa;delay(6);P0=0xf0;delay(4);}
if(flag2==4){P0=0xfa;delay(8);P0=0xf0;delay
(2);}
if(flag2==5){P0=0xfa;}
}
if(flag1=='<')//左转功能,通过控制左右轮速度的不同而进行左转
{
P0=0xf4;delay(3);P0=0xf1;delay(7);
}
if(flag1=='>')//右转功能,通过控制左右轮速度的不同而进行右转
{
P0=0xf4;delay(7);P0=0xf1;delay(3);
}
if(flag1=='o'){P0=0xf4;}//原地打转功能,通过一边轮转,一边轮不转来实现
}
}
voidint0()interrupt0//中断函数
{
EX0=0;
TH0=0;
TL0=0;
TR0=1;
while(jieshou==0);
TR0=0;
lowtime=TH0*256+TL0;
TH0=0;
TL0=0;
TR0=1;
while(jieshou==1);
TR0=0;
hightime=TH0*256+TL0;
if(lowtime>8460&&lowtime<9550&&hightime>3910&&hightime<5100)
{
if(shibie()==1)
{
if(a[2]==0x45){flag1='+';flag2=3;}//开启直流电机
if(a[2]==0x47){flag1='-';flag2=0;}//关闭直流电机
if(a[2]==0x09){flag1='+';flag2=flag2+1;if(flag2==6)flag2=1;}//前进
if(a[2]==0x15){flag1='-';flag2=flag2+1;if(flag2==6)flag2=1;}//后退
if(a[2]==0x40){flag1='<';}//左转
if(a[2]==0x43){flag1='>';}//右转
if(a[2]==0x16){flag1='o';}//原地打转
}
zhishi();//指示灯闪烁
}
EX0=1;
}
第四部分:
说明和注意事项
1、本制作中,单片机的晶振为12Mhz的,若使用其他频率的晶振,要对程序里红外解码的有关部分进行数值上的修改,否则单片机不能正确识别和解码。
2、一体化红外接收头容易受到干扰,故应该让接收头远离电机(电机需要大电流,在加减速时会造成很大的干扰)和单片机。
实际制作中,可以用硬棒把接收头架起来。
3、红外遥控的距离有限,大概为7-8米左右,故应当注意遥控的距离。
4、本制作中使用的车体是四驱的,即含有四个独立电机。
其中车体一边的两电机并联接入驱动模块中使用,其效果如同两驱的一样。
所以也适用于只有两个独立电机的车体。
5、本制作中,电机驱动使用的是双H桥直流电机驱动成品模块,其说明图如下(读者可以自己使用L298来制作):
电机A、B的正反转控制
电机A、B的使能控制