专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

实现超声波测距并语音播报

作者:佚名   来源:本站原创   点击数:  更新时间:2013年11月14日   【字体:

基于AT89S52的超声波测距实验
更多内容欢迎访问我的MCU技术记录博客共同讨论学习
(地址:http://blog.sina.com.cn/aftermind


超声波测距原理:单片机IO口接超声波模块TX引脚,由IO口产生40KHz频率方波发送给TX用以超声波模块发送超声波,同时打开单片机计时器,超声波遇到障碍后返回,模块接收头接收到超声波后产生信号由电路滤波整形放大后在RX引脚输出高电平,RX接单片机的P3^2(51内核单片机的外部中断),在中断服务程序中停止计数器,对所计数值进行处理得到超声波往返所用时间再乘以风速即得测量距离。由于超声波属于声波范围,其速度与温度有关,不同温度下超声波在空气中传播速度随温度变化关系:V=331.4+0.61T。所以要是测量结果更加精确需另加温度补偿模块(本实验中采用开发板上的DS18B20温度传感器完成温度补偿)。外围可添加LCD显示(本实验采用开发板1602LCD)和语音播报模块(本实验采用ISD4004语音模块)。


源程序
// *********************单片机实验室******************************/
// *文件名称 :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);
   delay2(50000);
   delay2(50000);
   s=D/100;
   if(s==1)
   {
    play(0x5a);
    delay2(50000);
   }
   if(s>1)
   {
    speaknum();
    delay2(50000);
    play(0x5a);
    delay2(50000);
   }
   s=D0/10;
   if(s!=0)
   {
    speaknum();
    delay2(50000);
   }
   play(0x64);
   delay2(50000);
   s=D;
   speaknum();
   delay2(50000);
   play(0xbe);
   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);  //显示开及光标设置
}

关闭窗口

相关文章