一、超声波测距基本原理
超声波测距的原理非常简单,超声波发生器在某一时刻发出一个超声波信号,当这个超声波信号遇到被测物体后会反射回来,被超声波接收器接收到。这样只要计算出从发出超声波信号到接收到返回信号所用的时间,就可以计算出超声波发生器与反射物体的距离。
距离的计算公::d=s/2=(c*t)/2
其中 d 为被测物与测距器的距离,s 为声波的来回路程,c 为声波,t 为声波来回所用的时间。
由于超声波也是一种声波,其声速 c 与温度有关,在不同温度下的超声波声速不同。在使用时,如果温度变化不大,则可认为声速是基本不变的。如果测距精度要求很高,则应通过温度补偿的方法加以校正。声速校正后,只要测得超声波往返的时间,即可求得距离。不同外温度下的超声波声速表如下
不同温度下超声波声速表
补充说明
最近在问答区发现有小伙伴疑惑为什么在计算距离时是根据Echo输出的高电平持续时间来计算距离。这里简单解释一下,仅供参考
1. 实际超声波测距的原理就是上面介绍的,记录的时间是从超声波发出到回波被接收的时间,然后这个时间除以2,再乘超声波的传播速度得到距离。超声波测距模块实际也是这么做的。模块内部会记录超声波发出的时刻,在接收到回波后会立刻以高电平的形式从Echo引脚输出高电平,高电平的持续时间就是超声波从发出到被接收到的时间间隔。这么做是模块为了方便单片机处理,快速得到超声波从发出到被接收到的时间间隔。
2. 实际大家可以自己测试一下,初始化一个定时器,从给Trig引脚10us高电平结束时刻开启定时器,到Echo引脚接收到高电平的时刻停止计时,用这个时间来计算距离,实际和用高电平持续时间来计算距离得到的结果基本是相同的。
3. 给Trig引脚10us高电平并不是说只发送10us的超声波。10us的高电平是为了触发模块工作。10us高电平结束后,模块会发送一个8个40KHz的方波。
二、超声波传感器简介
总体上讲,超声波发生器可以分为两大类:一类是用电气方式产生超声波类是用机械方式产生超声波。电气方式包括压电型、磁致伸缩型和电动型等:机械方式有加尔统笛、液哨和气流旋笛等。他们所产生的超声波的频率、功率和声波特性各不相同,因而用途也各不相同。目前较为常用的是压电式超声波发生。
压电式超声波发生器实际上是利用压电晶体的谐振来工作的。它有两个压电晶片和一个共振板。当它的两极外加脉冲信号,其频率等于压电晶片的固有振荡频率时,压电晶片将会发生共振,并带动共振板振动,便产生超声波。反之,如果两极间未外加电压,当共振板接收到超声波时,将压迫压电晶片作振动,将机械能转换为电信号,这时它就成为超声波接收器了。这里介绍的是一个超声波测距模块——HC-SR04。
HC-SR04
三、HC-SR04测距实现思路
这里就不再针对HC-SR04模块的原理和电路做详细介绍了,直接介绍利用该模块实现测距的思路。该模块有四个引脚。
• VCC —— 通常是5V供电
• GND —— 地
• Trig —— 给该引脚大于10us的高电平,超声波发射头会发送一个超声波信号
• Echo —— 该引脚在接收到返回的超声波信号后会变为高电平
Echo接收到的高电平持续时间即为超声波一个来回所用的时间,利用该时间除以2再乘上光速,即可得到测量距离。
四、超声波测距程序实现
4.1 HC-SR04初始化程序
HC-SR04初始化程序主要包括两部分,一部分是初始化HC-SR04的GPIO,Trig引脚设置为推挽式输出,Echo引脚设置为浮空输入。另一部分是初始化TIM2。初始化TIM2之后,没有立即使能,而是等到Echo接收到高电平的时刻,开启TIM2。
- /*
- *==============================================================================
- *函数名称:Drv_Hcsr04_Init
- *函数功能:初始化HC-SR04
- *输入参数:无
- *返回值:无
- *备 注:初始化HC-SR04引脚的同时,初始化了TIM2,用来记录高电平持续时间
- 初始化完TIM2后,没有使能,当Echo收到高电平后使能
- *==============================================================================
- */
- void Drv_Hcsr04_Init (void) // Hc-sr04初始化
- {
- // 结构体定义
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 生成用于定时器设置的结构体
- GPIO_InitTypeDef GPIO_InitStructure; // GPIO结构体
- NVIC_InitTypeDef NVIC_InitStructure; // NVIC结构体
- RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE); // 使能GPIO时钟
- // GPIO初始化
- GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG; // 发送电平引脚
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式输出
- GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
- GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
- GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO; // 返回电平引脚
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
- GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
- GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);
- // 定时器初始化 使用基本定时器TIM2
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能对应RCC时钟
- // 配置定时器基础结构体
- TIM_DeInit(TIM2);
- TIM_TimeBaseStructure.TIM_Period = (1000-1); // 设置在下一个更新事件装入活动的自动重装载寄存器周期的值(计数到1000为1ms )
- TIM_TimeBaseStructure.TIM_Prescaler =(72-1); // 设置用来作为TIMx时钟频率除数的预分频值 1M的计数频率 1US计数
- TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 不分频
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM向上计数模式
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
- TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除更新中断,免得一打开中断立即产生中断
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // 打开定时器更新中断
- // NVIC配置
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
- NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn; // 选择定时器2中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占式中断优先级设置为0
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应式中断优先级设置为0
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
- NVIC_Init(&NVIC_InitStructure);
- TIM_Cmd(TIM2,DISABLE);
- }
复制代码
4.2 TIM开关程序
- /*
- *==============================================================================
- *函数名称:Drv_Hcsr04_OpenTimerForHc
- *函数功能:打开定时器
- *输入参数:无
- *返回值:无
- *备 注:无
- *==============================================================================
- */
- void Drv_Hcsr04_OpenTimerForHc (void) // 打开定时器
- {
- TIM_SetCounter(TIM2,0); // 清除计数
- gMsHcCount = 0;
- TIM_Cmd(TIM2, ENABLE); // 使能TIMx外设
- }
- /*
- *==============================================================================
- *函数名称:Drv_Hcsr04_CloseTimerForHc
- *函数功能:关闭定时器
- *输入参数:无
- *返回值:无
- *备 注:无
- *==============================================================================
- */
- void Drv_Hcsr04_CloseTimerForHc (void) // 关闭定时器
- {
- TIM_Cmd(TIM2, DISABLE); // 使能TIMx外设
- }
复制代码
定时器中断服务函数如下
- /*
- *==============================================================================
- *函数名称:TIM2_IRQHandler
- *函数功能:定时器2中断服务程序
- *输入参数:无
- *返回值:无
- *备 注:无
- *==============================================================================
- */
- void TIM2_IRQHandler (void) // TIM2中断
- {
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) // 检查TIM2更新中断发生与否
- {
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIMx更新中断标志
- gMsHcCount++;
- }
- }
复制代码
4.3 获取定时时间
- /*
- *==============================================================================
- *函数名称:Drv_Hcsr04_GetEchoTimer
- *函数功能:获取定时器定时时间
- *输入参数:无
- *返回值:无
- *备 注:无
- *==============================================================================
- */
- u32 Drv_Hcsr04_GetEchoTimer (void)
- {
- u32 t = 0;
- t = gMsHcCount * 1000; // 得到MS
- t += TIM_GetCounter(TIM2); // 得到US
- TIM2 -> CNT = 0; // 将TIM2计数寄存器的计数值清零
- delay_ms(50);
- return t;
- }
复制代码
4.4 计算测量距离
- /*
- *==============================================================================
- *函数名称:Med_Hcsr04_GetLength
- *函数功能:获取测量距离
- *输入参数:无
- *返回值:无
- *备 注:一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
- 为了消除余震的影响,取五次数据的平均值进行加权滤波
- *==============================================================================
- */
- float Med_Hcsr04_GetLength (void )
- {
- u32 t = 0;
- int i = 0;
- float lengthTemp = 0;
- float sum = 0;
- while(i!=5)
- {
- TRIG_Send = 1; // 发送口高电平输出
- delay_us(20);
- TRIG_Send = 0;
- while(ECHO_Reci == 0); // 等待接收口高电平输出
- Drv_Hcsr04_OpenTimerForHc(); //打开定时器
- i = i + 1;
- while(ECHO_Reci == 1);
- Drv_Hcsr04_CloseTimerForHc(); // 关闭定时器
- t = Drv_Hcsr04_GetEchoTimer(); // 获取时间,分辨率为1us
- lengthTemp = ((float)t/58.0); // cm
- sum = lengthTemp + sum ;
- }
- lengthTemp = sum/5.0;
- return lengthTemp;
- }
复制代码
4.5 宏定义
- #define HCSR04_PORT GPIOB // 定义IO口
- #define HCSR04_CLK RCC_APB2Periph_GPIOB // 开启GPIO时钟
- #define HCSR04_TRIG GPIO_Pin_8 // 定义Trig对应引脚
- #define HCSR04_ECHO GPIO_Pin_9 // 定义Echo对应引脚
- #define TRIG_Send PBout(8) // 将TRIG_Send映射到PB8
- #define ECHO_Reci PBin(9) // 将ECHO_Reci映射到PB9
- void Drv_Hcsr04_Init(void); // Hc-sr04初始化
- void Drv_Hcsr04_OpenTimerForHc (void); //打开定时器
- void Drv_Hcsr04_CloseTimerForHc (void); //关闭定时器
- u32 Drv_Hcsr04_GetEchoTimer(void); // 获取定时器时间
复制代码
五、应用实例
利用串口打印距离信息,main函数如下
- float gDistance = 0; //定义获取返回距离变量
- int main(void)
- {
- Med_Mcu_Iint(); // 系统初始化
- while(1)
- {
- gDistance = Med_Hcsr04_GetLength(); //获取返回距离
- printf ("距离为:%.3f cm\n",gDistance); //串口打印返回距离
- delay_ms(500); //延时500ms = 0.5s
- }
- }
复制代码
六、拓展应用
超声波测距比较常用的,比如利用超声波测距模块实现智能车的自动避障,这个在后续实战项目系列中会有,在此就不再详细介绍了。主要思路就是根据HC-SR04测得的与障碍物的距离,来决定是否要停止或转弯,以及往哪边转弯。
|