从本文开始,在之后的一段时间里,我会通过本系列文章,详细介绍如何从零开始用51单片机去实现智能小车的控制,本文作为本系列的第一篇文章,主要介绍如何让小车动起来。
本系列文章链接:
详细介绍如何从零开始制作51单片机控制的智能小车(一)———让小车动起来
详细介绍如何从零开始制作51单片机控制的智能小车(二)———超声波模块、漫反射光电管、4路红外传感器的介绍和使用
详细介绍如何从零开始制作51单片机控制的智能小车(三)———用超声波模块和漫反射光电传感器实现小车的自动避障
详细介绍如何从零开始制作51单片机控制的智能小车(四)———通过蓝牙模块实现数据传输以及通过手机蓝牙实现对小车运动状态的控制
详细介绍如何从零开始制作51单片机控制的智能小车(五)———对本系列第四篇文章介绍的手机蓝牙遥控加减速异常的错误的介绍及纠正
一、硬件的选择
1、底盘和电机
底盘的形状呢,大家可以按照自己的需要自主选取,至于电机关注一下工作电压,转速,电机类型就差不多,对于新手,可以尝试以下样式(4WD智能小车底盘,附带4个直流减速电机,电机接线需要自己焊接),也就是本文例子采用的底盘和电机,组装简单,使用方便,特别适合新手。
2、电机驱动模块
L298N电机驱动模块,绝对是新手的首选,但是此系列也包含了很多类型,本文采用的是L298N双H桥驱动 红色版 ,除了性能外,我选择它是因为它具备了5v的输出接口,可以用来给单片机供电。大家可以用两个这种驱动,也可以用一个,另一个用个便宜点的。
由于家中有一个如下式样的L298N驱动,所以为了不让资源浪费,另一个,我就用如下型号的L298N驱动。
3、单片机最小系统
关于最小系统,大家只要选用自己熟悉的就行了,没什么特别的讲究,我采用的样式如下(芯片采用的是STC89C52)
4、电源
这一部分大家根据需要自己选择即可,选的时候注意一下电压和容量就行,我选用的是常见的 9v 650mAh(容量不是很大,但是这种电池比较常见,充一次电跑2~3小时应该不是问题),我选的是USB充电款,充电很方便,电池盒我选用的是如下的这种拆卸方便的款式,缺点就是不附带电源开关。
5、杜邦线
这是必备的辅件,就不多说了,公对公,母对母,公对母(这一种一般用的多一些)都要买一些,家里常备物品。
二、硬件的连接
本文涉及到的硬件连接为单片机、电源、 电机、 两个电机驱动L298N之间的连接,在这里我介绍一种参考的连接方式,大家可以自己设计连接方案
如上图所示,四个电机的正负极分别接两个L298N的绿色电机接口,至于到底哪个接正极,哪个接负极,根据你电机安装的方式而定,建议先把电机的两根线焊上,然后把底盘安装起来,这样电机的安装方式就确定了,先随便把两个L298N的4个绿色电机接口跟电机相接,等到把其他信号线接好后,再判断对错并调节,调节方法如下:在程序中让小车往前跑,观察车轮的转向,往前转的车轮的线不用变,把往后转的电机对应的L298N绿色接口的两根线换一下就行了。
如上图所示,L298N的左边数第一个蓝色端口 是5V输出,把其中一个L298N的该接口接到单片机的5v接口上,另一L298N该接口可以空着,左边数第二个蓝色端口是GND需要同时与单片机的GND与电源的负极相接,左边数第三个蓝色端口是L298N的电源输入端口,与电源的正极相接,我采用的是9v的电源。
剩下的就是L298N的信号线与单片机的连接了,介绍如上图所示,在这里我采用的是双驱的接法,也就是左边两个点击用同一个信号控制,右边两个电机用同一个信号控制,单片机的I/O口自行选择,与程序配合起来就行,我选用的是 ENA接P16 ENB 接P17 IN1接P34 IN2接P35 IN3接P36 IN4接P37 若改为4驱所需的I/O将扩大一倍。
实物图片如下:
三、程序的编写
1、工程的建立
编译环境根据自己习惯和需要选择,本文以KEIL C51为例,由于本次设计的小车控制并不复杂,所以我把工程中用到的所有头文件、函数的定义、sbit定义的位变量都放到了一个头文件中,取名为car.h(名字大家随意取即可),C文件呢我建议大家把各个部分别写在不同的文件中,比如我把与电机驱动有关的函数放到了motor_control.c(名字任意取)文件中,控制方案和延时函数,中断函数放到了主函数main.c(名字任意取)文件中,后续随着功能增加还会增设其他的C文件,只要所有的C文件均包含以上共同的头文件car.h,也就互相建立了联系。
2、根据L298N与单片机的接线,编写电机控制函数
虽然说本文选用的车型四个电机可以独立控制,但是为了简单化,方便化,我们让左边的两个电机采用共同的信号控制,右边的两个电机采用共同的信号控制,大家若需要可以自主改为4路独立的信号控制,根据本文第二部分——硬件的连接部分的介绍,我们选用了单片机的P34 P35 I/O口作为左电机的方向控制信号,单片机的P36 P37 I/O口作为右电机的方向控制信号,单片机的P16 I/O口作为左电机的PWM输出控制信号,单片机的P17 I/O口作为右电机的PWM输出控制信号。
以上6个I/O口的位定义如下(为方便各文件调用,我们把它放到统一的h文件car.h中)
sbit Left_moto_pwm=P1^6 ;
sbit Right_moto_pwm=P1^7;
sbit p34=P3^4;
sbit p35=P3^5;
sbit p36=P3^6;
sbit p37=P3^7;
左右电机的状态控制函数如下:
void Left_moto_go() //左电机正转
{p34=0;p35=1;}
void Left_moto_back() //左电机反转
{p34=1;p35=0;}
void Left_moto_stp() //左电机停转
{p34=1;p35=1;}
void Right_moto_go() //右电机正转
{p36=0;p37=1;}
void Right_moto_back() //右电机反转
{p36=1;p37=0;}
void Right_moto_stp() //右电机停转
{p36=1;p37=1;}
4、PWM调速输出函数的编写:
对于新手来说,如果理解不了以下两个函数,那只需要知道如何使用就行了,即通过修改push_val_left的值就可以调节左电机的转速,通过修改push_val _right的值就可以调节右电机的转速,push_val_left和push_val_right的值均位于1到10之间,值越大电机转速越快
bit Left_moto_stop =1;
bit Right_moto_stop =1;
unsigned char pwm_val_left =0;
unsigned char push_val_left =0;
unsigned char pwm_val_right =0;
unsigned char push_val_right=0;
void pwm_out_left_moto(void) //左电机调速
{
if(Left_moto_stop)
{
if(pwm_val_left<=push_val_left)
Left_moto_pwm=1;
else
Left_moto_pwm=0;
if(pwm_val_left>=10)
pwm_val_left=0;
}
else
Left_moto_pwm=0;
}
void pwm_out_right_moto(void) //右电机调速
{
if(Right_moto_stop)
{
if(pwm_val_right<=push_val_right)
Right_moto_pwm=1;
else
Right_moto_pwm=0;
if(pwm_val_right>=10)
pwm_val_right=0;
}
else
Right_moto_pwm=0;
}
5、小车姿态控制函数的编写:
理解了 左右电机的状态控制函数,编写小车姿态控制函数就很简单了,大家稍微想一下小车左右轮的状态,小车会怎么运行,就理解了,比如 左右电机都正转,那小车运行状态肯定是前行。每个函数的前两行是左右电机转速的设置。
unsigned char Left_Speed_Ratio; //左电机转速的设定值
unsigned char Right_Speed_Ratio; //右电机转速的设定值
void run(void) //小车前行
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_go();
Right_moto_go();
}
void back(void) //小车后退
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_back();
Right_moto_back();
}
void left(void) //小车左转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Right_moto_go();
Left_moto_stp();
}
void right(void) //小车右转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Right_moto_stp();
Left_moto_go();
}
void stop(void) //小车停止
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_stp();
Right_moto_stp();
}
void rotate(void) //小车原地转圈
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_back();
Right_moto_go();
}
6、与定时器中断有关函数的编写
void Timer0Init() //定时器初始化函数
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void timer0()interrupt 1 using 2 //定时器中断函数,此处配置为1ms产生一次中断,对PWM的输出进行控制
{
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
time++;
pwm_val_left++;
pwm_val_right++;
pwm_out_left_moto();
pwm_out_right_moto();
}
7、延时函数的编写
关于延时函数,大家只要会用就行,可以用单片机小精灵等辅助软件生成,以下为延时1秒的函数
void delay1s(void)
{
unsigned char a,b,c;
for(c=167;c>0;c--)
for(b=171;b>0;b--)
for(a=16;a>0;a--);
_nop_();
}
8、主函数内容的编写
关于主函数的内容,首先要调用定时器中断初始化函数,其次要设置左右电机的速度参数,本文的主要内容是让车动起来,所以主函数内要调用本部分第5步中编写的小车姿态控制函数,对其进行检验,为了便于观察两个状态之间加了5秒的延时,代码如下:
void main()
{
Timer0Init();
Left_Speed_Ratio=5; //设置左电机车速为最大车速的50%
Right_Speed_Ratio=5; 设置右电机车速为最大车速的50%
while(1)
{
run();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
back();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
left();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
right();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
stop();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
rotate();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
}
}
四、本文例子完整的C文件和H文件代码
1、motor_control.c文件完整代码如下:
#include <car.h>
unsigned char pwm_val_left =0;
unsigned char push_val_left =0;
unsigned char pwm_val_right =0;
unsigned char push_val_right=0;
unsigned char Left_Speed_Ratio;
unsigned char Right_Speed_Ratio;
bit Left_moto_stop =1;
bit Right_moto_stop =1;
void Left_moto_go() //左电机正转
{p34=0;p35=1;}
void Left_moto_back() //左电机反转
{p34=1;p35=0;}
void Left_moto_stp() //左电机停转
{p34=1;p35=1;}
void Right_moto_go() //右电机正转
{p36=0;p37=1;}
void Right_moto_back() //右电机反转
{p36=1;p37=0;}
void Right_moto_stp() //右电机停转
{p36=1;p37=1;}
void pwm_out_left_moto(void) //左电机PWM
{
if(Left_moto_stop)
{
if(pwm_val_left<=push_val_left)
Left_moto_pwm=1;
else
Left_moto_pwm=0;
if(pwm_val_left>=10)
pwm_val_left=0;
}
else
Left_moto_pwm=0;
}
void pwm_out_right_moto(void) //右电机PWM
{
if(Right_moto_stop)
{
if(pwm_val_right<=push_val_right)
Right_moto_pwm=1;
else
Right_moto_pwm=0;
if(pwm_val_right>=10)
pwm_val_right=0;
}
else
Right_moto_pwm=0;
}
void run(void) //小车前行
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_go();
Right_moto_go();
}
void back(void) //小车后退
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_back();
Right_moto_back();
}
void left(void) //小车左转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Right_moto_go();
Left_moto_stp();
}
void right(void) //小车右转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Right_moto_stp();
Left_moto_go();
}
void stop(void) //小车停止
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_stp();
Right_moto_stp();
}
void rotate(void) //小车原地转圈
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
Left_moto_back();
Right_moto_go();
}
2、main.c文件完整代码如下:
#include <car.h>
extern unsigned char Left_Speed_Ratio;
extern unsigned char Right_Speed_Ratio;
unsigned int time=0;
extern unsigned char pwm_val_left;
extern unsigned char pwm_val_right;
void delay1s(void)
{
unsigned char a,b,c;
for(c=167;c>0;c--)
for(b=171;b>0;b--)
for(a=16;a>0;a--);
_nop_();
}
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void timer0()interrupt 1 using 2
{
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
time++;
pwm_val_left++;
pwm_val_right++;
pwm_out_left_moto();
pwm_out_right_moto();
}
void main()
{
Timer0Init();
Left_Speed_Ratio=5; //设置左电机车速为最大车速的50%
Right_Speed_Ratio=5; 设置右电机车速为最大车速的50%
while(1)
{
run();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
back();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
left();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
right();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
stop();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
rotate();
delay1s(); delay1s(); delay1s(); delay1s(); delay1s();
}
}
3、car.h文件完整代码如下:
#ifndef __car_H
#define __car_H
#include <reg52.h>
#include <intrins.h>
sbit Left_moto_pwm=P1^6 ;
sbit Right_moto_pwm=P1^7;
sbit p34=P3^4;
sbit p35=P3^5;
sbit p36=P3^6;
sbit p37=P3^7;
void Left_moto_go() ;
void Left_moto_back() ;
void Left_moto_stp() ;
void Right_moto_go();
void Right_moto_back();
void Right_moto_stp();
void delay(unsigned int k) ;
void delay1s(void) ;
void pwm_out_left_moto(void) ;
void pwm_out_right_moto(void);
void run(void);
void back(void);
void left(void);
void right(void);
void stop(void);
void rotate(void);
五、本文例子实物视频演示
实物视频演示视频链接
点击上面的链接即可查看本文介绍内容的视频演示,内容依次为(即主函数中程序的内容):前进5秒 、后退5秒、左转5秒、右转5秒、停转5秒、转圈5秒。附视频网址:
https://www.bilibili.com/video/bv1N5411x7zL
本文到这里就结束了,本文完整的工程文件我会放在附件里,需要者自取,我放的时候都是免费的,但是过段时间它会自己涨…需要的在评论区留言我可以直接发给你,欢迎大家继续阅读本系列的后续文章“详细介绍如何从零开始制作51单片机控制的智能小车(二)———超声波模块、漫反射光电管、4路红外传感器的介绍和使用” 欢迎大家积极交流,本文未经允许谢绝转载