从零开始制作 51 单片机控制的智能小车,让小车动起来

从本文开始,在之后的一段时间里,我会通过本系列文章,详细介绍如何从零开始用51单片机去实现智能小车的控制,本文作为本系列的第一篇文章,主要介绍如何让小车动起来。左右电机的状态控制函数如下:每个函数的前两行是左右电机转速的设置。

2. 电机驱动模块 L298N 电机驱动模块绝对是初学者的首选,但这个系列也包括很多类型。本文使用 L298N 双 H 桥驱动器红色版本。除了性能之外,我选择它是因为它有一个 5v 输出接口,可以用来给单片机供电。你可以使用两个这样的驱动器,也可以使用一个,另一个使用更便宜的。

由于我家里有以下型号的L298N驱动程序,为了不浪费资源,另一个我将使用以下型号的L298N驱动程序。

3. MCU最小系统关于最小系统,选择自己熟悉的就行,没有特殊要求,我用的样式如下(使用的芯片是STC89C52)

4.电源这部分大家可以根据需求选择,选择的时候注意电压和容量就行,我选的是常见的9v 650mAh(容量不是很大,但是这种电池比较常见,充一次电跑2~3个小时应该没问题)。我选的是USB充电的,充电很方便。电池盒我选的是下图这种,拆卸方便,缺点就是没有自带电源开关。

5.杜邦线。这个是必备配件,就不多说了。需要买一些公对公,母对母,公对母的(这种一般用得比较多),家里常用的物品。

2.硬件连接本文涉及的硬件连接是单片机、电源、电机、两个电机驱动器L298N的连接,这里我介绍一种参考连接方法,大家可以自行设计连接方案

如上图,四个电机的正负极分别接在L298N的两个绿色电机接口上,至于哪个接正极,哪个接负极,就看你电机的安装方式了,建议先把电机的两根线焊接好,再装底盘,这样电机的安装方式就确定了,先把两个L298N的四个绿色电机接口随意接在电机上,其他信号线接好后判断对错再进行调整,调整方法如下:在程序中让小车向前跑,观察车轮转动的方向,车轮向前转的线不用改,只要把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 
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 
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 
#include 
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.本文示例的视频演示 视频演示链接 点击上方链接,可以查看本文介绍内容的视频演示,内容如下(即main函数中程序内容):前进5秒,后退5秒,左转5秒,右转5秒,停止5秒,绕圈5秒。 附视频网址:

本文就到此结束,我会把本文的完整项目文件放在附件里,需要的可以拿去,我放上去的时候是免费的,过一段时间就会涨价了……欢迎继续阅读本系列的后续文章《从零开始制作51单片机控制的智能小车详解(下)——超声波模块、漫反射光电管、4路红外传感器的介绍及使用》

本系列文章的附件均可单独下载,附件获取方式如下(建议通过Gitee免费下载):

欢迎大家积极交流。本文未经允许,禁止转载。

单片机

自学转行单片机行业,脱离蓝领阶层,追求高薪白领生活

2024-5-29 16:01:58

单片机

大学毕业生找人代写毕业论文,引发的思考与警示

2024-5-29 17:02:28

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索