超声波测距模块HC-SR04详解(基于51单片机)

本篇文章是个人整理的包含超声波测距模块HC-SR04的基本介绍与基本工作原理以及分别通过LCD1602、数码管和串口显示距离的实例讲解与代码的笔记,部分内容来自《HC-SR04超声波测距模块说明书》,代码使用模块化编辑,部分模块来自江科大自化协的51单片机教学视频。
希望大家早日掌握单片机。

文章目录

  • 一、基本介绍
  • 二、基本工作原理
  • 三、时序图
  • 四、实例
  • (1)最终效果
  • (2)思路
  • Ⅰ、LCD1602显示
  • Ⅱ、数码管显示
  • Ⅲ、串行通信显示
  • (3)代码实现
  • Ⅰ、LCD1602显示
  • ①HCSR04.c
  • ②Timer0.c
  • ③main.c
  • Ⅱ、数码管显示
  • ①Nixie.c
  • ②Timer1.c
  • ③HCSR04.c
  • ④main.c
  • Ⅲ、串行通信显示
  • ①HCSR04.c
  • ②UART.c
  • ③main.c
  • 一、基本介绍

    ​ 超声波测距模块是根据超声波遇障碍反射的原理进行测距的,能够发送超声波、接收超声波并通过处理,输出一段和发送与接收间隔时间相同的高电平信号,是常用的测距模块之一。HC-SR04是最常用的超声波测距模块之一,HC-SR04超声波模块可提供2cm~400cm的非接触式距离感测功能,测距精度可达3mm,工作电压为5V;内部模块包括超声波发射器、接收器与控制电路。如下为实物与对应端口图:

  • Vcc:+5V电源供电;
  • Trig:输入触发信号(可以触发测距);
  • Echo:传出信号回响(可以传回时间差);
  • GND:接地。
  • 二、基本工作原理

    (1)采用I/O口连接Trig触发测距,给最少10us的高电平后即可发送超声波;

    (2)模块自动发送8个40kHz的方波,并自动检测是否有信号返回;

    (3)若有信号返回,经内部电路处理后,通过Echo到I/O口输入一个高电平,高电平持续的时间就是超声波从发射到返回的时间

    (4)测试距离=(高电平时间*音速)/2;音速=340m/s=0.034cm/us

    三、时序图

    ​ 以上时序图表明你只需要向模块提供一个10us以上的脉冲触发信号,然后该模块内部将发出8个40kHz周期电平并检测回波,一旦检测到有回波信号,模块就向I/O口输出回响信号。回响信号的脉冲宽度与所测的距离成正比,由此通过回响信号的高电平时间计算得到距离。建议先将单位转换为cm/us,便于数据显示;测量周期为60ms以上,以防止发射信号对回响信号的影响。

  • 此模块不宜带电连接,若要带电连接,则先让模块的GND端先连接,否则会影响模块的正常工作;
  • 测距时,被测物体的面积不少于0.5平方米且平面尽量要求平整,否则会影响测量的结果。
  • 四、实例

    (1)最终效果

  • 实现超声波测距,并将数据分别显示在LCD1602、数码管和串行通信上。
  • (2)思路

    ​ 先将端口初始化(置0),再通过Trig输入一个12us的高电平作为触发信号,最后接收回响信号,回响信号高电平的时间通过定时器0测量——当Echo为1时开始计时,Echo为0时结束计时,不需要打开中断。得到时间后根据公式:测试距离=(高电平时间*音速)/2;音速=340m/s=0.034cm/us,计算出实际距离。

    Ⅰ、LCD1602显示

    ​ 调用LCD1602函数进行显示:

  • 注意:连接LCD1602的端口包括P0和P2,连接超声波模块时注意引脚不能冲突
  • Ⅱ、数码管显示

    ​ 调用数码管函数进行显示,使用定时器扫描数码管。

    Ⅲ、串行通信显示

  • 打开定时器1,配置串行通信,波特率为9600;
  • 将距离数据通过串口发送到电脑,将发送中断请求标志位TI置1,使计算机不断接收数据;
  • 调用stdio.h中的printf函数可以通过串口在电脑上打印数据。
  • (3)代码实现

    Ⅰ、LCD1602显示

    ①HCSR04.c
    #include <REGX52.H>
    #include "Timer0.h"
    
    //定义引脚接口
    sbit Trig = P1^0;
    sbit Echo = P1^1;
    
    /**
      * @brief  HC-SR04初始化
      * @param  无
      * @retval 无
      */
    void HCSR04_Init()
    {
    	Timer0_Init();
    	Trig = 0;
    	Echo = 0;
    }
    
    /**
      * @brief  HC-SR04接收一个12us的触发信号
      * @param  无
      * @retval 无
      */
    void HCSR04_Trig()
    {
    	unsigned char i;
    	Trig = 1;
    	i = 5;while (--i);		//延时12us
    	Trig = 0;
    }
    
    /**
      * @brief  HC-SR04接收触发信号并计算回波信号高电平时间
      * @param  无
      * @retval 无
      */
    void HCSR04_Set()
    {
    	HCSR04_Trig();
    	while(!Echo);
    	TR0 = 1;
    	while(Echo);
    	TR0 = 0;
    }
    
    /**
      * @brief  HC-SR04计算模块与障碍间的距离
      * @param  Duration(持续时间)	发送到接收的间隔时间
      * @param  Distance  模块与障碍间的距离
      * @retval Distance
      */
    float HCSR04_Calc()
    {
    	unsigned int Duration = 0;
    	float Distance = 0;
    	
    	Duration = TH0*256+TL0;
    	Distance = Duration*0.017;//时间*音速/2,音速=340m/s=0.034cm/us
    	TH0 = 0;
    	TL0 = 0;
    	
    	return Distance;
    }
    
    ②Timer0.c
    #include <REGX52.H>
    
    /**
      * @brief  定时器0初始化@11.0592MHz
      * @param  无
      * @retval 无
      */
    void Timer0_Init()      //@11.0592MHz
    {
    	TMOD &= 0xF0;		//设置定时器模式
    	TMOD |= 0x01;		//设置定时器模式
    	TH0 = 0;			//设置定时初值
    	TL0 = 0;			//设置定时初值
    	TF0 = 0;			//清除TF0标志
    	TR0 = 0;			//定时器0不计时
    }
    
    
    ③main.c
    #include <REGX52.H>
    #include "Delay.h"
    #include "HCSR04.h"
    #include "LCD1602.h"
    
    unsigned int Distance;
    
    void main()
    {
    	LCD_Init();						//LCD1602初始化
    	HCSR04_Init();					//HC-SR04初始化
    	LCD_ShowString(1,1,"Distance:");
    	LCD_ShowString(2,4,"cm");
    	while(1)
    	{
    		HCSR04_Set();				//HC-SR04接收触发信号并计算回波信号高电平时间
    		Distance = HCSR04_Calc();	//计算距离
    		LCD_ShowNum(2,1,Distance,3);//调用显示
    		Delay(20);
    	}
    }
    

    Ⅱ、数码管显示

    ①Nixie.c
    #include <REGX52.H>
    #include "Delay.h"
    
    //数码管显示缓存区
    unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};
    
    //数码管段码表
    unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
    
    /**
      * @brief  设置显示缓存区
      * @param  Location 要设置的位置,范围:1~8
      * @param  Number 要设置的数字,范围:段码表索引范围
      * @retval 无
      */
    void Nixie_SetBuf(unsigned char Location,Number)
    {
    	Nixie_Buf[Location]=Number;
    }
    
    /**
      * @brief  数码管扫描显示
      * @param  Location 要显示的位置,范围:1~8
      * @param  Number 要显示的数字,范围:段码表索引范围
      * @retval 无
      */
    void Nixie_Scan(unsigned char Location,Number)
    {
    	P0=0x00;				//段码清0,消影
    	switch(Location)		//位码输出
    	{
    		case 1:P2_4=1;P2_3=1;P2_2=1;break;
    		case 2:P2_4=1;P2_3=1;P2_2=0;break;
    		case 3:P2_4=1;P2_3=0;P2_2=1;break;
    		case 4:P2_4=1;P2_3=0;P2_2=0;break;
    		case 5:P2_4=0;P2_3=1;P2_2=1;break;
    		case 6:P2_4=0;P2_3=1;P2_2=0;break;
    		case 7:P2_4=0;P2_3=0;P2_2=1;break;
    		case 8:P2_4=0;P2_3=0;P2_2=0;break;
    	}
    	P0=NixieTable[Number];	//段码输出
    }
    
    /**
      * @brief  数码管驱动函数,在中断中调用
      * @param  无
      * @retval 无
      */
    void Nixie_Loop(void)
    {
    	static unsigned char i=1;
    	Nixie_Scan(i,Nixie_Buf[i]);
    	i++;
    	if(i>=9){i=1;}
    }
    
  • 使用定时器扫描数码管的好处是,在函数循环中调用数码管显示时,不会受到延时函数的影响导致数码管动态显示延时时间延长。
  • ②Timer1.c
    #include <REGX52.H>
    
    /**
      * @brief  定时器1初始化,1ms@11.0592MHz
      * @param  无
      * @retval 无
      */
    void Timer1_Init(void)
    {
    	TMOD &= 0x0F;		//设置定时器模式
    	TMOD |= 0x10;		//设置定时器模式
    	TL1 = 0x65;		//设置定时初值
    	TH1 = 0xFC;		//设置定时初值
    	TF1 = 0;		//清除TF1标志
    	TR1 = 1;		//定时器1开始计时
    	ET1=1;
    	EA=1;
    	PT1=0;
    }
    
    /*定时器中断函数模板
    void Timer1_Routine() interrupt 3
    {
    	static unsigned int T1Count;
    	TL1 = 0x65;		//设置定时初值
    	TH1 = 0xFC;		//设置定时初值
    	T1Count++;
    	if(T1Count>=1000)
    	{
    		T1Count=0;
    		
    	}
    }
    */
    
    
    ③HCSR04.c
    #include <REGX52.H>
    #include "Timer0.h"
    
    //定义引脚接口
    sbit Trig = P1^0;
    sbit Echo = P1^1;
    
    /**
      * @brief  HC-SR04初始化
      * @param  无
      * @retval 无
      */
    void HCSR04_Init()
    {
    	Timer0_Init();
    	Trig = 0;
    	Echo = 0;
    }
    
    /**
      * @brief  HC-SR04接收一个12us的触发信号
      * @param  无
      * @retval 无
      */
    void HCSR04_Trig()
    {
    	unsigned char i;
    	Trig = 1;
    	i = 5;while (--i);		//延时12us
    	Trig = 0;
    }
    
    /**
      * @brief  HC-SR04接收触发信号并计算回波信号高电平时间
      * @param  无
      * @retval 无
      */
    void HCSR04_Set()
    {
    	HCSR04_Trig();
    	while(!Echo);
    	TR0 = 1;
    	while(Echo);
    	TR0 = 0;
    }
    
    /**
      * @brief  HC-SR04计算模块与障碍间的距离
      * @param  Duration(持续时间)	发送到接收的间隔时间
      * @param  Distance  模块与障碍间的距离
      * @retval Distance
      */
    unsigned int HCSR04_Calc()
    {
    	unsigned int Distance,Duration;
    	
    	Duration = TH0*256+TL0;
    	Distance = Duration*0.017;//时间*音速/2,音速=340m/s=0.034cm/us
    	TH0 = 0;
    	TL0 = 0;
    	
    	return Distance;
    }
    
    ④main.c
    #include <REGX52.H>
    #include "Delay.h"
    #include "HCSR04.h"
    #include "Nixie.h"
    #include "Timer1.h"
    
    unsigned int Distance;
    
    void main()
    {
    	HCSR04_Init();						//HC-SR04初始化
    	Timer1_Init();						//定时器1初始化
    	while(1)
    	{
    		HCSR04_Set();					//HC-SR04接收触发信号并计算回波信号高电平时间
    		Distance = HCSR04_Calc();		//计算距离
    		Nixie_SetBuf(1,Distance/100);
    		Nixie_SetBuf(2,Distance%100/10);
    		Nixie_SetBuf(3,Distance%100%10);//调用显示
    		Delay(50);
    	}
    }
    
    void Timer1_Routine() interrupt 3
    {
    	static unsigned int T1Count1;
    	TL1 = 0x65;		//设置定时初值
    	TH1 = 0xFC;		//设置定时初值
    	T1Count1++;
    	if(T1Count1>=2)
    	{
    		T1Count1=0;
    		Nixie_Loop();//2ms调用一次数码管驱动函数
    	}
    }
    

    Ⅲ、串行通信显示

    ①HCSR04.c
    #include <REGX52.H>
    #include "Timer0.h"
    
    //定义引脚接口
    sbit Trig = P1^0;
    sbit Echo = P1^1;
    
    /**
      * @brief  HC-SR04初始化
      * @param  无
      * @retval 无
      */
    void HCSR04_Init()
    {
    	Timer0_Init();
    	Trig = 0;
    	Echo = 0;
    }
    
    /**
      * @brief  HC-SR04接收一个12us的触发信号
      * @param  无
      * @retval 无
      */
    void HCSR04_Trig()
    {
    	unsigned char i;
    	Trig = 1;
    	i = 5;while (--i);		//延时12us
    	Trig = 0;
    }
    
    /**
      * @brief  HC-SR04接收触发信号并计算回波信号高电平时间
      * @param  无
      * @retval 无
      */
    void HCSR04_Set()
    {
    	HCSR04_Trig();
    	while(!Echo);
    	TR0 = 1;
    	while(Echo);
    	TR0 = 0;
    }
    
    /**
      * @brief  HC-SR04计算模块与障碍间的距离
      * @param  Duration(持续时间)	发送到接收的间隔时间
      * @param  Distance  模块与障碍间的距离
      * @retval Distance
      */
    float HCSR04_Calc()
    {
    	unsigned int Duration = 0;
    	float Distance = 0;
    	
    	Duration = TH0*256+TL0;
    	Distance = Duration*0.017;//时间*音速/2,音速=340m/s=0.034cm/us
    	TH0 = 0;
    	TL0 = 0;
    	
    	return Distance;
    }
    
    ②UART.c
    #include <REGX52.H>
    
    /**
      * @brief  串口初始化,9600bps@12.000MHz
      * @param  无
      * @retval 无
      */
    void UART_Init()
    {
    	//设定定时器0和定时器1的工作模式
    	TMOD = 0x21;	
    	//设置定时器0用于计时
    	TH0 = 0;
    	TL0 = 0;		//设定定时器0重装初值
    	TF0 = 0;		//清除TF1标志
    	TR0 = 0;		//停止定时器0
    	
    	//设置定时器1用于串口波特率
    	SCON = 0x40;	//设定串行口工作方式1
    	PCON = 0x00;	//波特率不翻倍
    	TH1 = 0xFD;		
    	TL1 = 0xFD;		//设定定时器1重装初值,波特率为9600
    	ET1 = 0;		//禁止定时器1中断
    	TR1 = 1;		//启动定时器1
    	TI = 1;			//将发送中断请求标志位置1,使计算机不断接收数据
    }
    
    ③main.c
    #include <REGX52.H>
    #include <stdio.h>
    #include "Delay.h"
    #include "HCSR04.h"
    #include "UART.h"
      
    float Distance;
     
    void main()
    {
    	UART_Init();				  //串行口初始化
    	while(1)
    	{
    		HCSR04_Set();			  //HC-SR04接收触发信号并计算回波信号高电平时间
    		Distance = HCSR04_Calc(); //计算模块与障碍间的距离
    		printf("--.---cm\n"); 	  //在串口上打印分割线
    		printf("Distance=%.3f cm\n",Distance); //在串行口上打印距离数据
    		Delay(200);				  //延时200ms
    	}
    }
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 超声波测距模块HC-SR04详解(基于51单片机)

    发表评论