本程序來源于網絡,由51hei收集整理,測距原理:單片機IO口接超聲波模塊TX引腳,由IO口產生40KHz頻率方波發送給TX用以超聲波模塊發送超聲波,同時打開單片機計時器,超聲波遇到障礙后返回,模塊接收頭接收到超聲波后產生信號由電路濾波整形放大后在RX引腳輸出高電平,RX接單片機的P3^2(51內核單片機的外部中斷),在中斷服務程序中停止計數器,對所計數值進行處理得到超聲波往返所用時間再乘以風速即得測量距離。由于超聲波屬于聲波范圍,其速度與溫度有關,不同溫度下超聲波在空氣中傳播速度隨溫度變化關系:V=331.4+0.61T。所以要是測量結果更加精確需另加溫度補償模塊(本實驗中采用開發板上的DS18B20溫度傳感器完成溫度補償)。外圍可添加LCD顯示(本實驗采用開發板1602LCD)和語音播報模塊(本實驗采用ISD4004語音模塊)。
AT89S52單片機引腳圖
1602lcd引腳圖
源程序
// *********************單片機實驗室******************************/
// *文件名稱 :csbspk.c
// *功能 : 實現超聲波測距并語音播報 (測量范圍:4~80cm)
// *引腳連接 : ISD4004 SS接P1.2 MOSI接P1.0 MISO接P1.1 SCLK接P1.3
// * 超聲波模塊 TX接P3.1 RX接P3.2 J7接上插冒
// *當前版本 :1.0
// *作者 : 劉松
// *完成日期 :2010.11.11
// ****************************************************************/
#include<reg52.h>
#include <intrins.h>
#include"lcd.h" // 液晶顯示
#include"ds18b20.h" //溫度傳感器
sbit P3_1=P3^1;
sbit key1=P2^1; //定義按鍵
sbit SS=P1^2; //以下四行定義ISD4004引腳
sbit MOSI=P1^0;
sbit MISO=P1^1;
sbit SCLK=P1^3;
uchar addr; //語音地址全局變量
uchar s; //語音所對應數字
int VD; //擴大十倍的聲速
int D; //測量的距離
int temp; //超聲波反射時間
void timer() //初始化計數器
{
TMOD=0x10;
TH1=0;
TL1=0;
EA=1;
EX0=1;
}
void delay25us_40KHz(unsigned char us) //產生方波用于超聲波發射
{
TR1=1;
while(us--)
{
P3_1= 0;
_nop_();_nop_();
_nop_();_nop_();
_nop_();_nop_();
_nop_();_nop_();
_nop_();_nop_();
_nop_();
P3_1= 1;
_nop_();_nop_();
_nop_();_nop_();
}
P3_1= 1;
}
rec() interrupt 0 //計算超聲波反射時間
{
TR1=0;
EA=0;
temp=TH1*256+TL1;
}
void delayms(uchar ms) // 延時子程序用于語音播放上電等待
{
uchar j;
while(ms--)
{
for(j = 0; j < 120; j++);
}
}
void delay2(int m) //長延時用于語音播放
{
int l,j;
for(l=0;l<30001;l++)
for(j=0;j<m;j++);
}
////////////////////////////液晶顯示子函數//////////////////////////////////
void display()
{
float V;//聲速
V=331.4+0.61*T; VD=V*10;
D=temp*V/2000-29;
displaystring(0,0,"Dis=");
displaychar(4,0,(D/100)+0x30);
displaychar(5,0,(D0/10)+0x30);
displaychar(6,0,'.');
displaychar(7,0,(D)+0x30);
displaystring(8,0,"cm");
displaystring(0,1,"T=");
displaychar(5,1,(TD)+0x30);
displaychar(4,1,'.');
displaychar(3,1,(TD0/10)+0x30);
displaychar(2,1,(TD/100)+0x30);
displaychar(6,1,0xdf);
displaychar(7,1,'C');
displaystring(9,1,"V=");
displaychar(11,1,(VD/1000)+0X30);
displaychar(12,1,(VD00/100)+0X30);
displaychar(13,1,(VD0/10)+0X30);
displaychar(14,1,'.');
displaychar(15,1,(VD)+0X30);
}
///////////////////////////////////////////////////////////////
////////////////放音部分子程序,放音地址由ADDR決定////
void play(addr)
{
uchar y;
SS=0;
MOSI=0;//發送開始
SCLK=0;
for(y=0;y<8;y++)
{
SCLK=0;
if((0x20>>y)&0x01)MOSI=1; //上電命令
else MOSI=0;
_nop_();
_nop_();
_nop_();
SCLK=1;
_nop_();
_nop_();
_nop_();
}//發送結束
SS=1;//上電結束
delayms(50);
SS=0;
MOSI=0;//發送地址
SCLK=0;
for(y=0;y<16;y++)
{
SCLK=0;
if((addr>>y)&0x01)MOSI=1;
else MOSI=0;
_nop_();
_nop_();
_nop_();
SCLK=1;
_nop_();
_nop_();
_nop_();
}//發送地址結束
MOSI=0;//放音
SCLK=0;
for(y=0;y<8;y++)
{
SCLK=0;
if((0xe0>>y)&0x01)MOSI=1; //指定地址放音命令
else MOSI=0;
_nop_();
_nop_();
_nop_();
SCLK=1;
_nop_();
_nop_();
_nop_();
}
SS=1;
SS=0;
MOSI=0;
SCLK=0;
for(y=0;y<8;y++)
{
SCLK=0;
if((0xf0>>y)&0x01)MOSI=1; //忽略地址放音命令(連貫播放后續空間)
else MOSI=0;
_nop_();
_nop_();
_nop_();
SCLK=1;
_nop_();
_nop_();
_nop_();
}
SS=1;
}
///////////////////////////////////////////////////////////////
///////////////////讀數字子函數///////////////////////////////
void speaknum()
{
if(s==1) play(0x01);
if(s==2) play(0x0a);
if(s==3) play(0x14);
if(s==4) play(0x1e);
if(s==5) play(0x28);
if(s==6) play(0x32);
if(s==7) play(0x3c);
if(s==8) play(0x46);
if(s==9) play(0x50);
if(s==0) play(0x6e);
}
////////////////////////////////////////////////////////////////
///////////////////讀出顯示結果子函數////////////////////////////
void read()
{
play(0xdc); //播報“測量距離”這四個字,0xdc是之前記錄下4004錄這四個字的地址
delay2(50000);
delay2(50000); //延時等待播報完畢
s=D/100; //D是測量得到的數據值,此語句是取數據的最高位
if(s==1) //判斷最高位(十位)如果是“1”則不讀yi而讀shi
{
play(0x5a); //shi的地址
delay2(50000);
}
if(s>1) //如果大于1則讀出相應數字后直接在后面加讀shi
{
speaknum();
delay2(50000);
play(0x5a);
delay2(50000);
}
s=D0/10; //取次高位(個位)的值
if(s!=0) //次高位不為0則讀出相應的值,為0跳過
{
speaknum();
delay2(50000);
}
play(0x64); //讀小數點“dian”
delay2(50000);
s=D; //取小數點后一位的值
speaknum();
delay2(50000);
play(0xbe); //播報“cm”limi
delay2(50000);
///////////////////////////以下是播報溫度和當前計算的聲速,方法同上///////////////////
play(0xf0);
delay2(50000);
delay2(50000);
s=TD/100;
if(s==1)
{
play(0x5a);
delay2(50000);
}
if(s>1)
{
speaknum();
delay2(50000);
play(0x5a);
delay2(50000);
}
if(s!=0)
{
s=TD0/10;
speaknum();
delay2(50000);
}
play(0x64);
delay2(50000);
s=TD;
speaknum();
delay2(50000);
play(0xC8);
delay2(50000);
delay2(50000);
play(0xe6);
delay2(50000);
delay2(50000);
s=VD/1000;
speaknum();
delay2(50000);
play(0xfa);
delay2(50000);
s=VD00/100;
speaknum();
delay2(50000);
play(0x5a);
delay2(50000);
s=VD0/10;
speaknum();
delay2(50000);
play(0x64);
delay2(50000);
s=VD;
speaknum();
delay2(50000);
play(0xd2);
}
////////////////////////////////////////////////////////////
void main()
{
initlcd() ; //初始化LCD1602
while(1)
{
timer();
readtemp(); //讀溫度
delay25us_40KHz(15);
display();
if(key1==0) read();
}
}
以下是溫度傳感器頭文件ds18b20.h
sbit DQ=P2^2;
uchar tempdata[2];
uchar k=0;
int TD;
float T;
void delay1(uchar i)
{
while(i--);
}
void initDS18B20() //初始化DS18B20
{
DQ = 1; //DQ復位
delay1(8); //稍做延時
DQ = 0; //單片機將DQ拉低
delay1(80); //延時 大于 480us
DQ = 1; //拉高總線
delay1(30);
}
uchar readchar() //向DS18B20讀取一字節
{
uchar i = 0 ;
uchar dat = 0 ;
for (i = 8 ; i > 0 ; i--)
{
DQ = 0 ;
dat >>= 1 ;
DQ = 1 ;
if(DQ)
dat |= 0x80 ;
delay1(4) ;
}
return (dat) ;
}
void writecmd(uchar cmd) //向DS18B20寫入一字節
{
uchar i ;
for (i = 8 ; i > 0 ; i--)
{
DQ = 0 ;
DQ = cmd&0x01 ;
delay1(5) ;
DQ = 1 ;
cmd>>=1 ;
}
}
void readtemp()
{
initDS18B20() ;
writecmd(0xCC) ; // 跳過讀序號列號的操作
writecmd(0x44) ; // 啟動溫度轉換
initDS18B20() ;
writecmd(0xCC) ; //跳過讀序號列號的操作
writecmd(0xBE) ; //讀取溫度寄存器
tempdata[0] = readchar() ; //溫度低8位
tempdata[1] = readchar() ; //溫度高8位
TD=tempdata[1];
TD<<=8;
TD|=tempdata[0];
T=TD*0.0625; //DS18B20在出廠時以配置為12位,讀取溫度時共讀取16位,最高5位為符號位,溫度在0上,符號位為0,所以把后11位的2進制轉化為10進制后在乘以0.0625便為所測的溫度
TD=T*10+0.5; //將它放大10倍, 使顯示時可顯示小數點后一位, 并對小數點后第二2進行4舍5入
}
一下是液晶顯示頭文件lcd.h
sbit RS=P2^5;
sbit RW=P2^6;
sbit EN=P2^7;
#define uchar unsigned char
#define DATA P0
#define busy 0x80
void chkbusy() //檢測狀態
{
DATA=0xff;
RS=0;
RW=1;
EN=1;
_nop_();
_nop_();
while(busy&DATA);
EN=0;
}
void WIR(uchar CMD,uchar i) //寫命令
{
if(i) chkbusy();
RS=0;
RW=0;
EN=1;
_nop_();
_nop_();
DATA=CMD;
EN=0;
}
void WDR(char c) //寫數據
{
chkbusy();
RS=1;
RW=0;
EN=1;
_nop_();
_nop_();
DATA=c;
EN=0;
}
void displaychar(uchar x,uchar y,char c) //指定行列顯示字符
{
if(y==1) x|=0x40; //當要顯示第二行時地址碼+0x40;
x|=0x80;
WIR(x,0);
WDR(c);
}
void displaystring(uchar x,uchar y,char s[]) //指定行列顯示字符串
{
uchar p=0;
while(1)
{
displaychar(x,y,s[p]);
p++;
if(s[p]==0)
break;
x++;
if(x>=15) //每行最多顯示16個字符
{
x=0;
y=!y; //如果一行顯示不完,則轉到下一行或上一行顯示
}
}
}
void initlcd() //初始化lcd
{
WIR(0x38,0);
WIR(0x38,1); //顯示模式設置
WIR(0x08,1); //關閉顯示
WIR(0x01,1); //清屏
WIR(0x06,1); //光標移位設置
WIR(0x0c,1); //顯示開及光標設置
}
|