51单片机控制的高效循迹小车设计

在大学期间,单片机是部分学生的专业课之一,在学完课本上枯燥的知识后,需要实践加强巩固。简单且综合性尚可的循迹小车必然是实践的首要选择。下面将主要从机械、电控、代码三个部分对以51单片机为主控芯片的循迹小车进行简述

目录

1.总体介绍

2.机械部分

2.1底盘制作

 2.2底盘布局

 3.电控部分

3.1主控芯片

 3.2电路驱动

4.代码部分


1.总体介绍

博主制作的小车主控芯片选择了STC89C52RC,这是宏晶科技的新版51单片机芯片,老版型号为STC89C52。该芯片在淘宝上有售,大概4-6元一片。对于初学者而言,最好加购最小系统板,这样可以避免焊接的问题。底盘同样也采用了市面上常见的小车底盘。同时加购了L298N电路驱动模块控制电机的转速等。为实现循迹功能,我们也要购买红外传感器模块。(博主的车是为一次比赛而制作的,较一般的小车有多出的模块,如前端的机械爪。)

                    

图1-1小车

2.机械部分

对于基本的51循迹小车,机械部分主要是底盘部分,可以简单分为两部分:底盘制作、底盘布局。

2.1底盘制作

因为底盘并不是制作51循迹小车的关键,且新手缺乏相关知识与工具,所以博主不推荐自己制作底盘,建议在淘宝上选购,省时省力。

图2.1-1小车底盘​​

 2.2底盘布局

底盘布局简而言之就是各种器件的分布情况。对于循迹小车而言主要就是红外探头的分布情况,其余零器件只要保证接线时自己不混乱就行。

红外探头的放置需要靠前。如果红外探头放在小车车身中间,遇到拐角时,小车转弯便会不灵活,半个车身越过弯道才会开始执行转弯命令,放在后面更不用说。同时根据其工作原理,红外探头的位置需要接近地面,避免阳光直射,便于其更加稳定、灵敏的接受信号、判断黑线。

位置分布需要看情况。对于多个红外探头,如图2.2-1,如果仅需实现循迹功能,可以直接并排间隔或不间隔放置。探头的数量多可以保证循迹的精确度。(不是越多越好)如果要制作的小车有其他任务与地图限制,则需要调整部分红外探头位置

红外探头工作原理:红外探测器由两部分——发射器(红外发光二级管)与接收器(红外光电二极管)组成。在其工作时,由发射器向外发出一定波长的红外光,经物体表面反射后由接收器接收。当物体表面类似黑体时,便会使发射器发出的红外光无法发射会接收器,接收器本质是红外光电二极管,失去了光,电流会发生变化,由此产生信号。

图2.2-1四路红外传感器​​​

 黑色外表的是接受端,透明的是发射端。接收端需要抵抗外界光线的干扰,所以采用黑色外表。而发射端采用透明外表是为了更好的向各个方向发射红外光。

 3.电控部分

51循迹小车的电控部分主要可以分为两部分——主控芯片、电路驱动。

3.1主控芯片

前文已经提及过了,本小车采取STC89C52RC作为主控芯片,缺乏电路知识的的读者可以购买最小系统板,不缺乏的可以自己购买相关物品焊板子。

图3.1-1主控芯片

 如果你手中的是购买的系统板,需要观察一下板子上的晶振是否稳定,这对后续的程序有影响

图3.1-2晶振

 3.2电路驱动

单片机信号是无法直接驱动电机的,因此我们需要一块电路驱动模块来作为中转站,将单片机发出的信号转换为电机可以接受的信号。在此,我们选用L298N电路驱动。

图3.2-1L298N电路驱动

图·3.2-1中是一块四路L298N电路驱动模块,它集成了两块L298N芯片,可以同时控制四个电机,做到更加多样的行动。对于新手而言,建议选用如图3.2-2所示的正常的二路L298N电路驱动模块就可以了。 

图3.2-2二路L298N

 关于如何使用L298N控制电机转速等问题,留在代码部分进行讲解。

4.代码部分

在代码部分,首先我们要定义好接线口。如果没有定义的话,也可以使用,但在后续编写程序环节会给自己带来很大的麻烦。如下代码展示的是四路L298n的接口定义,第一行表示含义为L298N的In1口与单片机最小系统板的P10口接到一起。

sbit后方变量名可以随便取,只要不违反C语言中变量命名规则即可,同时要让自己知道是哪个口。

sbit L_In1 = P1^0;
sbit L_In2 = P1^1;
sbit L_In3 = P1^2;
sbit L_In4 = P1^3;
sbit L_In5 = P1^4;
sbit L_In6 = P1^5;
sbit L_In7 = P1^6;
sbit L_In8 = P1^7;

sbit L_EnL1 = P3^0;
sbit L_EnR1 = P3^1;
sbit L_EnL2 = P3^2;
sbit L_EnR2 = P3^3;

sbit CgqL1 = P2^1;
sbit CgqL2 = P2^2;
sbit CgqR1 = P2^3;
sbit CgqR2 = P2^4;

定义好接线口后,就要开始定义运动函数。这里同样是以四路为例,因为二路只需要在此基础上删减多余电机的运动与定义就可以了

#define Moto_Go_L1	{L_In1 = 0,L_In2 = 1;}
#define Moto_Go_R1	{L_In3 = 0,L_In4 = 1;}
#define Moto_Go_L2	{L_In5 = 0,L_In6 = 0;}
#define Moto_Go_R2	{L_In7 = 0,L_In8 = 1;}

#define Moto_Back_L1	{L_In1 = 1,L_In2 = 0;}
#define Moto_Back_R1	{L_In3 = 1,L_In4 = 0;}
#define Moto_Back_L2	{L_In5 = 1,L_In6 = 0;}
#define Moto_Back_R2	{L_In7 = 1,L_In8 = 0;}

#define Moto_Stop_L1	{L_In1 = 0,L_In2 = 0;}
#define Moto_Stop_R1	{L_In3 = 0,L_In4 = 0;}
#define Moto_Stop_L2	{L_In5 = 0,L_In6 = 0;}
#define Moto_Stop_R2	{L_In7 = 0,L_In8 = 0;}

这些函数里面没有可以让循迹小车转弯的功能。由于我们的循迹小车是不能直接转动轮胎方向的,所以我们要使小车两边轮胎的差速来达到转弯的目的。现在来讨论差速能够实现的转弯情况。

1.如果两边轮胎差速不大,且朝同一个方向转,这样只能实现小幅度的转动,可以用于小车的复位等情况。

2.如果两边轮胎旋转差速大,甚至一边正转、一边倒转,这样就能够实现90°的转弯,大转弯一般用于过直角弯。

制造差速就是调整电机的转速。可以改变通过电机的电流、或者施加于电机上的电压来使电机转速改变。电流的改变与电压的改变使一样的,在这里我们选择改变电压,也就是改变单片机发出的信号。如果单片机发出信号是稳定的、不断的,如图4-1所示,就会使一定时间内电机都有恒定电压,全速前进。那么想要调整电机转速,我们就只需要调整单片机一定周期内发出信号的时间,如图4-2让其没有电压的时间增长,就可以减慢转速。由于单片机能够定义周期都很短暂,所以人眼是不能够看到电机一转一停的情况,只能看到宏观表现——电机转速改变。更加学术的表达就是pwm信号调节直流电机转速

PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码

图4-1一个周期内稳定输出电压
图4-2改变一个周期内输出电压时间

 那么如何改变一个周期内单片机输出产生电压信号的时间呢。如图4-3所示,我们可以设置一个定时自增的数和一个固定的数。当定时自增的数大于固定数时,不产生信号或者产生信号。小于时则相反就可以达到图4-2的效果。

图4-3改变输出时间的方法

 对于这段有输出信号的部分占一个周期的时间,我们称之为占空比。调节pwm信号来调节直流电机转速,就是调节pwm信号中一个周期内输出电压的多少,也就是调节占空比。下面就来定义一下各个电机的占空比和自增的数(这里的pwmxx)。同时配置好图4-3所示的函数,这里仅放了一个电机的调节函数,其余电机同理即可。

unsigned char zkbL1 = 0;
unsigned char zkbR1 = 0;
unsigned char zkbL2 = 0;
unsigned char zkbR2 = 0;
unsigned char zkbD1 = 0;
unsigned char pwmL1 = 0;
unsigned char pwmR1 = 0;
unsigned char pwmL2 = 0;
unsigned char pwmR2 = 0;

//调占空比调速
void Pwm_L1 (void)//左前电机调速 
{
	pwmL1%=20;
		if(pwmL1<=zkbL1)
		{
			L_EnL1 = 1;
		}else 
		{
			L_EnL1 = 0;
		}
}

设置定时自增的数,我们需要用到单片机的定时器与中断功能。这些在这里不加以讲解,购买了单片机的人参考厂商配备的操作手册即可。下面就是产生pwm信号的函数。

void Timer0Init(void)		//10us@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0xF7;		//设置定时初始值
	TH0 = 0xFF;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	ET0 = 1;
	EA = 1;
	TR0 = 1;		//定时器0开始计时
}


//定时器中断服务产生pwm信号
void Timer0() interrupt 1
{
	TR0 = 0;//关闭定时器 
	TH0 = 0xF7;//初始化便于后续计时 
	TL0 = 0xFF;//初始化便于后续计时  
	pwmL1++;
	pwmR1++;
	Pwm_L1;
	Pwm_R1;
	TR0 = 1;//开启定时器 
}

有了这些,我们可以开始编写完整的运动函数

 void forward (void)//前进 
 {
 	zkbL1 = 18;
 	zkbR1 = 18;
 	Moto_Go_L1;//左前轮前进 
 	Moto_Go_R1;//右前轮前进 
 }

 void back (void)//后进 
 {
 	zkbL1 = 18;
 	zkbR1 = 18;
 	Moto_Back_L1;//左前轮后退 
 	Moto_Back_R1;//右前轮后退 
 }
void rightrunB (void)//大右转 
{
	zkbL1 = 20;
	zkbR1 = 18;
	Moto_Go_L1;//左前轮前进
	Moto_Back_R1;//右前轮后转 
 
}

void rightrunL (void)//小右转 
{
	zkbL1 = 20;
	zkbR1 = 18;
	Moto_Go_L1;//左前轮前进
	Moto_Go_R1;//右前轮慢速 
}

void leftrunB (void)//大左转 
{
	zkbL1 = 16;
	zkbR1 = 20;
	Moto_Go_R1;//右前轮前进
	Moto_Back_L1;//左前轮后转 
}

void leftrunL (void)//小左转 
{
	zkbL1 = 18;
	zkbR1 = 20;
	Moto_Go_R1;//右前轮前进
	Moto_Go_L1;//左前轮慢速 
}

void stop (void)
{
	Moto_Stop_L1;//左前轮停 
	Moto_Stop_R1;//右前轮停 
}

上面的代码都可以放在同一个.h文件中。在编写主要循迹代码时记得引用该头文件就行。

完成了这些简单的定义后,我们可以开始编写主要的循迹代码了。这里只是一个简单的示例,实际跑动还需要添加更多细节。

#include <REGX52.H>
#include <Define.H>


void main()
{

		Timer0Init();//开启10us定时与中断
		while(1)
		{
			if(CgqL1==0&&CgqL2==1&&CgqR1==1&&CgqR2==0)
			{
				forward();
			}else if (CgqL1==0&&CgqL2==1&&CgqR1==1&&CgqR2==1)
			{
				rightrunB();
			}else if (CgqL1==1&&CgqL2==0&&CgqR1==0&&CgqR2==0)
			{
				leftrunB();
			}else if (CgqL1==0&&CgqL2==0&&CgqR1==0&&CgqR2==0)
			{
				stop();
				Delay(50) ;
			}else if(CgqL1==0&&CgqL2==1&&CgqR1==0&&CgqR2==0)
			{
				leftrunL(); 
			} else if(CgqL1==0&&CgqL2==0&&CgqR1==1&&CgqR2==0)
			{
				rightrunL();
			}

	}
}

至于用什么写代码,以及如何将代码烧录进单片机内,在这里不加以描述。博主建议去b站上学习单片机相关入门操作。https://www.bilibili.com/video/BV1Mb411e7re?from=search&seid=1198388772741917233&spm_id_from=333.337.0.0https://www.bilibili.com/video/BV1Mb411e7re?from=search&seid=1198388772741917233&spm_id_from=333.337.0.0

物联沃分享整理
物联沃-IOTWORD物联网 » 51单片机控制的高效循迹小车设计

发表评论