超聲波測距系統(stc89c52單片機+lcd1602+srf04超聲波傳感器+DS18B20溫度傳感器+無源蜂鳴器)

傳感與檢測的大作業,小組選題爲基於超聲波測距的倒車報警系統。

計劃通過超聲波傳感器測距,溫度傳感器校正誤差,lcd1602顯示結果,距離小於某一值時將蜂鳴報警。

   總體設計思路

HC-SR04超聲波傳感器模塊爲核心裝置,發射、接受超聲波,產生使單片機開始計數和停止計數的信號,從而計算超聲波往返的時間。

利用溫度傳感器DS18B20測量溫度並修正當前的聲速

LCD1602液晶模塊爲顯示裝置,單片機計算完成後輸送信息到LCD1602,顯示測量距離和當前環境的溫度。

當單片機判斷距離小於某值時控制蜂鳴器電路產生報警信號。

超聲波測距的原理介紹

超聲波測距的原理採用回波探測法。
超聲波發射器向某一方向發射超聲波,在發射時刻的同時開始計時,超聲波在介質中傳播,
途中碰到障礙物就立即返回來,超聲波接收器收到反射波就立即停止計時。
超聲波在空氣中0℃時的傳播速度爲C0=332m/s,又已知超聲波速度C0與溫度T(℃)的關係爲
  C=C0+0.607×T
計算發射點距障礙物的距離的公式爲
S=C*TIME/2。

超聲波傳感器(HC-SR04)介紹

該超聲波收發模塊可產生40kHz的方波,經放大電路驅動後超聲波發射探頭髮射超聲波,
發射出去的超聲波經障礙物反射後由超聲波接收探頭接收。
當沒有發送超聲波時ECHO引腳輸出低電平。從開始發送超聲波到街道回波這一段時間內ECHO引腳均爲高電平,
可以此控制計數器的啓動與停止。在ECHO引腳上產生方波脈衝的脈衝寬度與被測距離成線性關係。
使用HC-SR04超聲波收發模塊進行距離測量測量時,單片機只需要輸出觸發信號,並監視迴響引腳,
通過定時器計算迴響信號寬度,並換算成距離即可。此超聲波模塊所需單片機的引腳少,便於控制。

報警距離調整功能設計

P3.2和P3.3引腳檢測到低電平時產生外部中斷0,1,
由軟件代碼調整內部alarm_distance變量,
P3.2腳按鍵按下每次增加5cm
P3.3腳按鍵按下每次減少5cm

程序設計

主程序

          

溫度數據處理子函數

 

 

距離計算子函數

 

LCD驅動程序

#include"lcd.h"

/*******************************************************************************
* 函 數 名         : Lcd1602_Delay1ms
* 函數功能		   : 延時函數,延時1ms
* 輸    入         : c
* 輸    出         : 無
* 說    名         : 該函數是在12MHZ晶振下,12分頻單片機的延時
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //誤差 0us
{
    uchar a,b;
	for (; c>0; c--)
	{
		 for (b=199;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(5);
	LCD1602_E = 0;

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

	LCD1602_E = 1;	 //寫入時序
	Lcd1602_Delay1ms(5);
	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(5);
	LCD1602_E = 0;

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

	LCD1602_E = 1;	  //寫入時序
	Lcd1602_Delay1ms(5);
	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

 

DS18B20溫度傳感器驅動程序

#include"temp.h"
/*******************************************************************************
* 函數名         : Delay1ms
* 函數功能		   : 延時函數
* 輸入           : 無
* 輸出         	 : 無
*******************************************************************************/

void Delay1ms(unsigned int y)
{
	unsigned int x;
	for(y;y>0;y--)
		for(x=110;x>0;x--);
}
/*******************************************************************************
* 函數名         : Ds18b20Init
* 函數功能		   : 初始化
* 輸入           : 無
* 輸出         	 : 初始化成功返回1,失敗返回0
*******************************************************************************/

unsigned char Ds18b20Init()
{
	unsigned int i;
	DSPORT=0;			 //將總線拉低480us~960us
	i=70;	
	while(i--);//延時642us
	DSPORT=1;			//然後拉高總線,如果DS18B20做出反應會將在15us~60us後總線拉低
	i=0;
	while(DSPORT)	//等待DS18B20拉低總線
	{
		i++;
		if(i>5000)//等待>5MS
			return 0;//初始化失敗	
	}
	return 1;//初始化成功
}

/*******************************************************************************
* 函數名         : Ds18b20WriteByte
* 函數功能		   : 向18B20寫入一個字節
* 輸入           : com
* 輸出         	 : 無
*******************************************************************************/

void Ds18b20WriteByte(unsigned char dat)
{
	unsigned int i,j;
	for(j=0;j<8;j++)
	{
		DSPORT=0;			//每寫入一位數據之前先把總線拉低1us
		i++;
		DSPORT=dat&0x01; //然後寫入一個數據,從最低位開始
		i=6;
		while(i--); //延時68us,持續時間最少60us
		DSPORT=1;	//然後釋放總線,至少1us給總線恢復時間才能接着寫入第二個數值
		dat>>=1;
	}
}
/*******************************************************************************
* 函數名         : Ds18b20ReadByte
* 函數功能		   : 讀取一個字節
* 輸入           : com
* 輸出         	 : 無
*******************************************************************************/


unsigned char Ds18b20ReadByte()
{
	unsigned char byte,bi;
	unsigned int i,j;	
	for(j=8;j>0;j--)
	{
		DSPORT=0;//先將總線拉低1us
		i++;
		DSPORT=1;//然後釋放總線
		i++;
		i++;//延時6us等待數據穩定
		bi=DSPORT;	 //讀取數據,從最低位開始讀取
		/*將byte左移一位,然後與上右移7位後的bi,注意移動之後移掉那位補0。*/
		byte=(byte>>1)|(bi<<7);						  
		i=4;		//讀取完之後等待48us再接着讀取下一個數
		while(i--);
	}				
	return byte;
}
/*******************************************************************************
* 函數名         : Ds18b20ChangTemp
* 函數功能		   : 讓18b20開始轉換溫度
* 輸入           : com
* 輸出         	 : 無
*******************************************************************************/

void  Ds18b20ChangTemp()
{
	Ds18b20Init();
	Delay1ms(1);
	Ds18b20WriteByte(0xcc);		//跳過ROM操作命令		 
	Ds18b20WriteByte(0x44);	    //溫度轉換命令
//	Delay1ms(100);	//等待轉換成功,而如果你是一直刷着的話,就不用這個延時了
   
}
/*******************************************************************************
* 函數名         : Ds18b20ReadTempCom
* 函數功能		   : 發送讀取溫度命令
* 輸入           : com
* 輸出         	 : 無
*******************************************************************************/

void  Ds18b20ReadTempCom()
{	

	Ds18b20Init();
	Delay1ms(1);
	Ds18b20WriteByte(0xcc);	 //跳過ROM操作命令
	Ds18b20WriteByte(0xbe);	 //發送讀取溫度命令
}
/*******************************************************************************
* 函數名         : Ds18b20ReadTemp
* 函數功能		   : 讀取溫度
* 輸入           : com
* 輸出         	 : 無
*******************************************************************************/

int Ds18b20ReadTemp()
{
	int temp=0;
	unsigned char tmh,tml;
	Ds18b20ChangTemp();			 	//先寫入轉換命令
	Ds18b20ReadTempCom();			//然後等待轉換完後發送讀取溫度命令
	tml=Ds18b20ReadByte();		//讀取溫度值共16位,先讀低字節
	tmh=Ds18b20ReadByte();		//再讀高字節
	temp=tmh;
	temp<<=8;
	temp|=tml;
	return temp;
}


 

 

PROTEUS仿真

原理圖

實物

   (還有一張圖片大了上傳不成功)

總結

1.發現返回的溫度值不正常,導致顯示的溫度值(ascII碼顯示)不是數值而是亂碼,經過檢查發現獲取溫度的函數中溫度值比實際值大10倍,修正後問題解決。

2.通過溫度校正超聲波的速度時,計算出的距離總是比實際距離小,檢查發現存在精度損失,原因爲速度在函數中定義時爲unsigned int型,不符合實際情況,修改爲float型後問題解決。

3.給蜂鳴器管腳輸出高電平後,蜂鳴器不響,
經查閱資料發現,開發板上的蜂鳴器爲無源蜂鳴器,
需要一定週期的脈衝序列方可響,而不是單一的高電平。

4.發現計算的距離隨着時間不斷遞增,直至超出量程,經過檢查發現,在每次計算時間後,只有計數器低位被置零,高位沒有置零,修改後問題解決。
 

 

 

 

 


 

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