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