51单片机项目设计:超声波测距、智能小车(keil+proteus)带仿真

超声波测距小车

资料链接:51单片机超声波测距.rar

项目展示:
在这里插入图片描述
仿真展示 :
在这里插入图片描述

一、项目要求

(一)项目内密

本课题的主要内容是,将超声波传感器安装在一台沿直线行走的小车上,由AT89C51 控制超声波传感器,发出超声信号,记录超声液从发射到接收的所需要的时间,得到其与|被测物体之间的能离。在小车行走的过程中,不断重复测距,得到一组距离数据。由单片机记录测得的距离,经过数据处理,将其显示在液晶屏上。同时,将一组距离数据,由串口传到上位机,由VB程序保存距离数据,并画出曲线,就得到了被测物体的单边轮廓。

(二)课题研究方需

超声波测距仪系统主要由单片机最小系统、超声波模块、LCO显示电路及电源电路组成。系统的主要功能如下:

超声波传感器发射和接收超声波,依据计时时间测量出与被测物体之间的矩离。

AT89CS51单片机作为主控制器,控制超声波信号的发送和接收,可以用来控制发射模块脸发脉冲的开始时间和脉宽,响应接受超声波返回时刻并进行测量和计数发射到返回的时间间隔。

液晶屏显示器实时显示测量距离。

上位机串口通讯,接收距离数据,利用VB程序把则好的距离记下并画出曲线,还原轮那曲线,从而实现通过超声波来测量物体单边轮常测量。

二、项目设计

(一)项目设计思想

1、使用HC-SR04超声波传感器模块测量距离
2、使用LCD1602液晶显示屏显示实时测量距离
3、使用L298N电机驱动,驱动两个电机运转,设置一个按键,按下按键,小车前进一段距离
4、开启串口通信,把距离数据通过串口发送到电脑进行处理

(二)购买材料
1、智能小车底盘
在这里插入图片描述
2、L298N
在这里插入图片描述
3、超声波模块HC-SR04
在这里插入图片描述
4、液晶屏 LCD1602
在这里插入图片描述
再准备一些其他小元件就OK,单片机最小系统、洞洞板、按键、插座等

(三)先设计硬件
根据原理图设计硬件
在这里插入图片描述
这是我设计好的实物图
在这里插入图片描述
在这里插入图片描述

(四)编写代码
main.c

/********************************************
项目名称:51超声波测距
作者:化作尘
版本:V1.1
更新时间:V1.0 2020年4月12日18:28:02
	      V1.1 2020年4月23日13:35:00
邮箱:[email protected]
*********************************************/

#include "reg52.H"//器件配置文件
#include"lcd.h"
//SR04传感器接口
sbit RX  = P3^2;
sbit TX  = P3^3;
sbit key  = P1^4;
char keymode=0;   //按键 模式
int keycount=0;  //小车运动延时控制

//变量声明
unsigned int  time=0;
unsigned long S=0;

char Mode=0;
bit  flag=0;
bit flag_KEY=0;
unsigned char act[]={0xff,0xfa,0xf5,0xf9,0xf6};	  //小车动作控制
unsigned char disbuff[4]	   ={0,0,0,0};		 //缓存数组用于存放距离信息
/***************************************************************************/
//延时
void delay_ms(unsigned int m)
{
    unsigned int b,c;
    for(c=0;c<m;c++)
        for(b=0;b<120;b++);
}
//短延时
void delay(unsigned int t)
{
	while(t--);
}
/***************************************************************************/
void Key(void)//按键扫描函数
{
	key = 1;
	delay_ms(1);
	if(key==0)		  //检测按键K1是否按下	   开关
	{	
			delay_ms(10);   //消除抖动 一般大约10ms
			if(key==0)	 //再次判断按键是否按下
			{
				keymode++;
				if(keymode == 2)keymode = 0;
				
			}
			while(!key);	 //检测按键是否松开
	}
	if(keymode==1)  //检测到按键按下了一次,就让运动延时一会
	{
		keycount++;
		if(keycount==550)
		{
			keycount=0;
			keymode=0;
		}
	}
	P1 = act[keymode];		//小车运动控制
}
/***************************************************************************/
 //超声波测距函数,距离保存到S
void Conut(void)  
{
	TX=1;			          //启动一次模块
	delay(10);        //延时10US
	TX=0;; 
	while(!RX);		//当上次接收完波后,RX引脚是低电平,取反就是1,此while成立,反复判断RX状态。当RX没有接收到返回波时是高电平,取反就是0,此while不成立,跳出
	TR0=1;			//开启计数
	while(RX);		//当RX没有接收到返回波,此while成立,程序停在这里一直判断RX状态。当RX接收到返回波,RX引脚变为低电平,此while不成立,跳出
	TR0=0;			//停止计数
	time=TH0*256+TL0;	  //读出T0的计时数值
	TH0=0;
	TL0=0;				  //清空计时器
	S=(time*1.7)/100;     //算出来是CM
	//声音的速度是340m/s,时间的单位是us,计算到秒需要将时间数据/1000000,
	//长度=速度*时间,340*time/1000000,长度数据单位是m转换成cm需要乘以100得到340*time/10000,
	//小数点都向左移两位得到3.4*time/100,因为超声波是往返了,所以再除以2,得到距离数据(time*1.7)/100
	if(Mode==0)			  //非设置状态时
	{
		if(S>=999) //超出测量范围显示“-”
		{	
			disbuff[0]='o'-0x30;	   //“-”
			disbuff[1]='v'-0x30;	   //“-”
			disbuff[2]='e'-0x30;	   //“-”
			disbuff[3]='r'-0x30;	   //“-”
		}
		else
		{
			disbuff[0]=S%1000/100;		 //将距离数据拆成单个位赋值
			disbuff[1]=S%100/10;
			disbuff[2]='.'-0x30;
			disbuff[3]=S%10;
		}
	}

} 
/***************************************************************************/
//定时器0
void zd0() interrupt 1 		 //T0中断用来计数器溢出,超过测距范围
{
	flag=1;						 //中断溢出标志
}
/***************************************************************************/
//定时器初始化
void Timer_Init(void)
{
	TMOD=0x11;		   //设T0为方式1
	TH0=0;
	TL0=0;          
	TR0=0;				//停止计数
	ET0=1;				//允许T0中断
	EA=1;					//开启总中断
}
/***************************************************************************/
//串口初始化
void UsartInit()  
{
	SCON=0X50;			//设置为工作方式1
	TMOD=0X20;			//设置计数器工作方式2
	PCON=0X80;			//波特率加倍
	TH1=0XF3;				//计数器初始值设置,注意波特率是4800的
	TL1=0XF3;
	EA=1;						//打开总中断
	TR1=1;					//打开计数器
}
/***************************************************************************/
//串口发送距离数据
void Uart_Send(void)
{
	/**将距离数据通过串口发送出去**/
		SBUF=S/256;//将数据放入到发送寄存器
		while(!TI);			 //等待发送数据完成
		TI=0;						 //清除发送完成标志位
		SBUF=S%256;//将数据放入到发送寄存器
		while(!TI);			 //等待发送数据完成
		TI=0;						 //清除发送完成标志位
}


/****************************************************************************/
//主函数
void main(void)
{ 
	int displaycount=0;
	Timer_Init();      //初始化定时器
	UsartInit();				//串口初始化 9600
	LcdInit();	      //初始化LCD
	LcdWritestr("  superwave  ");		//液晶显示距离
	while(1)
	{
		Conut();			//计算
		displaycount++;
		if(displaycount==10)//隔一段时间刷新一次显示屏
		{
			displaycount=0;
			LcdWriteCom(0x80);
			P1 = 0xff;
			LcdWritestr("distance:");		//液晶显示距离
			LcdWriteData(disbuff[0]+0x30);
			LcdWriteData(disbuff[1]+0x30);
			LcdWriteData(disbuff[2]+0x30);
			P1 = act[keymode];		//小车运动控制
			LcdWriteData(disbuff[3]+0x30);
			LcdWritestr("cm");
		}
		Key();    //按键控制电机运转
		Uart_Send();//串口发送距离数据
	}
}

void Usart() interrupt 4
{
	RI = 0;//清除接收中断标志位
}
             

lcd.h

#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS

/**********************************
包含头文件
**********************************/
#include<reg52.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;

/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c);   //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/	
void LcdWriteData(uchar dat)	;
/*LCD1602初始化子程序*/		
void LcdInit();	
void LcdWritestr(uchar *dat);
void LcdWritenum(uint num);
void LcdClean(void);					  

#endif

lcd.c

#include "lcd.h"
/*******************************************************************************
* 函 数 名         : Lcd1602_Delay1ms
* 函数功能		   : 延时函数,延时1ms
* 输    入         : c
* 输    出         : 无
* 说    名         : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //误差 0us
{
    uchar a,b;
	for (; c>0; c--)
	{
		 for (b=20;b>0;b--)
		 {
		  	for(a=1;a>0;a--);
		 }      
	}
    	
}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能		   : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
#ifndef 	LCD1602_4PINS	 //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;     //使能
	LCD1602_RS = 0;	   //选择发送命令
	LCD1602_RW = 0;	   //选择写入
	
	LCD1602_DATAPINS = com;     //放入命令
	Lcd1602_Delay1ms(1);		//等待数据稳定

	LCD1602_E = 1;	          //写入时序
	Lcd1602_Delay1ms(5);	  //保持时间
	LCD1602_E = 0;
}
#else 
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;	 //使能清零
	LCD1602_RS = 0;	 //选择写入命令
	LCD1602_RW = 0;	 //选择写入

	LCD1602_DATAPINS = com;	//由于4位的接线是接到P0口的高四位,所以传送高四位不用改
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	 //写入时序
//	Lcd1602_Delay1ms(1);//注释让lcd写快一点 
	LCD1602_E = 0;

	LCD1602_DATAPINS = com << 4; //发送低四位
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	 //写入时序
//	Lcd1602_Delay1ms(1);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能		   : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/		   
#ifndef 	LCD1602_4PINS		   
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	//使能清零
	LCD1602_RS = 1;	//选择输入数据
	LCD1602_RW = 0;	//选择写入

	LCD1602_DATAPINS = dat; //写入数据
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;   //写入时序
	Lcd1602_Delay1ms(5);   //保持时间
	LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	  //使能清零
	LCD1602_RS = 1;	  //选择写入数据
	LCD1602_RW = 0;	  //选择写入

	LCD1602_DATAPINS = dat;	//由于4位的接线是接到P0口的高四位,所以传送高四位不用改
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	  //写入时序
//	Lcd1602_Delay1ms(1);
	LCD1602_E = 0;

	LCD1602_DATAPINS = dat << 4; //写入低四位
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	  //写入时序
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能		 : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/		   
#ifndef		LCD1602_4PINS
void LcdInit()						  //LCD初始化子程序
{
 	LcdWriteCom(0x38);  //开显示
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}
#else
void LcdInit()						  //LCD初始化子程序
{
	LcdWriteCom(0x32);	 //将8位总线转为4位总线
	LcdWriteCom(0x28);	 //在四位线下的初始化
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}
#endif



/*******************************************************************************
* 函 数 名         : LcdWritestr
* 函数功能		   : 向LCD写入多个字节的数据
* 输    入         : *dat
* 输    出         : 无
*******************************************************************************/		   
	   
void LcdWritestr(uchar *dat)			//写入字符串
{
	while(*dat != '\0')
	{
		LcdWriteData(*dat);
		dat++;	
	}
}

点个小赞赞,获取资料
发送“51单片机超声波测距" 到我的邮箱:[email protected]

博主能力有限,如有不到之处还请各位批评指正 。

猜你喜欢

转载自blog.csdn.net/mbs520/article/details/106599219