RYMCU

Nebula Pi 开发板进阶教程(二)——超声波测距模块HC-SR04的使用

ii11nnocent 2 年前
# hc-sr04 # 超声波测距模块 # 定时器 # echo

HC-SR04 是一块超声波测距模块,包含了超声波发射器、接收器与控制电路,实物图如下图所示。
HCSR04.jpg
该模块能在 2cm~400cm 的距离内进行测距,精度可达 3mm。模块共有 4 个引脚,其中 VCC 引脚接 +5V 电源,GND 引脚接地线,Trig 引脚是脉冲触发引脚,Echo 引脚是回响信号引脚,其时序图如下图所示。
HCSR04 时序图.png

当单片机发送给脉冲触发引脚 20us 以上的高电平信号时,模块开始工作并连续发出 8 个 40KHz 的脉冲信号,当脉冲信号发送完毕后回波引脚 Echo 的电平会由 0 变成 1。当超声波遇到障碍物返回模块时,此时测距结束,回波引脚 Echo 的电平会由 1 变为 0。
假设从发射超声波到接收到回波信号的时间(Echo 引脚高电平的时间)为 t,声速为 v,那么超声波测距模块到障碍物之间的距离为 vt/2。
也就是说,超声波模块测距的实质就是测量超声波从发出到返回的时间,即 Echo 引脚高电平的时间。具体的测定方法如下:
当 Echo 引脚的电平值由低变为高时启动定时器,然后等待回波信号;当有回波信号产生时,Echo 引脚的电平值会由高变为低,此时关闭定时器,然后计算定时器的定时时间。
由于超声波测距模块的最大测试距离为 4m,声波的速度在 25℃ 时是 346m/s,因此 Echo 引脚处于高电平的最大时间约为 23ms。设 Echo 引脚的高电平时间为 tms,单片机的定时器每过 1ms 产生一次中断,那么 t 的值就由两部分组成:整数部分为单片机进入定时器中断的总次数,小数部分是定时器最后一次重置初值后所经过的时间。例如 t=5.5,那么此时单片机进入了 5 次定时器中断,每次经过的时间是 1ms,在第 5 次进入中断后,定时器又经过 0.5ms,然后 Echo 引脚变为低电平,这个时间无法通过定时器中断服务函数获取,可以通过定时器 TH0 和 TL0 的值来获取。具体的程序代码如下(lcd.c 和 lcd.h 中代码和前面章节一样,不再重复粘贴,仅附上超声波驱动和主函数代码):
ultrasonic.h:

#ifndef _ULTRASONIC_H  
#define _ULTRASONIC_H  
  
#include "reg52.h"  
#include "intrins.h"        //_nop_()指令头文件  
  
sbit Trig = P3^2;               //HC-SR04模块Trig引脚,触发信号的输入端  
sbit Echo = P3^3;               //HC-SR04模块Echo引脚,回响信号的输出端  
  
/****下面两个变量用关键字extern进行声明,使其能够在其他c文件中被调用****/  
extern unsigned char Count;     //中断累加变量,用来计算进入定时器中断的次数
extern long int Distance;       //超声波模块最终所测距离
void    delay_20us(void);                 
void    Measuring_Init(void);  
void    Timer0_Init(void);  
void    Trigger(void);  
void    Measuring(void);  
#endif

ultrasonic.c:

#include    "ultrasonic.h"  
#include		"lcd.h"
/*在h文件中用extern关键字声明的变量如果需要使用,需要再定义一次,否则报错*/  
unsigned char Count;          
long int Distance;            
  
void    delay_20us(void)                //延时20us函数  
{  
    _nop_();_nop_();_nop_();_nop_();_nop_();  
    _nop_();_nop_();_nop_();_nop_();_nop_();  
    _nop_();_nop_();_nop_();_nop_();_nop_();  
    _nop_();_nop_();_nop_();_nop_();_nop_();        
    //一条_nop_()指令精确延时1us,20条延时20us  
}  
  
void    Measuring_Init(void)        //超声波模块初始化函数  
{  
    Trig = 0;                       //Trig引脚拉低  
    Echo = 0;                       //Echo引脚拉低  
    Count = 0;                      //Count清零  
}  
  
void    Timer0_Init(void)  
{  
    TMOD = 0x01;                    //定时器工作在方式1  
    TH0 = 0xFC;                       
    TL0 = 0x66;                     //1ms定时的定时器初值  
    ET0 = 1;                        //打开定时器0中断允许  
    EA = 1;                         //打开CPU总中断允许  
}  
  
void    Trigger(void)//模块的启动函数,即单片机发送给Trig引脚20us的高电平信号
{  
    Trig = 1;        //给Trig引脚发送高电平信号  
    delay_20us();    //延时20us
    Trig = 0;        //将Trig引脚拉低  
}  
  
/*测距函数,通过定时器计算Echo高电平的时间,再乘以声速,除以2,即为测距结果*/
void    Measuring(void)               
{  
    unsigned char L;     //最后一次不到1ms定时中,时间的低8位     
    unsigned int  H;     //最后一次不到1ms定时中,时间的高8位          
    unsigned int uTime;  //小于1ms时间的存储变量,最大值<0xFFFF,因此用整型变量
    long int Time;        //总时间的存储变量,单位是us,因此采用长整型变量  
      
    TR0 = 1;                        //开启定时器,开始计时  
    while(Echo == 1);               //当超声波在传输时,Echo一直处于高电平,此时等待回波;当有回波产生时,Echo由高电平变为低电平  
    TR0 = 0;                        //Echo由高电平变为低电平时,接收到了返回的超声波,此时关闭定时器,开始计算时间  
      
    H = TH0;                        //关闭定时器后,TH0和TL0会停止增加,此时(TH0+TL0)-0xFC66即为本次定时的时间  
    L = TL0;      
    uTime = (H<<8) + L;             //H为时间的高8位,因此将H左移8位后与L相加,最后的结果即为(TH0+TL0)  
    uTime = uTime - 0xFC66;         //得到最后一次小于1ms的时间,单位是us     
      
    /****下面根据时间开始计算所测距离****/  
    Time = uTime + 1000*Count;  //总时间=最后一次小于1ms的时间+定时器的进入次数(1次代表1ms)*1000,单位是us  
      
    TH0 = 0xFC;  
    TL0 = 0x66;                //计算完总时间后,给定时器赋初值,便于下次计算  
    delay_20us();  
    /***********************************************************************
    2*Distance=超声波走过的距离,因此Distance = 超声波走过的距离/2 
    超声波走过的距离 = 声速*时间,25℃时,声音在空气中的传播速度为346m/s,即346000mm/s,所以DiStance=(346000*Time)/(2*1000000)=346*Time/2000 
    ***********************************************************************/    Distance = (346*Time)/2000; //显示单位换算:如果最终测量距离为2.125m,那么Distance=2125,即单位为mm  
}

main.c:

#include    "reg52.h"  
#include    "string.h"  
#include    "ultrasonic.h"  
#include    "lcd.h"  
  
unsigned char mm,lm,fm,m;   //定义最后显示距离中米、分米、厘米、毫米的存储变量  
  
void main(void)  
{  
    LCD_Init();             //LCD初始化  
    Timer0_Init();          //定时器0初始化  
    Measuring_Init();       //HC-SR04模块初始化  
      
    while(1)  
    {         
        Trigger(); //发送20us高电平信号,Trig引脚接受到高电平信号后,模块会发送8个脉冲   
        while( !Echo  );//脉冲发送完毕后,待Echo引脚由低电平变为高电平时,开始测量距离  
        Measuring();    //测距  
          
        /****将显示距离中米、分米、厘米、毫米分别取出来****/  
        mm = (Distance%10) + '0';  
        lm = (Distance/10)%10 + '0';  
        fm = (Distance/100)%10 + '0';  
        m  = (Distance/1000) + '0';  
          
        /****显示被测距离****/  
        LCD_WriteString(1,1,"Dis:");  
        LCD_WriteString(1,6,&m);  
        LCD_WriteString(1,7,".");  
        LCD_WriteString(1,8,&fm);  
        LCD_WriteString(1,9,&lm);  
        LCD_WriteString(1,10,&mm);  
        LCD_WriteString(1,11,"m");        
          
        Measuring_Init();   //重新初始化模块,为的是进行下一次测量  
        delay(50000);       //延时一下,防止两次测量间隔太短造成结果出错  
    }  
}  
  
void  Timer0() interrupt 1          //定时器0中断函数  
{  
    TH0 = 0xFC;  
    TL0 = 0x66;                     //重新赋初值  
    Count++;                        //每一次溢出,count就+1,用来计算总时间  
    if(Count == 23)  
    {  
        TR0 = 0;      //计数到23的时候,时间为23ms,超过最大测量距离,进行清零,关闭定时器
        TL0 = 0x66;  
        TH0 = 0xFC;  //重新赋初值,等待下一次测距 
        Count = 0;  
    }    
}

将上述程序烧写进单片机中,启动单片机,就可以在 LCD1602 屏幕上看到超声波模块的测距信息。
超声波测距视频.mp4

后发布评论