目录
模块简介
HY-SRF05 超声波测距模块可提供2cm-450cm 的非接触式距离感测功能,测距精度可达3mm。
模块包括超声波发射器、 接收器与控制电路。
电气参数
电气参数 | HY-SRF05 超声波模块 |
工作电压 | DC 5 V (4.5V 至 5.5V) |
工作电流 | 15mA (10 至 40mA) |
工作频率 | 40Hz |
静态电流 |
小于2mA |
最远射程 | 4.5m |
最近射程 | 2cm |
测量角度 | 15 度 |
输入触发信号 | 10uS 的 TTL 脉冲 |
输出回响信号 | 输出 TTL 电平信号, 与射程成比例 |
规格尺寸 | 45*20*15mm |
实物规格
引脚接线
VCC | 5V | 电源 |
GND | GND | 接地 |
TRIG | PB1 | 触发控制, 信号输入(对模块而言是输入) |
ECHO | PB0 | 回响信号输出 |
OUT | 不接 | 开关量输出(当报警模块使用) |
工作原理
(1) 采用IO 口TRIG 触发测距, 给至少10us 的高电平信号;
(2) 模块自动发送8 个40khz 的方波, 自动检测是否有信号返回;
(3) 有信号返回, 通过IO 口ECHO 输出一个高电平, 高电平持续的时间就是超声
波从发射到返回的时间。 测试距离=(高电平时间*声速(340m/s) ) /2;
超声波测距原理是在超声波发射装置发出超声波,它的根据是接收器接到超声波时的时间差,与雷达测距原理相似。 超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播,途中碰到障碍物就立即返回来,超声波接收器收到反射波就立即停止计时。
时序图
以上时序图表明你只需要提供一个10uS 以上脉冲触发信号,该模块内部将 发出8 个40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号.
回响信号的脉冲宽度与所测的距离成正比。
由此通过 发射信号到收到的回响信号时间间隔可以计算得到距离。
公式: : uS/58=厘米或者uS/148=英寸或距离=高电平时间*声速(340m/s)/2;
距离计算公式的推导在本博客公式推导部分
注: 1、 此模块不宜带电连接, 若要带电连接, 则先让模块的GND端先连接,否则会影响模块的正常工作。
2、 测距时, 被测物体的面积不少于0.5 平方米且平面尽量要求平整, 否则影响测量的结果。
3.建议测量周期为60ms 以上, 以防止发射信号对回响信号的影响.
公式推导
Distance=340m/s*time/2=34000cm*time/1000000us/2==time/58
Distance单位为cm,time单位为us
模块可测距离为:2cm-450cm
代入公式,可得time范围为116us~26100us
英文手册
Echo : The pulse length is propotional to the distance from the obstacle ( 100usec to 25ms, times out if 30ms)
译文:回声:脉冲长度与障碍物的距离成正比(100us到25ms, 30ms超时)
则可以令定时器1us计数一次,即1MHz,计数频率为72M/72=1M,计数器可以为60000,可以计数60000次,绰绰有余。
驱动代码
这个函数用来处理多组测量的距离,减小误差。数据排序采用冒泡算法
有能力的同学可以学学其他较好的排序算法
/** * @brief 统分函数的英文名称是trimmean,又叫修剪函数 * @param Data-待处理数组 ,len-数组长度,deleted_bothdata-去掉的最大值的个数=去掉的最小值的个数 * @retval 去掉指定个数最大值与最小值后平均值 */ float Trimmean(float *Data,uint8_t len,uint8_t deleted_bothdata) { uint8_t i,j,flag=0; float temp,sum=0; //冒泡排序-升序 for(i=0;i<len-1;i++) { flag=0; for(j=0;j<len-1-i;j++) { if(Data[j]>Data[j+1]) { temp=Data[j]; Data[j]=Data[j+1]; Data[j+1]=temp; flag=1; } } if(flag==0){break;} } for(i=deleted_bothdata;i<len-deleted_bothdata;i++) { sum+=Data[i]; } return sum/(len-deleted_bothdata*2); }
Timer.h
#ifndef _TIMER_H_
#define _TIMER_H_
void Timer_Init(void);
#endif
Timer.c
#include "stm32f10x.h" // Device header
#include "Timer.h"
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 60000- 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //记一次数1us
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_Cmd(TIM2, DISABLE);
}
Ultrasound.h
#ifndef _ULTRASOUND_H_
#define _ULTRASOUND_H_
void Ultrasound_Init(void);
float Ultrasound_GetDistance(void);
#endif
Ultrasound.c
#include "stm32f10x.h" // Device header
#include "Ultrasound.h"
#include "Delay.h"
#define test_num 10
void Ultrasound_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // 1-Trig
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//默认输出低电平
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 0-Echo
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void Ultrasound_Start(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_1);
Delay_us(15);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}
/*去掉最大值与最小值,求平均值函数,统分函数的英文名称是trimmean,又叫修剪函数*/
float Trimmean(float *Data,uint8_t len,uint8_t deleted_bothdata)
{
uint8_t i,j,flag=0;
float temp,sum=0;
//冒泡排序-升序
for(i=0;i<len-1;i++)
{
flag=0;
for(j=0;j<len-1-i;j++)
{
if(Data[j]>Data[j+1])
{
temp=Data[j];
Data[j]=Data[j+1];
Data[j+1]=temp;
flag=1;
}
}
if(flag==0){break;}
}
for(i=deleted_bothdata;i<len-deleted_bothdata;i++)
{
sum+=Data[i];
}
return sum/(len-deleted_bothdata*2);
}
float Ultrasound_GetDistance(void)
{
uint16_t time=0; //us
float Distance[test_num]={0};//cm
uint8_t i;
for(i=0;i<test_num;i++)
{
Ultrasound_Start();
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==RESET);
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==SET)
{
TIM_Cmd(TIM2,ENABLE);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==SET);
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==RESET)
{
TIM_Cmd(TIM2,DISABLE);
time=TIM2->CNT;
TIM_SetCounter(TIM2,0);
Distance[i]=(34000*time/1000000)/2;//cm
}
}
Delay_ms(80);//测量周期60ms以上
}
//数据滤波,test_num次数据排序,舍弃最大最小值求平均值
return Trimmean(Distance,test_num,2);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Ultrasound.h"
float Disatance;
int main(void)
{
OLED_Init();
Ultrasound_Init();
Timer_Init();
OLED_ShowString(1,1,"000.0");
while (1)
{
Disatance=Ultrasound_GetDistance();
OLED_ShowNum(1,1,Disatance,3);
OLED_ShowNum(1,5,(uint16_t)(Disatance*10)%1000%100%10,1);
Delay_ms(100);
}
}
代码分析
float Ultrasound_GetDistance(void)函数中有while和Delay延时。
阻塞延迟会影响其他任务的执行,导致它们暂时停止,等待延迟结束后才能继续执行。这种延迟方式会浪费CPU,因为在延迟期间,CPU无法执行其他任务。会影响main函数中while内的其他任务。
我们应采用更好的方法来实现功能,将会在下一篇更新。看到这里了,点个赞吧。感谢🙇
参考手册
HY-SRF05 使用手册明书 - 道客巴巴 (doc88.com) ------中文版
M_HY-SRF05_0003.pdf (micros.com.pl)--------------------English