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]

博主能力有限,如有不到之處還請各位批評指正 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章