文章下方附学习资源 请自主领取
世界上最遥远的距离是我在你身边,你却在低头玩手机!
小学有一篇语文《雷达与蝙蝠》,介绍科学家如何通过实验解开蝙蝠在夜晚高速飞行并能灵活避开障碍物和捕猎的秘密。它的喉头发出一种超过人耳听阈的高频声波,这种声波沿着直线传播,一旦碰到物体就迅速返回来,它们用耳朵接收了这种返回来的超声波,使它们能够做出准确的判断,引导它们飞行[1]。今天,超声波应用广泛。医院的超声波B超,汽车超声波倒车雷达,超声波测液位,超声波测高仪,智能小车的超声波避障,停车场的停车位车辆检测等等。
读到这里,猜到今天的主题了吧?超声波测距。下面以HC-SR04超声波模块为例,介绍51单片机定时器在超声波测距中的应用。超声波测距是通过测量时间,间接测量距离的。关于定时器测量时间的方法,请阅读“C51编程入门(十九)定时器编程-测量时间”。
一、HC-SR04超声波模块
HC-SR04超声波测距模块集成了超声波发射器、超声波接收器与控制电路,可提供2cm~400cm的非接触式距离感测功能,测距精度高达3mm。
图1为HC-SR04超声波模块的外观图。T为超声波发射器,R为超声波接收器。超声波模块的接口引脚有4个,分别是VCC、Trig、echo和Gnd。引脚功能说明见表1。
图1 HC-SR04超声波模块
表1 HC-SR04 引脚
引脚 |
方向 |
功能 |
VCC |
输入 |
模块供电电源,5V |
Trig |
输入 |
触发控制引脚,输入10us以上高电平信号,触发一次测距过程 |
Echo |
输出 |
检测到回波后,通过echo输出高电平,高电平持续时间表示超声波从发生到返回的时间。由此时间可以根据声速、传输时间和距离的关系公式算出距离。 |
Gnd |
- |
电源地 |
工作原理:
超声波模块的工作原理示意图如图2所示。超声波模块通过Tx端发射超声波信号,超声波信号沿着直线传播,遇到物体后反射,反射的超声波信号被Rx探头接收。由Tx发射信号到Rx接收回波信号的时间间隔,可以算出超声波模块与物体之间的距离。设发射信号与反射信号的夹角为A,则 距离 =(超声波传输的路径长度/2)×cos(A/2)。当夹角A比较小时,距离≈(超声波传输的路径长度/2)。
图2 超声波模块工作原理示意图
超声波模块的时序图如图3所示。
图3 超声波模块工作时序图
由图3我们可以知道:
- 通过Trig引脚触发测距,输入至少10us的高电平信号;
- 模块自动发送8个40kHz的方波,自动检测是否有回波信号返回。
- 当检测到回波信号,通过Echo引脚输出一个正脉冲,高电平持续的时间即超声波从发射到返回的时间。则障碍物到超声波的距离为:
L = echo高电平持续时间 × 声速/2 (1)
声速与气压、温度相关。声速不是一个固定的值。在干燥空气中,声速的经验公式是:
V=331.3 (0.606×c) m/s (2)
式(2)中,c为摄氏温度。温度越高,声速越快。空气中的声速在1个标准大气压和15℃的条件下约为340米/秒。25℃时声速约为346m/s,35℃时声速约为352m/s。
二、电路接口设计
根据表1列出的超声波模块引脚,51单片机要用两个I/O连接超声波模块,一个I/O用于输出高电平到Trig引脚触发一次测距,一个I/O用于监测Echo引脚的电平。因此,单片机与超声波的模块接线图如图4所示。本例中用三位数码管显示测量的距离(单位cm),关于数码管电路请阅读 C51编程入门(十一)数码管显示(上)——显示十进制整数。
图4 51单片机与超声波模块接口电路
三、软件设计
下面的例子用定时器T0测量Echo的高电平时间来测量距离,并显示在3位数码管上。
根据图3所示的超声波模块工作时序图,单次测距的思路如下:
- 初始化定时器T0为定时模式,工作方式1(16位计数器),初值为0;
2. P1.6引脚输出约大于10us的高电平给超声波的Echo引脚,超声波模块发射超声波信号;
- 测量超声波由发射到接收到回波的时间间隔。监控P1.7引脚,当Echo由低变高时,启动定时器; 当Echo由高变低时停止定时器。这时,定时器T0计数值time_duration就是时间间隔,单位为us。(设晶振频率为12MHz)。
- 利用公式计算声速、距离与时间的关系计算出距离。
L = echo高电平持续时间 × 声速/2
取声速为340m/s,时间单位为us,L为cm,则上式变为:
L = (time_duration/10^6) × 340*100cm/s
= 17×time_duration/1000 cm
测距代码封装到一个函数里Testing,距离结果存放在一个unsigend int 全局变量里。注意,
Testing里没有做超时判断,即当超声波模块没有收到回波(障碍物太远),程序会卡死在 while(Echo_pin ==0); 这条语句。
void Testing() //开始测量
{
//输出10us高电平到Trig引脚
Trig_pin = 1;
led_obj = 1; //指示灯off
_nop_();_nop_();_nop_();_nop_();_nop_(); //延时5us
_nop_();_nop_();_nop_();_nop_();_nop_(); //延时5us
_nop_();_nop_();_nop_();_nop_();_nop_(); //延时5us
Trig_pin = 0; //低电平
nCount = 0; //清零
time_duration = 0; //清零
TH0=0;TL0 =0;//清零计数器
//检测Echo
while(Echo_pin ==0); //等待Echo输出高电平
TR0 = 1; //启动定时器
while(Echo_pin ==1); //等待Echo变为低电平
TR0 = 0; //停止定时器
led_obj = 0; //指示灯on
//计算Echo持续的高电平时间,单位:us
//time_duration = TH0 * 256 TL0;
time_duration = nCount * 65536 TH0 * 256 TL0;
// 计算距离,单位为cm,假设声速为340m/s
// 公式:L = 340*t/2 = 170*t //单位m
// 由于定时器time_duration为us,则:
// L = (170*tus/(10^6)) * 100 = = 17*tus/1000 ,单位cm
time_duration = time_duration*17;
time_duration = time_duration/1000;
distance = time_duration; //变成距离
}
嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。
点击这里找小助理0元领取:嵌入式物联网学习资料(头条)
获得距离后,就可以显示。距离显示函数定义如下。三位数字显示一次需要3ms左右。
/***************************
*函数名:displayDistance()
*功能:显示测距结果,单位:cm(使用四位BCD数码管) P2和P3口
*参数:要显示的四位数
*返回值:无
***************************/
void displayDistance()
{
unsigned char i;
uchar num[3];
if(distance>400)
{
distance = 888; //超限则显示888
}
num[0] = distance/100; //百位
num[1] = (distance0)/10; //十位
num[2] = distance; //个位
for(i=0;i<3;i )
{
displayNUM(i,num[i]);
delayMS(1);
}
}
完整代码:
#include "reg52.h"
#include "intrins.h" //_nop_()的头文件
#include "7seg.h"
#define uchar unsigned char
#define uint unsigned int
//HC-SR04引脚连接
sbit Trig_pin = P1^6; // Trig--P1.6
sbit Echo_pin = P1^7; // Echo--P1.7
sbit led_obj = P1^0; //指示检测到物体
uint nCount = 0; //T0中断次数
unsigned long time_duration = 0; //Echo高电平持续时间,与距离成正比
unsigned int distance; //超声波测距结果,单位:cm
/**************************
*函数:delayMS()
*功能:ms级延时函数@12MHz
*参数:延时的毫秒值,1~65535
*返回值:无
*调用示例:delayMS(1000); //延时1s
***************************/
void delayMS(uint nms)
{
uint i,j;
for(i = 0; i < nms; i ) //延时nms毫秒
for(j = 0; j < 120; j ); //延时1毫秒
}
/***************************
*函数名:displayDistance()
*功能:显示测距结果,单位:cm(使用四位BCD数码管) P2和P3口
*参数:要显示的四位数
*返回值:无
***************************/
void displayDistance()
{
unsigned char i;
uchar num[3];
if(distance>400)
{
distance = 888; //超限则显示888
}
num[0] = distance/100; //百位
num[1] = (distance0)/10; //十位
num[2] = distance; //个位
for(i=0;i<3;i )
{
displayNUM(i,num[i]); //显示数字
delayMS(1); //保持
}
}
/**************************
*函数:Testing()
*功能:超声波测距,结果为cm,声速取340m/s
注意,无超时判断,如果一直没有回波,程序会“卡死”
*参数:无
*返回值:结果存到一个名为distance的全局变量里
*调用示例:无
***************************/
void Testing() //开始测量
{
//输出10us高电平到Trig引脚
Trig_pin = 1;
led_obj = 1; //指示灯off
_nop_();_nop_();_nop_();_nop_();_nop_(); //延时5us
_nop_();_nop_();_nop_();_nop_();_nop_(); //延时5us
_nop_();_nop_();_nop_();_nop_();_nop_(); //延时5us
Trig_pin = 0; //低电平
nCount = 0; //清零
time_duration = 0; //清零
TH0=0;TL0 =0;//清零计数器
//检测Echo,注意,如果一直无回波,则代码卡死在等待
//Echo变高
while(Echo_pin ==0); //等待Echo输出高电平
TR0 = 1; //启动定时器
while(Echo_pin ==1); //等待Echo变为低电平
TR0 = 0; //停止定时器
led_obj = 0; //指示灯on
//计算Echo持续的高电平时间,单位:us
//time_duration = TH0 * 256 TL0;
time_duration = nCount * 65536 TH0 * 256 TL0;
// 计算距离,单位为cm,假设声速为340m/s
// 公式:L = 340*t/2 = 170*t //单位m
// 由于定时器time_duration为us,则:
// L = (170*tus/(10^6)) * 100 = = 17*tus/1000 ,单位cm
time_duration = time_duration*17;
time_duration = time_duration/1000;
distance = time_duration; //变成距离
}
void main()
{
unsigned int i = 0;
//T0初始化
TMOD = 0x01; //16-bit timer
EA = 1;
ET0 = 1;
Trig_pin = 0; //默认不激活HC-SR04
while(1)
{
i ;
if(i==300)//约3ms*300的采样间隔
{
Testing(); //开始测量
i = 0;
}
displayDistance(); //3ms的执行时间
}
}
//T0定时器中断
void isr_timer0() interrupt 1
{
nCount ; //计数加一
}
四、测距测试
编译程序,下载.hex文件到开发板。测量环境为空调卧室,室温约27℃。
图5为桌面与天花板的距离,测量结果为191cm。真实距离约为200cm,误差约为9cm左右。
图5为在超声波上面放置鼠标,测量得到的距离为5cm,误差未知。
关于实际误差计算,必须用精确的尺测出超声波模块与物体之间的距离作为真值,测量值减去真值取绝对值就是绝对误差。
根据距离公式L= S*T/2(S为声速,T为时间),误差的因素有:
- 声速:与气压和环境温度有关,温度影响大。
- 时间精度。。
误差ΔL = (ΔS*T S*ΔT)/2。一般假设温度恒定,则声速恒定,则ΔS*T约为0,则误差
ΔL = S*ΔT/2
可见,误差取决于声速和时间精度。而51定时器的时间精度为us级,1us引起的误差为:
ΔL = S*ΔT/2 = 340m * 1us/2 = 17000/10^6 = 0.017cm =0.17mm
但实际上时间精度还受到超声波Echo输出高电平的持续时间精度的影响,距离误差的计算不能直接套用以上公式。
对于HC-SR04模块,技术规格里会给出精度,一般为3mm或4mm。
但时间的误差引起ΔL变化较小,关键还是声速的精度。因此,对于要求精确测距的场合,一般要定时测量环境温度,得到声速,再计算距离。
到此,超声波测距基本完成。最后需要注意以下三点:
- 两次超声波测距的最小间隔问题
根据 t = L*2/S,取L=4m,声速为340m/s,回波到达探头RX要经过23529us左右,即24ms。因此两次超声波测距的最小时间间隔应该大于24ms。避免没有收到回波,又再次触发发射,引起干扰等。
- 超声波测距超时问题
如果超声波前方没有障碍物,一直收到不到回波,程序会一直在等待Echo变为高电平。应用时,通常引入超时。超时时间的选取与两次超声波测距的最小间隔相同或更大。
- 必要时,引入数据处理算法,如滤波,即多次测量取平均值,并考虑剔除异常值。
参考文献:
- 梁洪亮. 科技史与方法论[M], 清华大学出版社.2016.10.
附7seg.h
#include"reg52.h"
#define led_seg P0
#define led_com P2
//Anode mode code table for 0-9,A-F, and off
//unsigned char LED_CODE[] = {0xC0,0xF9,0xA4,0xB0,0x99, //0~4
// 0x92,0x83,0xF8,0x80,0x98,0xFF}; //5~9;
//Cathode mode code table for 0-9,A-F, and off
unsigned char LED_CODE[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
unsigned char LED_COM[] = {0,1,2,3,4,5,6,7};
void displayNUM(unsigned char digital_sel, unsigned char i)
{
led_com = LED_COM[digital_sel];
led_seg = LED_CODE[i];
}
,原文作者:快乐冻鱼
作品来源:轻松学单片机
来源链接:https://mp.weixin.qq.com/s/3XoGr6_79bD06HBdhTRigQ