超声波测距仿真模拟及原理解析

微处理器课设——超声波测距仿真

  • 超声波测距仿真
  • 设计总体要求
  • 系统的整体设计方案
  • 整体设计方案仿真图
  • 原件表单
  • 程序设计部分
  • 接口定义及变量定义部分
  • LCD控制函数部分
  • LCD1602显示函数部分
  • 超声波控制模块程序部分
  • 上限值和下限值的参数修改函数
  • LCD显示距离函数
  • 检测距离计算并显示函数
  • 距离显示函数
  • 外部中断函数
  • 主函数
  • 延时函数
  • 超声波测距原理解释
  • 最终效果说明
  • 超声波测距仿真

    萌新第一次写博客,有格式不正确的地方还请大家见谅。

    设计总体要求

    1. 用HCSR04超声波传感器测量距离,测量范围0~170cm,精确到小数点后一位。
    2. 用LCD1602或者数码管显示测量到的距离。
    3. 当距离大于120cm时,绿色LED灯亮;
      (1)当距离在50-120cm之间,蜂鸣器间断发声“滴 滴 滴 ”提示,黄色LED灯亮。
      (2)当距离低于50cm时,蜂鸣器持续报警,红色LED灯亮,显示报警信号(如warning)并闪烁。
    4. 临界值默认50和120cm,可以用按键调节临界值,需满足下限值<上限值。

    系统的整体设计方案

      本设计的构建是基于89C51单片机外围芯片的超声信号检测的。超声波发生模块送出片刻的40KHz的矩形波信号,遇物体反射后,被超声波接收模块接收并作为本设计的Input,单片机对此信号进行判断加工处理后,把计算出的距离结果传到LCD显示屏上,当检测距离小于预设值时报警模块工作。
      本设计的硬件部分主要由AT89C51单片机控制模块、超声波发出和接收模块、预警模块以及LCD显示模块等几部分组成。系统的总体结构设计框图在第二部分展示。
      设计的硬件部分主要包括时钟信号子模块,复位电路子模块,参数修改子模块,信号灯子模块,AT89C51单片机,LCD1602液晶显示模块,超声波测距模块以及蜂鸣器模块。系统软件部分主要51主程序处理模块、delay子程序块、超声波发生与感应子程序模块等。

    整体设计方案仿真图

    仿真设计图
      注意,使用protuse8.0以后的版本才能在元件库中找到SRF04超声波测距模块,老版本里面是不含这个模块的,要自己做。并且系统自带的模块只能设置整数的距离值(单位cm)。为了解决精确到小数点后一位的问题,额,需要使用一点小技巧

    原件表单

    2N4403,AT89C52,BUTTON,BUZZER,CAP,CRYSTAL,LED-GREEN,LED-RED,LED-YELLOW,LM016L,MZPY4V3RL,POT-HG,RES,RES10SIPB,RESPACK-8,SRF04

    程序设计部分

      程序实现主要有LCD控制函数LCD1602显示函数超声波控制模块程序部分上限值和下限值的参数修改函数LCD显示距离函数检测距离计算并显示函数距离显示函数外部中断函数主函数延时函数

    接口定义及变量定义部分

    #include <reg51.h>
    #include <intrins.h>
    #include "delay.h"
    #define LCD1602_shuru P0
    //接口定义
    sbit LCD1602_E=P2^2;
    sbit LCD1602_RW=P2^1;
    sbit LCD1602_RS=P2^0;
    sbit RED=P2^7;
    sbit GREEN=P2^5;
    sbit YELLOW=P2^6;
    sbit BUZZER=P3^7;  
    sbit TR=P1^0;
    sbit ECHO=P1^1;
    //参数调整按键接入芯片管脚端
    sbit L_plus=P1^2;  
    sbit L_minus=P1^3;  
    sbit replacement=P1^4;  
    sbit H_plus=P1^5;  
    sbit H_minus=P1^6;  
    
    int flag=0,i;
    int time,distance;
    float distance1;
    int High=120,Low=50;//测量范围
    int bai,shi,ge,xiao;//数码管显示
    unsigned long S=0;
    

    LCD控制函数部分

    此部分包含LCDwritecom函数LCDwritedata函数LCDwritedata函数三个函数,以实现对LCD1602的写入以及初始化的功能。

    void LCDwritecom(char a)   //LCD1602的写入命令
    {
      LCD1602_E=0;     //使能清零
      LCD1602_RS=0;    //选择发送命令
      LCD1602_RW=0;    //选择写入
      LCD1602_shuru=a;     //放入命令
      delay(1);     //等待数据稳定
      LCD1602_E=1;     //写入时序
      delay(5);   //保持时间
      LCD1602_E=0;
    }
    void LCDwritedata(char b)   //LCD1602写入数据
    {
      LCD1602_E=0; //使能清零
      LCD1602_RS=1; //选择输入数据
      LCD1602_RW=0; //选择写入
      LCD1602_shuru=b; //写入数据
      delay(1);
      LCD1602_E=1;   //写入时序
      delay(5);   //保持时间
      LCD1602_E=0;
    }
        
    void LCDwritedata()        //LCD初始化程序
    {
      LCDwritecom(0x38);  //开显示
      LCDwritecom(0x0c);  //开显示不显示光标
      LCDwritecom(0x06);  //写一个指针加1
      LCDwritecom(0x01);  //清屏
      LCDwritecom(0x80);  //设置数据指针起点
    }
    
    

    LCD1602显示函数部分

    LCD1602类似于多位数码管的位选择显示,第Y+1行的第X+1个显示位置显示Data的具体内容

    void display(char X,char Y,char Data)   //LCD1602的便捷显示程序
    {
      Y&=0x1;  //限制X不能大于15,Y不能大于1(LCD1602一共有两行,每行16个显示位置)
      X&=0xF; 
      if(Y) 
       X|=0x40; //当要显示第二行时地址码+0x40;
       X|=0x80; // 算出指令码
      LCDwritecom(X); //这里不检测信号,发送地址码
      LCDwritedata(Data);
    }
    
    

    超声波控制模块程序部分

    控制超声波模块发射超声波

    void  startSRF04()            //超声波传感器SRF04的启动模块
    {
       TR=0;                      //起始应该是低电平
       TR=1;                      //启动一次模块
       for(i=0;i<=20;i++)         //停止10us左右
            _nop_();
       TR=0;
    }
    
    

    上限值和下限值的参数修改函数

    void modification()   
    {
     if (replacement==0)
    	 {
        High=120,Low=50;
       }
    }
    
    

    LCD显示距离函数

    LCD显示,范围为0—170,为了不显示不必要的0,需要对每种情况进行判断

     void display_parameter() 
     {
     //下限处理
     if(Low>=100&&Low<=170)
     {
      display(8,1,(char)(Low/100+'0')); 
      display(9,1,(char)(Low/10%10+'0')); 
      display(10,1,(char)(Low%10+'0')); 
     }
     else if(Low>=10&&Low<=170)
         {
         display(8,1,' ');
         display(9,1,(char)(Low/10%10+'0')); 
            display(10,1,(char)(Low%10+'0'));    
        }
     else if(Low<=170)
     {
      display(8,1,' ');
      display(9,1,' ');
      display(10,1,(char)(Low%10+'0'));   
     }
     //上限处理
     if (High>=100 && High<=170)
     {  
      display(12,1,(char)(High/100+'0')); 
      display(13,1,(char)(High/10%10+'0')); 
      display(14,1,(char)(High%10+'0'));    
     }
     else if(High>=10&&High<=170)
         {
             display(12,1,' ');
             display(13,1,(char)(High/10%10+'0'));
             display(14,1,(char)(High%10+'0'));   
         }
     else if(High<=170)
     {
      display(12,1,' ');
      display(13,1,' ');
      display(14,1,(char)(High%10+'0'));   
     }
     
     display(11,1,'-');
    }
    
    

    检测距离计算并显示函数

    void Ranging_algorithm()   
    {
      startSRF04();
      while(!ECHO);  //当RX为0时,等待
      TR0=1;       //开启计数
      while(ECHO);   //当RX为1时,计数并等待
      TR0=0;    //关闭计数
      time=TH0*256+TL0;
      TH0=0;
      TL0=0;
      distance =(time*1.705)/100;  //算出来是cm
    	distance1 =(time*1.705)/100;
      distance1=distance1+0.05;     //四舍五入
    
    }
    
    

    距离显示函数

    void distance_display()
    {	
    	xiao=((int)(distance1*10))%10; //distance强制类型转换后出现了bad operand type
      bai=distance/100;
      shi=distance/10%10;
      ge=distance%10;
    	//误差修正
    	if(distance>95&&distance<160) xiao=xiao-5;
    		
      if(distance>=100&&distance<=170)  //距离显示(不显示不必要的零)
      {
        display(7,0,(char)(bai+'0')); 
        display(8,0,(char)(shi+'0'));
        display(9,0,(char)(ge+'0'));   
        display(10,0,'.'); 
        display(11,0,(char)(xiao+'0'));  
      }
      else  if(distance>=10&&distance<=170)
       {
          display(7,0,' ');
          display(8,0,(char)(shi+'0')); 
          display(9,0,(char)(ge+'0'));
          display(11,0,(char)(xiao+'0'));  					
       }
      else if(distance<=170)
      {
       display(7,0,' ');
       display(8,0,' ');
       display(9,0,(char)(ge+'0'));
       display(11,0,(char)(xiao+'0')); 		
      }
      if (distance<=170)
      {
       display(5,0,' ');
    	 display(6,0,' ');
       display(10,0,'.');  
       display(13,0,'c');
       display(14,0,'m');
       display(15,0,' ');
      }
      //距离判断
      if(distance<=Low)  
      { 
       display(0,1,'w');  //警告并闪烁
       display(1,1,'a');
       display(2,1,'r');
       display(3,1,'n');
       display(4,1,'i');
       display(5,1,'n');
       display(6,1,'g');
       display(7,1,' ');
       display(8,1,' ');
       display(0,1,' ');  
       display(1,1,' ');
       display(2,1,' ');
       display(3,1,' ');
       display(4,1,' ');
       display(5,1,' ');
       display(6,1,' ');
       display(7,1,' ');
       display(8,1,' ');
       GREEN= 1;
       YELLOW=1;
       RED=0;     //红灯亮  
       BUZZER=0;       //蜂鸣器持续响
      }
      else if(distance>Low && distance<=High)
           {
             YELLOW=0;      //黄灯亮
             GREEN=1;
             RED=1;
             BUZZER=1;
             delay(30);
             BUZZER=0;     //蜂鸣器 嘀~嘀~嘀
             delay(60);
             BUZZER=1; 
             delay(30);
    
           }     
      else if(distance>High && distance<=170)
           {
             GREEN=0;  //绿灯亮
             YELLOW=1;
             RED=1;
             BUZZER=1;  //蜂鸣器不响
           }
      else if((distance>170)||flag==1)  //超出显示范围或者定时器时间溢出
           {  
             flag=0;
             display(5,0,'o'); 
             display(6,0,'u'); 
             display(7,0,'t'); 
             display(8,0,' '); 
             display(9,0,'o'); 
             display(10,0,'f'); 
             display(11,0,' '); 
             display(12,0,'r'); 
             display(13,0,'a'); 
             display(14,0,'n'); 
             display(15,0,'g'); 
    				 GREEN=1;
           }
    }
    
    

    外部中断函数

    经过仿真发现当不使用外部中断时,加减是在是太慢了,内部中断又被用去了一个,不够四个中断源,只能从硬件电路上进行扩展

    void zhongduan1() interrupt 1    //T0中断用来检测计数器溢出,表示超过测距范围,使用掉了内部定时器1
    {
        flag=1;        //中断溢出标志
    }
    void Init1()   //中断初始化
    {
    	//内部定时器0
      TMOD=0x01;     //采用16位计数器模式
      TH0=0;         //高位
      TL0=0;         //低位
      ET0=1;         //中断允许
    	//外部中断0
    	IT0=0;         //外部中断低电平触发
    	EX0=1;         //外部中断有效,分开关开
      EA=1;     
    }
    void Int0()	interrupt 0
    {
       if(L_plus==0)
    	{
    		while(L_plus==0);
    		if(Low<High){
    	    Low++;
    	 }
    	}
    	if(L_minus==0)
    	{
    		while(L_minus==0);
    		if(Low>0) Low--;
    	}
    	if(H_plus==0)
    	{
    		while(H_plus==0);
    		if(High<170) High++;
    	}
    	if(H_minus==0)
    	{
    		while(H_minus==0);
    		if(High>Low)
    			{
           High--;
    	 }
    	}
    }
    
    

    主函数

    void main()
    {
     BUZZER=1;  //初始设置蜂鸣器不响
     Init1();
     LcdInit();
     display(0,0,'D'); 
     display(1,0,'I'); 
     display(2,0,'S'); 
     display(3,0,'T'); 
     display(4,0,':');
     display(0,1,'P');
     display(1,1,'a');
     display(2,1,'r');
     display(3,1,'a');
     display(4,1,'m');
     display(5,1,':');
     
     while(1)
     {
      modification();
      display_parameter();
      Ranging_algorithm();
      distance_display();
     }
    }
    
    

    延时函数

    为了增强函数的可复用性,对延时函数单独进行封装,下面分别是Delay.hDelay.c

    #ifndef __DELAY_H__
    #define __DELAY_H__
    /*------------------------------------------------
     uS延时函数,含有输入参数 unsigned char t,无返回值
     unsigned char 是定义无符号字符变量,其值的范围是
     0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
     长度如下 T=tx2+5 uS 
    ------------------------------------------------*/
    /*------------------------------------------------
     mS延时函数,含有输入参数 unsigned char t,无返回值
     unsigned char 是定义无符号字符变量,其值的范围是
     0~255 这里使用晶振12M,精确延时请使用汇编
    ------------------------------------------------*/
    void delay(unsigned int xms);  //延时函数
    
    #endif
    
    
    void Delay(unsigned int xms)		//@12.000MHz
    {
    	unsigned char i, j;
      while(xms--){
    	i = 2;
    	j = 239;
    	do
    	{
    		while (--j);
    	} while (--i);
    	
    	}
    }
    
    

    超声波测距原理解释

      超声波模块采用现成的HC-SRF04超声波模块,该模块可提供2cm-400cm 的非接触式距离感测功能,测距精度可达高到3mm。模块包括超声波发射器、接收器与控制电路。基本工作原理:采用IO口TRIG 触发测距,给至少10us的高电平信号;模块自动发送8个 40khz 的方波,自动检测是否有信号返回;有信号返回,通过 IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(3.40cm/us))/2。在第二部分的原理图中,VCC供5V电源,GND 为地线,TRIG 触发控制信号输入,ECHO 回响信号输出等四支线。

      超声波探测模块HC-SR04的使用方法如下:IO口触发,给Trig口至少10us的高电平,启动测量;模块自动发送8个40Khz的方波,自动检测是否有信号返回;有信号返回,通过IO口Echo输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间,测试距离=(高电平时间*340)/ 2,单位为m。程序中测试功能主要由两个函数完成。

      实现中采用定时器0进行定时测量,8分频,TCNTT0预设值0XCE,当timer_0溢出中断发生2500次时为125ms,计算公式为(单位:ms):T =(定时器0溢出次数 * (0XFF – 0XCE))/ 1000 ,其中定时器0初值计算依据分频不同而有差异。

    最终效果说明

      首先LCD1602初始化和实现便捷显示,传感器启动并开始测距,输出信号由单片机接收并处理转换为测量距离数值,然后先查询上下限值修改按键是否按下,如果被按下,则相应修改上下限值,反之则保持上下限值,再是测量距离数值与上下限值以及测量范围的比较,如果在测量范围内,大于上限值,则控制绿灯亮,黄红两灯不亮,LCD1602显示当前测量距离和上下限值以及normal(正常提示),开关三极管断开,蜂鸣器不工作,无声音;
      大于下限值小于上限值,则控制黄灯亮,绿红两灯不亮,LCD1602显示当前测量距离和上下限值以及cautions(小心警示),开关三极管间断打开,蜂鸣器间断工作,从而实现发出滴 滴 滴~的声音;小于下限值,则控制红灯亮,黄绿两灯不亮,LCD1602显示当前测量距离和上下限值,同时warning显示又消显实现闪烁警报,开关三极管一直打开,蜂鸣器持续工作,一直发声警报。反之若超出测量范围,LCD1602显示距离位置显示out of rang(超出范围),提示显示位置显示error(错误)。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 超声波测距仿真模拟及原理解析

    发表评论