傳感與檢測的大作業,小組選題爲基於超聲波測距的倒車報警系統。
計劃通過超聲波傳感器測距,溫度傳感器校正誤差,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.發現計算的距離隨着時間不斷遞增,直至超出量程,經過檢查發現,在每次計算時間後,只有計數器低位被置零,高位沒有置零,修改後問題解決。