基于51单片机四路循迹小车

基于51单片机四路循迹小车这学期开设的51单片机课程的课程设计即将验收,今天开始正式着手做循迹小车~

大家好,又见面了,我是你们的朋友全栈君。

这学期开设的51单片机课程的课程设计即将验收,今天开始正式着手做循迹小车~

一. 任务要求

在这里插入图片描述
在这里插入图片描述

二. 系统原理

本系统以设计题目的要求为目的,采用STC89C52单片机为控制核心,利用红外传感器检测轨道,控制电动小汽车的自动循迹,快慢速行驶。

这里的轨道是指汽车沿着白色地板上的黑线行驶。由于黑线与白地板的反射系数不同,可以根据接收到的反射光的强度来判断“道路”。常用的方法是红外检测。

红外检测方法,即在汽车行驶过程中,利用红外光在具有不同颜色的物体表面的不同反射特性,不断向地面发射红外光。当红外光与白纸地板相遇时,会发生漫反射,反射光被安装在车内的接收器接收,如果遇到黑线,就会变成红色。外部光被吸收,车内的接收器不能接收红外线。单片机根据是否接收到反射的红外光来确定黑线的位置和汽车的行驶路线。

三. 整体方案

1. 控制模块

小车的核心控制部件采用STC公司生产的8位单片机STC89C52。它是一种低功耗、高性能CMOS8位微控制器,具有8K字节系统可编程Flash存储器。STC89C52使用经典的MCS-51内核,但是做了很多的改进使得芯片具有传统51单片机不具备的功能。STC89C52有3个16 位定时器/计数器,2个外部中断,1个串口中断。

最小系统

51单片机的具体引脚功能不细说,不了解的可自行百度。直接上最小系统电路图(自己画的,凑合着看)。顺便说一句,在我看来一个没有指示灯的电路是最烦的电路<(`^´)> 我的复位一定要有灯才行!

在这里插入图片描述
三部分:
①电源电路:给单片机提供5V的电源
②时钟电路:外接11.0592M石英晶振。
③复位电路:确保单片机是从一个确定的初始状态开始。

焊接时注意P0口要接上拉电阻,否则不能用,一般都用排阻做上拉电阻(当然如果能自己画板子就更好了)。

去除AD网格线

记一下怎么去除AD网格线,如图步骤,最后去掉Visible前面的对勾即可,别忘记最后点OK:
在这里插入图片描述

2. 电机驱动模块

我使用的是最经典的LM298N电机驱动:

我太喜欢用298了。不仅可以接至12v(即拥有更强的驱动能力),而且有过电流保护功能,当出现电机卡死时,可以保护电路和电机等。且自带7805降压模块可直接给单片机供电~~
实物图:
在这里插入图片描述
驱动与各部分的连接简介:
输出A、B:分别接到左右马达上,通过使能A、使能B来控制电机正常工作(接高电平为正常工作);
单片机IO控制输入(即IN1-IN4):用来控制马达正反转。接单片机引脚,通过在程序中给引脚高低电平实现正转或反转(详见下边输入输出关系表);
12V输入:接电池盒正极;
5V输入:接单片机VCC,给单片机供电;
电源地(即GND):把电池盒负极和单片机的GND一起接入。
在这里插入图片描述
电路图:
在这里插入图片描述
输入输出关系(使能ENA、ENB均为1的状态下):

In1 In2 运转状态
1 0 正转
0 1 反转
1 1 刹停
0 0 停止

3. 电源模块

我自己使用的是两节18500锂电池供电。如果觉得两节电池不够,三节12V又太大,也可以在中间加一个7809稳压管把电压降到9V再接入驱动:
在这里插入图片描述

4. 循迹模块

光电循迹是由四对红外收发管组成,通过检测接收到的反射光强,判断黑白线。

当模块探测灯检测到黑线时指示灯熄灭,同时 OUT端口持续输出高电平信号,反之未检测到黑线的探测灯应常亮,OUT端口输出低电平。该模块检测距离 2~60cm,检测角度 35 ° ,检测距离可以通过电位器进行调节,顺时针调电位器,灵敏度增加;逆时针调电位器,灵敏度减少。
在这里插入图片描述
原理图由红外对管和电压比较器两部分组成,红外对管输出的模拟电压通过电压比较器转换成数字电平输出到单片机。

主控板的电路图:
在这里插入图片描述
小板电路图:
在这里插入图片描述
注意连接的时候:VCC-VCC、GND-GND、IN-OUT。

比较器LM339
在这里插入图片描述

5. 超声波测距模块

实物图:
在这里插入图片描述
电路图:
在这里插入图片描述
工作原理
  (1)采用IO口TRIG触发测距,给至少10us的高电平信号;
  (2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
  (3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。
  测试距离=(高电平时间*声速(340M/S))/2;

四. 车体实物图

在这里插入图片描述

五. 软件实现

#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;

sbit ENA = P2^0;   //右点机使能
sbit IN1 = P2^1;   //为0右轮反转
sbit IN2 = P2^2;   //为0右轮正转
sbit IN3 = P2^3;   //为0左轮正转
sbit IN4 = P2^4;   //为0左轮反转
sbit ENB = P2^5;   //左电机使能

sbit left1 = P1^3;
sbit left2 = P1^2;
sbit right1 = P1^1;
sbit right2 = P1^0;

u8 PWMCnt1 = 0;
u8 PWMCnt2 = 0;
u8 cntPWM1 = 0;
u8 cntPWM2 = 0;

void Timer0Init();
void XunJi();

void main()
{
	Timer0Init();
	while(1)
	{
		XunJi();

	}	
}
// i = 1时, 大概延时10us
//void delay(u16 i)
//{
//    while (i--);
//}

void Timer0Init()
{
	TH0 = 0xFF;
	TL0 = 0xA3;
	TMOD &= 0xF0;
	TMOD |= 0x01;
	EA = 1;
	ET0 = 1;
	TR0 = 1;
}

void TurnRight1()		   //右转
{
	IN1 = 0;	//右轮反转
	IN2 = 1;

	IN3 = 0;	//左轮正转
	IN4 = 1;
	cntPWM1 = 70;
	cntPWM2	= 55;
}

void TurnRight2()		   //右转
{
	IN1 = 0;	//右轮反转
	IN2 = 1;

	IN3 = 0;	//左轮正转
	IN4 = 1;
	cntPWM1 = 50;
	cntPWM2	= 40;
}

void TurnLeft1()				//左转
{
	IN1 = 1;
	IN2 = 0;   	//右轮正转

	IN3 = 1;
	IN4 = 0;   	//左轮反转
	cntPWM1 = 55;
	cntPWM2	= 70;
}			  	

void TurnLeft2()				//左转
{
	IN1 = 1;
	IN2 = 0;   	//右轮正转

	IN3 = 1;
	IN4 = 0;   	//左轮反转
	cntPWM1 = 40;
	cntPWM2	= 50;
}	
	
void Forward()				 //前进
{
	IN1 = 1;
	IN2 = 0;   	//右轮正转

	IN3 = 0;   	//左轮正转
	IN4 = 1;

	cntPWM1 = 40;
	cntPWM2	= 40;
}

//void Backward()					 //后退
//{
//	IN1 = 0;	//右轮反转
//	IN2 = 1;
//
//	IN3 = 1;
//	IN4 = 0;	//左轮反转
//	cntPWM1 = 30;
//	cntPWM2	= 30;
//}

void Stop()					  //停止
{
	IN1 = 0;
	IN2 = 0;

	IN3 = 0;
	IN4 = 0;
}
void XunJi()

{
	unsigned char flag = 0;

	if((left1 == 0)&&(left2 == 0)&&(right1 == 0)&&(right2 == 0))   //0 0 0 0
		flag = 0;

	if((left1 == 0)&&(left2 == 0)&&(right1 == 0)&&(right2 == 1))   //0 0 0 1
		flag = 1;

	if((left1 == 0)&&(left2 == 0)&&(right1 == 1)&&(right2 == 0))   //0 0 1 0
		flag = 0;

	if((left1 == 0)&&(left2 == 0)&&(right1 == 1)&&(right2 == 1))   //0 0 1 1
		flag = 1;

	if((left1 == 0)&&(left2 == 1)&&(right1 == 0)&&(right2 == 0))   //0 1 0 0
		flag = 0;

	if((left1 == 0)&&(left2 == 1)&&(right1 == 0)&&(right2 == 1))   //0 1 0 1
		flag = 4;

	if((left1 == 0)&&(left2 == 1)&&(right1 == 1)&&(right2 == 0))   //0 1 1 0
		flag = 0;
	
	if((left1 == 0)&&(left2 == 1)&&(right1 == 1)&&(right2 == 1))    //0 1 1 1
		flag = 1;
	
	if((left1 == 1)&&(left2 == 0)&&(right1 == 0)&&(right2 == 0))   //1 0 0 0
		flag = 3;
	
	if((left1 == 1)&&(left2 == 0)&&(right1 == 0)&&(right2 == 1))   //1 0 0 1
		flag = 0;
	
	if((left1 == 1)&&(left2 == 0)&&(right1 == 1)&&(right2 == 0))   //1 0 1 0
		flag = 2;
	// ?
	if((left1 == 1)&&(left2 == 0)&&(right1 == 1)&&(right2 == 1))   //1 0 1 1
		flag = 0;
	
	if((left1 == 1)&&(left2 == 1)&&(right1 == 0)&&(right2 == 0))   //1 1 0 0
		flag = 3;
	 //?
	if((left1 == 1)&&(left2 == 1)&&(right1 == 0)&&(right2 == 1))   //1 1 0 1
		flag = 0;

	if((left1 == 1)&&(left2 == 1)&&(right1 == 1)&&(right2 == 0))   //1 1 1 0
		flag = 3;
	
	if((left1 == 1)&&(left2 == 1)&&(right1 == 1)&&(right2 == 1))   //1 1 1 1
		flag = 5;
	
	switch(flag)
	
{
		case 0:Forward();break;
		
		
		case 1:TurnRight1();break;
		
		
		case 2:TurnRight2();break;
		
		
		case 3:TurnLeft1();break;
		
		
		case 4:TurnLeft2();break;


		default:Stop();break;
	}
}

void InterruptTime0() interrupt 1
{
	PWMCnt1++;
	PWMCnt2++;
	
	if(PWMCnt1  >= 200)
	{
		PWMCnt1 = 0;
	}
	if(PWMCnt1 <= cntPWM1)
	{
		ENA = 1;
	}
	else
	{
		ENA = 0;
	}

	if(PWMCnt2 >= 200)
	{
		PWMCnt2 = 0;
	}
	if(PWMCnt2 <= cntPWM2)
	{
		ENB = 1;
	}
	else
	{
		ENB = 0;
	}

	TH0 = (65536 - 50)/256;
	TL0 = (65536 - 50)%256;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/152244.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 说一下 runnable 和 callable 有什么区别(call你)

    Runnable和Callable的区别是,(1)Callable规定的方法是call(),Runnable规定的方法是run().(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得(3)call方法可以抛出异常,run方法不可以(4)运行Callable任务可以拿到一个Future对象,Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的…

    2022年4月9日
    72
  • STUN详解

    STUN详解STUN是一个简单的客户端-服务器协议。客户端发送一个请求到一台服务器,而服务器返回一个响应。有两种类型的请求:绑定请求(通过UDP发送)和共享密钥请求(发送TLS(通过TCP))。共享秘密请求服务器返回一个临时的用户名和密码。此用户名和密码用于在随后的绑定请求和绑定响应,身份验证和消息完整性的目的。STUN客户和STUN服务器之间可能有一个或多个NAT。

    2022年7月17日
    36
  • 数据挖掘/机器学习/算法岗2017校招面试总结「建议收藏」

    数据挖掘/机器学习/算法岗2017校招面试总结「建议收藏」目前就职于腾讯,想内推朋友可以发我简历(校招/社招都要),邮箱384375530@qq.com,注明岗位和工作城市。这个岗位叫法很多,算法岗,数据挖掘岗,机器学习岗,基础研究等等,总结一下从16年9月校招的面试情况。百度:师姐给我内推的,一面聊了2个半小时,基本在写代码。用MapReduce写好友推荐,在一堆单词里面找出现次数最多的k个;设计一个栈,O(1)时间返回最值;求多叉树深…

    2022年6月12日
    32
  • linux运维面试题总结「建议收藏」

    linux运维面试题总结「建议收藏」一、问答题1、安装linux系统对硬盘分区时,必须有那两种分区类型?2、简述raid0、raid1、raid5三种工作原理及特点3、linux下如何改ip,主机名,dns?4、一个ext3的文件分区,当使用touchtest.file命令创建一个新文件时报错,报错的信息是显示磁盘已满,但是采用df-h命令查看磁盘大小时,只使用了60%的磁盘空间,为什么会出现这个情况,说说你的理由5、…

    2022年5月4日
    46
  • JAVA基础——接口(全网最详细教程)「建议收藏」

    JAVA基础——接口(全网最详细教程)「建议收藏」Java基础——接口接口概念官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。我的解释:接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组…

    2022年6月5日
    29
  • 如何查看文件夹被什么程序使用

    如何查看文件夹被什么程序使用

    2021年7月18日
    86

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号