第3-8讲:超声波测距(基于HC-SR04)
-
- 学习目的
- 了解超声波测距原理。
- 掌握单片机编程使用HC-SR04测距,并使用LCD1602液晶显示模块显示测试结果。
- 超声波测距
- 超声波测距原理
- 超声波测距
超声波是利用反射的原理测量距离的,被测距离一端为超声波传感器,另一端必须有能反射超声波的物体。测量距离时,将超声波传感器对准反射物发射超声波,并开始计时,超声波在空气中传播到达障碍物后被反射回来,传感器接收到反射脉冲后立即停止计时,然后根据超声波的传播速度和计时时间就能计算出两端的距离。
当介质为空气时,声速为 340m/s,根据记录的时间,即可计算出发射位置与障碍物之间的距离。
-
-
- HC-SR04超声波测距模块
-
- 模块简介
艾克姆的HC-SR04超声波测距模块如下图所示,其核心是两个压电陶瓷超声波传感器。一个用作发射器,当接收到“Trig”引脚的触发信号后对外发射40 KHz超声波脉冲,另外一个用作接收器,监听到发射器发射的脉冲后,“Echo”引脚将产生一个输出脉冲,该脉冲宽度为超声波发出到接收的时间,由此可确定脉冲传播的距离。
图1:HC-SR04超声波测距模块
- 模块参数
图2:模块参数
- 引脚定义
表1:HC-SR04超声波测距模块引脚描述
序号 | 针脚名称 | 功能描述 |
1 | VCC | 模块供电正,典型工作电压(3.3~5)VDC。 |
2 | Trig | 触发端。 |
3 | Echo | 接收端,当接收到反射信号时,引脚产生一个脉冲。脉冲的长度与检测发射信号所需的时间成正比。 |
4 | GND | 模块供电负。 |
- 工作流程
HC-SR04超声波测距模块工作时序如下图所示。
图3:超声波时序图
流程:
- 通过Trig 发送触发信号,给宽度至少 10us 的脉冲信号;
- 超声波模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
- 有信号返回后,超声波模块Echo引脚 输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间,距离计算公式如下:
-
- 软件设计
- 超声波测距实验(串口输出测试结果)
- 软件设计
- 注:本节的实验是在“实验2-6-1:串口1数据收发实验”的基础上修改,本节对应的实验源码是:“实验3-8-1:超声波测距实验(串口输出测试结果)”。
-
-
- 实验内容
-
-
- STC8A8K64D4单片机的P1.7连接HC-SR04超声波模块的Trig引脚,测量距离时发送触发信号;P1.7 连接HC-SR04超声波模块的Echo引脚,获取回响信号。
- 使用Timer0测量回响信号高电平持续时间,定时器时钟配置为12T,在24MHz主频下,一个计数值的时间为0.5us。
- 每200ms测量一次,计算出距离后通过串口输出结果。
-
-
- 代码编写
-
-
- 新建一个名称为“HC-SR04.c”的文件及其头文件“HC-SR04.h”保存到工程的“Source”文件夹,并将“HC-SR04.c”加入到Keil工程中的“SOURCE”组。该文件用于存放HC-SR04测距操作相关的函数。
- 引用头文件
因为在“main.c”文件中使用了“hc_sr04.c”文件中的函数,所以需要引用下面的头文件“hc_sr04.h”。
代码清单:引用头文件
- //引用HC-SR04的头文件
- #include "hc_sr04.h"
- 定义引脚
本例中,我们用P1.6和P1.7分别用于HC-SR04超声波测距模块的ECHO和TRIG信号引脚,定义如下。
代码清单:定义用于I2C通信的引脚
- sbit ECHO = P1^6; //连接HC-SR04超声波模块的Echo引脚,获取回响信号
- sbit TRIG = P1^7; //连接HC-SR04超声波模块的Trig引脚,发送触发信号
- 初始化定时器0
Timer0用于测试回响信号高电平持续时间,这里我们需要计算一下,以确保Timer0的最大溢出时间大于回响信号高电平持续时间,这样,我们每次测量时,就可以让Timer0从0开始计数,测量完成后读出Timer0的计数值即可计算出时间,同时,还可以利用定时器的溢出中断判断测量是否超时。
- 超声波最大测试距离为4.5m,对应的回响高电平持续时间 = (4.5×2)/340(S) ≈0.0265S ;
- Timer0时钟配置为12T,在24MHz主频下,一个计数值的时间为0.5us,最大溢出时间 = 65536 × 0.5us = 0.0327S,满足要求。
定时器初始化代码清单如下:
代码清单:初始化Timer0
- /**********************************************************************************
- 功能描述:初始化Timer0(系统时钟使用24MHz)
- 参 数:无
- 返 回 值:无
- ***********************************************************************************/
- void timer0_init(void)
- {
- AUXR &= 0x7F; //定时器时钟12T模式
- TMOD &= 0xF0; //配置Timer0为定时器
- TL0 = 0x00; //设置定时初始值
- TH0 = 0x00; //设置定时初始值
- ET0 = 1; //使能定时器0中断
- }
- 测量距离
按照HC-SR04超声波模块工作流程编写测量函数如下,需要注意的是:在使用while循环等待回响信号时,要进行超时判断(这里也可以使用IO中断来判断)。
代码清单:触发超声波测量一次距离
- /**********************************************************************************
- * 描 述 : 触发超声波测量一次距离
- * 参 数 : 无
- * 返回值 : =0:测量超时,测量错误;>0:测量的距离,单位CM
- **********************************************************************************/
- float measure_distance(void)
- {
- u16 t;
- float dist;
- TL0 = 0x00; //设置定时初始值
- TH0 = 0x00; //设置定时初始值
- TRIG=1; //发出触发信号(Trig输出高电平),持续时间大于10uS
- delay_ms(1);
- TRIG=0;
- while((!ECHO) && (meas_time_out == false)); //等待ECHO端口回响信号(ECHO端口由低电平变为高电平)
- TR0=1; //启动定时器0开始计数
- while((ECHO)&&(meas_time_out==false)); //等待ECHO端口回响信号结束(ECHO端口由高电平变为低电平)
- TR0=0; //停止定时器0计数
- meas_time_out = false;
- if(meas_time_out == true)return 0; //定时器溢出,测量出错
- t = (u16)(((TH0<<8) + TL0)/2); //读取Timer0计数值,换算成us
- dist = (float)(1.7 *t /100); //计算出距离,单位CM
- return dist;
- }
- 主函数
主函数中完成相关的初始化,之后,在主循环中每200ms测量距离,并通过串口输出测量结果。
代码清单:主函数
- /**************************************************************************
- 功能描述:主函数
- 入口参数:无
- 返 回 值:int类型
- **************************************************************************/
- int main(void)
- {
- float dist_cm;
- P2M1 &= 0xBF; P2M0 &= 0xBF; //设置P2.6为准双向口(LED1)
- P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口(串口1的RxD)
- P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出(串口1的TxD)
- P1M1 &= 0x3F; P1M0 &= 0x3F; //设置P1.6~P1.7为准双向口
- TRIG=0; //触发引脚设置为低电平
- uart1_init(); //串口1初始化
- timer0_init(); //定时器0初始化
- EA = 1; //使能总中断
- while(1)
- {
- dist_cm = measure_distance(); //执行一次测距
- if(dist_cm != 0)
- {
- printf("distance %.2f CM\r\n",dist_cm);//串口打印测距结果,单位CM
- }
- else
- {
- printf("err:measure timeout\r\n"); //测距超时出错
- }
- delay_ms(200);
- led_toggle(LED_1); //翻转指示灯D1的状态,指示一次测距完成
- }
- }
-
-
- 硬件连接
-
-
如下图所示,用杜邦线将HC-SR04超声波模块连接到开发板上。
图4:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验3-8-1:超声波测距实验(串口输出测试结果)”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\HC-SR04\project”目录下的工程文件“HC-SR04.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“HC-SR04.hex”位于工程的“…\HC-SR04\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。
- 程序运行后,将超声波模块探头正对墙壁或其他物体测量,串口调试助手接收窗口可以看到测试结果,将超声波模块远离/靠近墙壁,可以观察到距离变大/减小,如下图所示。
- 注意事项:测距时,被测物体的面积不少于 0.5 平方米且要尽量平整。否则会影响测试结果。
图5:超声波测距
-
-
- 光照强度测量实验(连续高精度模式)
-
- 注:本节的实验是在“实验3-8-1:超声波测距实验(串口输出测试结果)”的基础上修改,本节对应的实验源码是:“实验3-8-2:超声波测距实验(LCD1602显示测试结果)”。
-
-
- 实验内容
-
-
在“实验3-8-1:超声波测距实验(串口输出测试结果)”的基础上,将测量的距离在LCD1602液晶显示模块上显示出来。
-
-
-
- 代码编写
-
-
- 将“实验3-3-1:LCD1602显示字符”源码目录“…\lcd1602\Source”下的“lcd1602.c”的文件及其头文件“lcd1602.h”拷贝到本工程的“Source”文件夹,并将“lcd1602.c”加入到Keil工程中的“SOURCE”组。
- main()函数中,调用measure_distance()函数获取测量结果后,通过sprintf函数将浮点数转换为字符串存储到数组中,接着调用LCD1602显示函数将存储在数组中的字符显示在液晶屏上。
代码清单:主函数
- /**********************************************************************************
- 功能描述:主函数
- 参 数:无
- 返 回 值:int类型
- ***********************************************************************************/
- int main(void)
- {
- u8 i = 0;
- u16 dist_int;
- float dist_cm;
- P2M1 &= 0xBF; P2M0 &= 0xBF; //设置P2.6为准双向口(指示灯D1)
- P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口(串口1的RxD)
- P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出(串口1的TxD)
- P3M1 &= 0xCB; P3M0 |= 0x34; //设置P3.2,P3.4,P3.5为强推挽输出(LCD1602控制)
- P6M1 &= 0x00; P6M0 &= 0x00; //设置P6.0~P6.7为准双向口(LCD1602数据)
- P1M1 &= 0x3F; P1M0 &= 0x3F; //设置P1.6~P1.7为准双向口(HC-SR04的Echo和Trig)
- uart1_init(); //串口1初始化
- lcd1602_init(); //LCD1602显示模块初始化
- timer0_init(); //定时器0初始化
- EA = 1; //使能总中断
- while(1)
- {
- LCD1602_WriteCmd(0x01); //清屏
- dist_cm = measure_distance(); //执行一次测距
- if(dist_cm != 0)
- {
- printf("distance %.2f CM\r\n",dist_cm);//串口打印测距结果,单位CM
- dist_int = (u16)dist_cm; //得到整数部分
- sprintf(disp_buf,"%3.02f",dist_cm); //将浮点数转换为字符串并存储到disp_buf数组
- LCD1602_DispString(0, 0, table1);
- if(dist_int>=100) //3位整数
- {
- for(i=0;i<6;i++)LCD1602_DispOneChar(1, i, disp_buf[i]);
- }
- else if(dist_int>=10) //2位整数
- {
- for(i=0;i<5;i++)LCD1602_DispOneChar(1, i, disp_buf[i]);
- }
- else if(dist_int>0) //1位整数
- {
- for(i=0;i<4;i++)LCD1602_DispOneChar(1, i, disp_buf[i]);
- }
- }
- else
- {
- printf("err:measure timeout\r\n"); //测距超时出错
- LCD1602_DispString(1, 0, table2); //显示“err”,提示测量出错
- }
- delay_ms(1000);
- led_toggle(LED_1); //翻转指示灯D1的状态,指示一次测距完成
- }
- }
-
-
- 硬件连接
-
-
如下图所示,连接HC-SR04超声波模块和LCD1602液晶显示模块。
图6:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验3-8-2:超声波测距实验(LCD1602显示测试结果)”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\HC-SR04-DISP\project”目录下的工程文件“HC-SR04-DISP.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“HC-SR04-DISP.hex”位于工程的“…\HC-SR04-DISP\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。
- 程序运行后,将超声波模块探头正对墙壁或其他物体测量,LCD1602液晶显示模块上可以看到测试结果,如下图所示。
- 注意事项:测距时,被测物体的面积不少于 0.5 平方米且要尽量平整。否则会影响测试结果。
图7:LCD1602显示测量的距离