超声波模块HC-SR04应用示例(带完整驱动程序)

HC-SR04模块是常用的超声波测距模块。本文总结出该模块在51和32两种MCU的应用示例,以供大家学习交流。

文章目录

  • 硬件
  • 软件
    • 基于stm32f10x
    • 基于at89c51/52
  • 注意事项

硬件

超声波模块HC-SR04应用示例(带完整驱动程序)_第1张图片
上图是该模块的样子,它有四个引脚,其电气特性如下:
超声波引脚功能图
其测距原理为:Trig引脚输入一个大于10us的高电平脉冲(注意必须是脉冲,如果不拉低不会有输出),然后它会自动发出一系列的超声波,经反射后,它会立刻从Echo引脚输出一段高电平,高电平延长的时间就是从发出声波到接收到声波的时间。因此距离计算公式为:

d i s = t d ∗ v s / 2 {\rm{dis = }}{{\rm{t}}_d}*{v_s}/2 dis=tdvs/2
其中, t d t_d td为高电平延时时长, v s v_s vs为声速,计算结果为距离。
上述过程的时序图如下:
超声波模块HC-SR04应用示例(带完整驱动程序)_第2张图片
从上面的时序,我么大致可以给出程序流程,首先配置初始化引脚(51不需要,但是32需要),然后给Trig一个高电平脉冲,然后监控Echo引脚(可以把它配置为上升沿触发的外部中断),如果Echo为高,开启定时器,开始计时,最后计算结果输出即可。

软件

软件部分,我们分32和51两种MCU来讲。

基于stm32f10x

stm32的外设资源丰富,其中它的通用定时器和高级定时器支持输入捕获模式,所以我们可以配置它,然后捕获对应引脚上的高电平时间就可以得到发射+收到过程的总用时长。当然除了上面的那个思路,我们还有很多种方法,比如用外部中断监听Echo引脚,当上升沿的时候就触发计时,然后等待该引脚为低电平后,就读取计时值,然后计算距离。需要说明的是32的时钟源有好几个,为了保证一定的精度(这个问题后面还会再谈),最好选择外部时钟源。计数的话外设用基本定时器就可以了,当然用内部的sysTick也行,而且后者是cm3公有的外设,用后者反而会使得程序的可移植性较好。具体的代码比较多,这里只给出库函数版本的核心代码。如下:

void hc_sr04_init(void) //引脚初始化
{
		 GPIO_InitTypeDef  GPIO_InitStructure;
			
		 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
			
		 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;		//PB5接Trig引脚,在头文件里面会将此引脚重定义为Trig		 
		 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
		 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 
		 GPIO_Init(GPIOB, &GPIO_InitStructure);					 

		 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;   //PB6接Echo引脚,在对应头文件里面会将此引脚重定义为Echo
		 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置下拉输入
		 GPIO_Init(GPIOB, &GPIO_InitStructure);
}
float getDistance(void) //获取一次距离
{
		Trig = 1; //高电平脉冲
		delay_us(20);
		Trig = 0;
		
		while(!Echo); //等待Echo高电平
		SysTick->CTRL = 0x000e0008; //未出现标志,9MHz,不触发异常, 先不开启计数
		SysTick->VAL = 0xffffff; 
		SysTick->LOAD = 0xffffff; 
		SysTick->CTRL |= 0x00000001; //Start counting 
		while(Echo); //等待高电平结束
		SysTick->CTRL &= 0xfffffffe; //stop counting
		send_int_data(0xffffff - SysTick->VAL); //调试使用,用串口监控计数值,可注释掉
		
		return (0xffffff - SysTick->VAL)/9/(1e6)*170; //单位m
}

上面的代码采用32内部的SysTick作为计时器,它是一个24位的定时器。在stm32中,该外设以HCLK(AHB时钟)或它的8分频作为时钟源。它功能比较简单,只有4个寄存器(CTRL、VAL、LOAD、CALIB,最后一个一般不用),上面的程序中把它配置为了9MHz、不开启定时中断的工作模式。VAL寄存器存放着当前递减的数值(它只能向下计数),LOAD为自动重装载值。

基于at89c51/52

8位的MCU没有上面的32那么复杂,我们直接把该模块的驱动程序封装成两个函数,一个用来初始化,另一个用于获取一次距离值,直接上代码:

 //微秒级延时函数,延时t微秒,注意要包含头文件“intrins.h”
void delay_us(unsigned char t)
{
	while(t--)
	{
				_nop_();
	}
}

void HC_SR04_Init(void) //用到了T0计时器
{
		TMOD &= 0xf1; //把T0配置为16位的定时计数模式
}
float getDistance(void) //获取一次距离值
{
		unsigned int n; 
		Trig = 1;
		TH0 = TL0 = 0;
		delay_us(10);
		Trig = 0; //10us以上的高电平脉冲
		while(!Echo); //等待返回,如果Echo为高就要开始计时了
		TR0 = 1;  //启动计时器
		while(Echo) ; //等待输出的高电平结束
		TR0 = 0; //关掉计时器
		n = (TH0)*256+TL0; //计算计数的数值
//		sendOneByte(TH0); //用于串口调试,可忽略
//		sendOneByte(' ');
//		sendOneByte(TL0);
		return 15.6672*n/100000; //单位m 
}

代码使用方法:先初始化定时器,即调用初始化函数,然后每当需要距离值得时候调用getDistance函数即可。

注意事项

在使用该模块的过程中,难免会遇到一些问题,这里也总结一下。

  1. 测距精度问题
    从该模块的数据手册中可以知道,当距离很近的时候(<40cm),误差很大,而且读数不稳定,其实从声波的特性就能想到,声波测距对面承担反射声波的载体最好表面光滑、面积比较大、并且和声波发出的方向最好垂直(除此之外,还有一些不可控的因素会影响测量精度,比如气温、室外的话还有风速等会影响声波的速度),这样测出来的值相对比较精确。至于,读数不稳定这一问题,个人认为,我们可以多读几次数,然后做滤波处理(常用的数字滤波算法及实现),这样就能减少误差,尽可能提高精度。
  2. 测距范围
    关于测距范围,最小距离就是数据手册上提到的5cm,最大距离则是一个不定值,它即受制于程序的配置或MCU的能力,又受现实中一些不可控的因素的影响。我们先从程序这个角度分析,对于51/52单片机,假如51的晶振频率12MHz,那么我们把定时器配置为最大的16位的计时器,这样,可以记到的最大值为65536个,定时器12分频一下,得到时长65.536ms,设声速340m/s(仅适用于空气为介质的情况),那么最大距离为65ms*340/2/1000 ≈ 11m;对于32单片机,它的计时器有24位,如果只往下递减一次,那么最大距离值为0xffffff /9/(1e6)*340/2≈304m,如果大于此距离,还可以开启定时中断,累加计数的周期次数(一个计数周期就是从0xffffff计到0)。再从现实中的情况分析,声波发射和反射的过程中会损失相当一部分能量,所以距离过长,超声波可能就收不到反射回来的信号了。这时就得不到数据了,这种情况没法直接向上面那样定量分析,所以只需要明白,距离不要太大,也不要过小就行了。

上面封装好的驱动程序,会在微信公众号“24K纯学渣”上面开源出来,如有需要,回复“超声波”即可获取。

参考文献:
HC-SR中文数据手册
STM32数据手册
百度百科

你可能感兴趣的:(嵌入式软件开发,单片机)