基于STC89C52RC模块的巡线小车
在STC89C52RC的基础上使用电机驱动使小车完成巡线,停站,避障,掉头等多功能智能小车
所需头文件:
#include
#include
定义无字符型变量:
#define uint unsigned int //宏定义无符号整型变量
#define uchar unsigned char //宏定义无符号字符型变量
所需的电机驱动模块:L298N
主要功能特点是:
要害芯片:L298N 双H 桥直流/步进电机驱动芯片
L298N 芯片作业电压:DC 4.5~5.5V。
电机驱动电源电压DC 5--35V。
电源输入正常时有LED 灯指示。
PCB尺度:4.4*5.0cm
最大输出电流2A(瞬间峰值电流3A),最大输出功率25W。
输出正常时电机工作有LED 灯指示。
具有二极管续流维护。
可单独操控2台直流电机或1台两相4 线(或6 线)步进电机。
能够选用并联接法操控一台高达3A 的直流电机。
可完成电机正回转。
定义电机驱动引脚:
/*电机驱动引脚定义*/
sbit qlIN1=P3^0; //左前电机IN1引脚定义
sbit qlIN1=P3^1; //左前电机IN2引脚定义
sbit qlIN1=P3^2; //右前电机IN1引脚定义
sbit qlIN1=P3^3; //右前电机IN2引脚定义
sbit qlIN1=P3^4; //左后电机IN1引脚定义
sbit qlIN1=P3^5; //左后电机IN2引脚定义
sbit qlIN1=P3^6; //右后电机IN1引脚定义
sbit qlIN1=P3^7; //右后电机IN2引脚定义
uint cut=0; //定义整型变量cut 在定位器中装数
定义pwm调速:
脉宽调制(PWM)基本原理:控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期中产生多个脉冲,使各脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。
按一定的规则对各脉冲的宽度进行调制,即可改变逆变电路输出电压的大小,也可改变输出频率。
例如,把正弦半波波形分成N等份,就可把正弦半波看成由N个彼此相连的脉冲所组成的波形。这些脉冲宽度相等,都等于 π/n ,但幅值不等,且脉冲顶部不是水平直线,而是曲线,各脉冲的幅值按正弦规律变化。
如果把上述脉冲序列用同样数量的等幅而不等宽的矩形脉冲序列代替,使矩形脉冲的中点和相应正弦等分的中点重合,且使矩形脉冲和相应正弦部分面积(即冲量)相等,就得到一组脉冲序列,这就是PWM波形。可以看出,各脉冲宽度是按正弦规律变化的。
根据冲量相等效果相同的原理,PWM波形和正弦半波是等效的。对于正弦的负半周,也可以用同样的方法得到PWM波形。在PWM波形中,各脉冲的幅值是相等的,要改变等效输出正弦波的幅值时,只要按同一比例系数改变各脉冲的宽度即可,因此在交-直-交变频器中,PWM逆变电路输出的脉冲电压就是直流侧电压的幅值。
根据上述原理,在给出了正弦波频率,幅值和半个周期内的脉冲数后,PWM波形各脉冲的宽度和间隔就可以准确计算出来。按照计算结果控制电路中各开关器件的通断,就可以得到所需要的PWM波形。
/*pwm控制引脚定义*/
sbit qlENA=P2^0; //左前电机使能
sbit qrENB=P2^1; //右前电机使能
sbit hlENA=P2^2; //左后电机使能
sbit hrENB=P2^3; //右后电机使能
/*pwm周期初始赋值*/
unsigned char qlpwm=0;
unsigned char nqlpwm=0; //左前电机占空比
unsigned char qrpwm=0;
unsigned char nqrpwm=0; //右前电机占空比
unsigned char hlpwm=0;
unsigned char nhlpwm=0; //左后电机占空比
unsigned char hrpwm=0;
unsigned char nhrpwm=0; //右后电机占空比
定义电机功能:
/*电机功能定义*/
#define qlgo {qlIN1=0,qlIN2=1;} //左前电机前进
#define qrgo {qrIN3=0,qrIN4=1;} //右前电机前进
#define hlgo {hlIN1=0,hlIN2=1;} //左后电机前进
#define hrgo {hrIN3=0,hrIN4=1;} //右后电机前进
#define qlback {qlIN1=1,qlIN2=0;} //左前电机后退
#define qrback {qrIN3=1,qrIN4=0;} //右前电机后退
#define hlback {hlIN1=1,hlIN2=0;} //左后电机后退
#define hrback {hrIN3=1,hrIN4=0;} //右后电机后退
#define qlstop {qlIN1=0,qlIN2=0;} //左前电机停止
#define qrstop {qrIN3=0,qrIN4=0;} //右前电机停止
#define hlstop {hlIN1=0,hlIN2=0;} //左后电机停止
#define hrstop {hrIN3=0,hrIN4=0;} //右后电机停止
定义红外传感器引脚:(0为白线<亮>;1为黑线<灭>)
/*红外传感器引脚定义*/
sbit Lred=P0^0; //左边红外传感器引脚定义
sbit redA=P0^1; //左中红外传感器引脚定义
sbit redB=P0^2; //中右红外传感器引脚定义
sbit Rred=P0^3; //右边红外传感器引脚定义
sbit Tred=P1^7; //停障红外传感器引脚定义
可以通过调节滑动变阻器改善亮灭情况;
红外模块:TCRT500 运算放大器:M324N.
两种tcrt5000红外传感模块制作方法都可,个人推荐第一种,简单易懂。
电机调速:
void vqlpwm() /*左前电机调速 nqlpwm改变电机转速与占空比*/
{
if(qlpwm<=nqlpwm)
{
qlENA=1;
}
else
{
qlENA=0;
}
if(qlpwm>=20)
qlpwm=0;
}
void vqrpwm() /*右前电机调速 nqrpwm改变电机转速与占空比*/
{
if(qrpwm<=nqrpwm)
{
qrENB=1;
}
else
{
qrENB=0;
}
if(qrpwm>=20)
qrpwm=0;
}
void vhlpwm() /*左后电机调速 nhlpwm改变电机转速与占空比*/
{
if(hlpwm<=nhlpwm)
{
hlENA=1;
}
else
{
hlENA=0;
}
if(hlpwm>=20)
hlpwm=0;
}
void vhrpwm() /*右后电机调速 nhrpwm改变电机转速与占空比*/
{
if(hrpwm<=nhrpwm)
{
hrENB=1;
}
else
{
hrENB=0;
}
if(hrpwm>=20)
hrpwm=0;
}
定义运动函数:
void zhizou() //直行函数定义
{
nhlpwm=20;
nhrpwm=20;
nqlpwm=20;
nqrpwm=20;
qlgo;
qrgo;
hlgo;
hrgo;
}
void houtui() //后退函数定义
{
nhlpwm=20;
nhrpwm=20;
nqlpwm=20;
nqrpwm=20;
qlback;
qrback;
hlback;
hrback;
}
void zuozhuan() //左转函数定义
{
nhlpwm=20;
nhrpwm=20;
nqlpwm=20;
nqrpwm=20;
qlback;
hlback;
qrgo;
hrgo;
}
void weizuozhuan() //向左微调函数定义
{
qlback;
hlback;
qrgo;
hrgo;
nhlpwm=20;
nhrpwm=20;
nqlpwm=20;
nqrpwm=20;
}
void youzhuan() //右转定义函数
{
nhlpwm=20;
nhrpwm=20;
nqlpwm=20;
nqrpwm=20;
qlgo;
hlgo;
qrback;
hrback;
}
void weiyouzhuan() //向右微调函数定义
{
qlgo;
hlgo;
qrback;
hrback;
nhlpwm=20;
nhrpwm=20;
nqlpwm=20;
nqrpwm=20;
}
void tingzhi() //停止函数定义
{
qlstop;
qrstop;
hlstop;
hrstop;
}
定时器相关设定:
定时器需要配置:TMOD =0x01;配置成使用定时器0,工作方式为1;同理使用定时器1工作方式1:TMOD =0x10;则同时使用两个定时器且工作方式为1,那么可以:TMOD =0x11;
定时器1配置成工作方式2:TMOD =0x20;
EA:中断总控制位。EA=1,CPU开放所有中断;EA=0,CPU禁止所有中断。
ES:串行口中断控制位。ES=1,允许串行口中断;ES=0,屏蔽串行口中断。
ET1:定时/计数器TI中断控制位。ET1=1,允许T1中断;ET1=0,禁止T1中断。
EX1:外部中断1中断控制位。EX1=1,允许外部中断1中断;EX1=0,禁止外部中断1中断。
ET0:定时/计数器T0中断控制位。ET0=1,允许T0中断;ET0=0,禁止T0中断。
EX0:外部中断0中断控制位。EX0=1,允许外部中断0中断;EX0=0,禁止外部中断0断。
总之,对于定时器部分只要记着EA=1:开启总中断;ET0=1:开启定时器T0中断,TR0=1为启动定时器;
例如:
定时器0:
void isr_timer_0(void) interrupt 1 //默认中断优先级 1
{
TH0 = (65536-2000)/256;
TL0 = (65536-2000)%256; //定时器重载
display();
}
定时器1:
void isr_timer_1(void) interrupt 3 //默认中断优先级 3
{
TH0 = (65536-2000)/256;
TL0 = (65536-2000)%256; //定时器重载
display();
}
void Timer0Init() //1毫秒@11.0592MHz
{
AUXR |=0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初始值
TH0 = 0xD4; //设置定时初始值
TF0 = 0; //清除FFO标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1; //开总中断
}
void timer0()interrupt 1 /*TIMER0中断服务子函数产生PWM信号*/
{
TH0=0XFc; //1MS定时
TL0=0X18;
qlpwm++;
qrpwm++;
hlpwm++;
hrpwm++;
vqrpwm();
vqlpwm();
vhrpwm();
vhlpwm();
}
主函数:
void main(void) //主函数
{
Timer0Init();
while(1) //函数循环
{
if(Lred==0&&redA==1&&redB==1&&Rred==0&&Tred==1) //直行条件
{
zhizou(); //直行函数
}
if(Lred==0&&redA==1&&redB==0&&Rred==0&&Tred==1) //向左微调条件
{
weizuozhuan(); //向左微调函数
}
if(Lred==0&&redA==0&&redB==1&&Rred==0&&Tred==1) //向右微调条件
{
weiyouzhuan(); //向右微调函数
}
if(Lred==1/*&&redA==1&&redB==1*/&&Rred==0&&Tred==1) //左转条件
{
zuozhuan(); //左转函数
}
if(Lred==0/*&&redA==1&&redB==1*/&&Rred==1&&Tred==1) //右转条件
{
youzhuan(); //右转函数
}
if(Lred==1&&redA==1&&redB==1&&Rred==1&&Tred==1) //黑线停站
{
tingzhi();
Delay50ms();
zhizou();
Delay10ms();
}
if(Tred==0) //停障
{
tingzhi();
//Delay10ms(); //可作为掉头命令
//youzhuan();
//Delay50ms();
}
}
}