基于STM32的超声波HC-SR04和红外测距模块测量距离的实验对比(HAL库)
时间:2022-08-21 06:00:01
前言:本文主要是为了日常普通场合下测距做的对比实验,本实验主要包括2种模块:超声波测距模块(HC-SR04)和红外测距模块(SHARP GP2Y0A21YK0F)。两个模块的测距原理和编程实验方法不同,测距效果也大不相同。有兴趣的读者可以看看这篇文章。文章末尾有实验程序开源。
硬件设备:STM32F103C8T6;OLED;超声模块:HC-SR04及红外测距模块:SHARP GP2Y0A21YK0F
硬件实物图:
效果图:
引脚连接:
超声波HC-SR04模块:
VCC --> VCC
GND --> GND
Trig --> PA5
Echo --> PA0
红外测距SHARP GP2Y0A21YK0F模块:
VCC --> VCC
GND --> GND
Vo(数据) --> PA1
1.超声波测距模块简介
超声波是振动频率高于20KHZ机械波。它具有频率高、波长短、绕射现象小、方向性好等特点射线和定向传播适用于大学生、工程师、技术人员和电子爱好者。
新版HC-SRO4,性能远超老版HC-SRO4、US-015测距精度高于旧版HC-SRO4和US-在015的情况下,测距范围更远,可达6米,远超一般超声波测距模块。CS-100A超声波测距SOC芯片,高性能,工业级,宽电压,低价格,只有普通超声波测距模块的一半,性能远远超过普通超声波测距模块。US-均采用025相同CS100A芯片,接口完全兼容。
主要参数:
超声波测距芯片:CS100A
工作电压:DC 3V-5.5V
工作电流:5.3mA
工作温度:-40℃-85℃
输出方式:GPI0
感应角度:小于15度
探测距离:2cm~600cm
探测精度:0.1cm 1%
工作原理:
1、采用IO触发测量距离个测量距离us的高电平;
自动发送模块8个40khz的方波,自动检测是否有信号返回;
3.信号返回,通过lo输出高电平、高电平持续的时间就是超声波从发射到返回的时间,测试距离=(高电平时间*声速(340M/s)/2);
该模块有一个接口:4Pin供电和通信接口。为2.54mm如图所示:从左到右依次编号1、2、3、4,它们的定义如下∶
1号Pin:接VCC电源(直流3V-5.5V)。
2号Pin:连接外部电路Trig端,输入一个管脚10uS以上模块测距可触发高电平。
3号Pin:连接外部电路Echo端,当测距结束时,管脚将输出高电平平宽度超声波往返时间之和。
4号Pin:连接外部电路的地面。
超声时序图:
上面的时序图表明你只需要提供10uS以上脉冲模块内部触发信号8个40kHz周期电平并检测回波。
一旦检测输出回波信号回响信号。
回响信号的脉冲宽度与所测的距离成正比。距离可以通过发射信号到收到的回响信号的时间间隔来计算。
公式: us/58-厘米或者uS/148-英寸;或:距离=高电平时间*声速(340M/S)/2;
特别注意:
1.该模块不应带电连接。如果要带电连接,请先让模块连接GND先连接,否则会影响模块的正常工作。
2.测距时,被测物体的面积不小于0.平面应尽可能平整,否则会影响测量结果。
二、红外测距模块简介
GP2YOA21YKOF/span>是测距传感器单元PSD的集成组合构成(位置敏感探测器),IRED((红外发光二极管)和信号处理电路的各种物体的反射率,对环境温度和工作时间不易受到影响的距离检测由于采用三角测量方法,这个装置输出对应于该电压检测距离,所以这种传感器还可以来作为接近传感器。
工作电压:4.5~5.5V DC
消耗电流:典型30mA
工作温度:-10~60℃
储存温度:-40~70℃
测量距离:10~80cm(10cm内数据盲区)
★输出类型:模拟量输出型
引脚说明:
①Vo:模拟电压输出
②GND:地
③VCC:电源正
输出电压(v)与距离反射物体L (cm)
从上图就可以明显看出,该模块0~10cm内的电压与距离不成单调关系,故一般该传感器的有效值在10~80cm。
应用场合:
2、机器人吸尘器
3.传感器用于节能(自动取款机,复印机,自动售货机)
4.游乐设备(机器人,街机游戏机)
三、OLED简介
篇幅有限,如果这方面不熟悉的朋友可以去看看本人的另一篇文章,十分相近,希望对您有所帮助。【强烈推荐】基于stm32的OLED各种显示实现(含动态图)_混分巨兽龙某某的博客-CSDN博客https://blog.csdn.net/black_sneak/article/details/125418537?spm=1001.2014.3001.5501
四、CubexMX配置
1、RCC配置外部高速晶振(精度更高)——HSE;
2、SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁);
3、ADC1配置:配置ADC-IN1模数转换——红外测距仪所用(PA1接Vo)。
4、 TIM2配置:设置定时器TIM2每1us向上计数一次,通道1为上升沿捕获并连接到超声波模块的ECHO引脚,记得开启定时器中断(涉及到捕获中断+定时器溢出中断)。
5、I2C2配置:作为OLED的通讯方式;
6、GPIO配置:PA5接到了HC-SR04的TRIG触发引脚,默认输出低电平
7、TIM1配置:由上面可知HC-SR04的使用需要us级的延迟函数,HAL库自带只有ms的,所以需要自己设计一个定时器;
8、时钟树配置:
注意:ADC的时钟频率不要过高,否则会影响精度(8~12Mhz)
9、工程配置
五、代码
5.1 超声波HC-SR04模块代码
其实,超声波HC-SR04的驱动就是基于GPIO口的调用。同时,由于超声波测距模块是基于超声波的物理性质,去进行距离测量,故此其精度受到很多因素影响。
影响因素:温度,传播介质及其物理属性等
本次实验就不进行超声波模块的精度补偿了,后面有空可以给大家出一个相关的文章。
HC-SR04.h:
#ifndef HCSR04_H_
#define HCSR04_H_
#include "main.h"
#include "delay.h"
typedef struct
{
uint8_t edge_state;
uint16_t tim_overflow_counter;
uint32_t prescaler;
uint32_t period;
uint32_t t1; // 上升沿时间
uint32_t t2; // 下降沿时间
uint32_t high_level_us; // 高电平持续时间
float distance;
TIM_TypeDef* instance;
uint32_t ic_tim_ch;
HAL_TIM_ActiveChannel active_channel;
}Hcsr04InfoTypeDef;
extern Hcsr04InfoTypeDef Hcsr04Info;
/**
* @description: 超声波模块的输入捕获定时器通道初始化
* @param {TIM_HandleTypeDef} *htim
* @param {uint32_t} Channel
* @return {*}
*/
void Hcsr04Init(TIM_HandleTypeDef *htim, uint32_t Channel);
/**
* @description: HC-SR04触发
* @param {*}
* @return {*}
*/
void Hcsr04Start();
/**
* @description: 定时器计数溢出中断处理函数
* @param {*} main.c中重定义void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
* @return {*}
*/
void Hcsr04TimOverflowIsr(TIM_HandleTypeDef *htim);
/**
* @description: 输入捕获计算高电平时间->距离
* @param {*} main.c中重定义void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
* @return {*}
*/
void Hcsr04TimIcIsr(TIM_HandleTypeDef* htim);
/**
* @description: 读取距离
* @param {*}
* @return {*}
*/
float Hcsr04Read();
#endif /* HCSR04_H_ */
HC-SR04.c:
#include "hc-sr04.h"
Hcsr04InfoTypeDef Hcsr04Info;
/**
* @description: 超声波模块的输入捕获定时器通道初始化
* @param {TIM_HandleTypeDef} *htim
* @param {uint32_t} Channel
* @return {*}
*/
void Hcsr04Init(TIM_HandleTypeDef *htim, uint32_t Channel)
{
/*--------[ Configure The HCSR04 IC Timer Channel ] */
// MX_TIM2_Init(); // cubemx中配置
Hcsr04Info.prescaler = htim->Init.Prescaler; // 72-1
Hcsr04Info.period = htim->Init.Period; // 65535
Hcsr04Info.instance = htim->Instance; // TIM2
Hcsr04Info.ic_tim_ch = Channel;
if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_1)
{
Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_1; // TIM_CHANNEL_4
}
else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_2)
{
Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_2; // TIM_CHANNEL_4
}
else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_3)
{
Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_3; // TIM_CHANNEL_4
}
else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_4)
{
Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_4; // TIM_CHANNEL_4
}
else if(Hcsr04Info.ic_tim_ch == TIM_CHANNEL_4)
{
Hcsr04Info.active_channel = HAL_TIM_ACTIVE_CHANNEL_4; // TIM_CHANNEL_4
}
/*--------[ Start The ICU Channel ]-------*/
HAL_TIM_Base_Start_IT(htim);
HAL_TIM_IC_Start_IT(htim, Channel);
}
/**
* @description: HC-SR04触发
* @param {*}
* @return {*}
*/
void Hcsr04Start()
{
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);
DelayUs(10); // 10us以上
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
}
/**
* @description: 定时器计数溢出中断处理函数
* @param {*} main.c中重定义void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
* @return {*}
*/
void Hcsr04TimOverflowIsr(TIM_HandleTypeDef *htim)
{
if(htim->Instance == Hcsr04Info.instance) // TIM2
{
Hcsr04Info.tim_overflow_counter++;
}
}
/**
* @description: 输入捕获计算高电平时间->距离
* @param {*} main.c中重定义void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
* @return {*}
*/
void Hcsr04TimIcIsr(TIM_HandleTypeDef* htim)
{
if((htim->Instance == Hcsr04Info.instance) && (htim->Channel == Hcsr04Info.active_channel))
{
if(Hcsr04Info.edge_state == 0) // 捕获上升沿
{
// 得到上升沿开始时间T1,并更改输入捕获为下降沿
Hcsr04Info.t1 = HAL_TIM_ReadCapturedValue(htim, Hcsr04Info.ic_tim_ch);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, Hcsr04Info.ic_tim_ch, TIM_INPUTCHANNELPOLARITY_FALLING);
Hcsr04Info.tim_overflow_counter = 0; // 定时器溢出计数器清零
Hcsr04Info.edge_state = 1; // 上升沿、下降沿捕获标志位
}
else if(Hcsr04Info.edge_state == 1) // 捕获下降沿
{
// 捕获下降沿时间T2,并计算高电平时间
Hcsr04Info.t2 = HAL_TIM_ReadCapturedValue(htim, Hcsr04Info.ic_tim_ch);
Hcsr04Info.t2 += Hcsr04Info.tim_overflow_counter * Hcsr04Info.period; // 需要考虑定时器溢出中断
Hcsr04Info.high_level_us = Hcsr04Info.t2 - Hcsr04Info.t1; // 高电平持续时间 = 下降沿时间点 - 上升沿时间点
// 计算距离
Hcsr04Info.distance = (Hcsr04Info.high_level_us / 1000000.0) * 340.0 / 2.0 * 100.0;
// 重新开启上升沿捕获
Hcsr04Info.edge_state = 0; // 一次采集完毕,清零
__HAL_TIM_SET_CAPTUREPOLARITY(htim, Hcsr04Info.ic_tim_ch, TIM_INPUTCHANNELPOLARITY_RISING);
}
}
}
/**
* @description: 读取距离
* @param {*}
* @return {*}
*/
float Hcsr04Read()
{
// 测距结果限幅
if(Hcsr04Info.distance >= 500)
{
Hcsr04Info.distance = 500; //元器件资料说是600cm最高距离,这里保守一点
}
return Hcsr04Info.distance;
}
由于利用中断去读取定时测算的脉冲距离,所以这里需要重写定时器的中断服务函数。(这部分放在main.c最后即可)
/* USER CODE BEGIN 4 */
/**
* @description: 定时器输出捕获中断
* @param {TIM_HandleTypeDef} *htim
* @return {*}
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //捕获回调函数
{
Hcsr04TimIcIsr(htim);
}
/**
* @description: 定时器溢出中断
* @param {*}
* @return {*}
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) //在中断回调函数中添加用户代码
{
Hcsr04TimOverflowIsr(htim);
}
/* USER CODE END 4 */
5.2 红外测距SHARP GP2Y0A21YK0F模块代码
其实,红外测距模块就是单纯的ADC读取,通过电压结合手册中电压与距离的关系公式得到计算的距离。
ranging.h:
#ifndef __SHARP2Y0A21_H
#define __SHARP2Y0A21_H
#include "main.h"
#define Adc1IN1Distance_READ_TIMES 10 //定义红外传感器读取次数,以便取平均值
void DistanceSensor_Init(void); //初始化红外传感器
float DistanceSensor_Get_Val(void); //读取红外传感器的值
#endif
ranging.c:
#include "sharp.h"
#include "adc.h"
#include "main.h"
#include "stdio.h"
//初始化ADC,不用修改
//这里我们仅以规则通道为例
//初始化传感器,需要修改端口和引脚号,这里是c出口,c1引脚,ADC1的IN1
void DistanceSensor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟
//先初始化ADC1通道11 IO口
GPIO_InitStructure.Pin = GPIO_PIN_1;//PA1
GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;//模拟输入
GPIO_InitStructure.Pull = GPIO_NOPULL ;//不带上下拉
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
MX_ADC1_Init();//初始化ADC1
}
//PA1=IN1
float DistanceSensor_Get_Val(void)
{
uint32_t temp_val=0;
float distemp=0.0;
uint8_t t;
for(t=0;t
5.3 OLED显示代码
OLED模块作为常用的数据输出显示模块,应该是必须要掌握的。如果有朋友不是很熟悉,可以去参考本人上面提到的那篇文章,十分详尽!!!
唯一不同的点:本实验的目的是需要对比一下超声波测距模块与红外测距模块的效果怎么样。其精度最高越好,这里就需要考虑到实现OLED显示含有小数的数字功能!
含有小数的数字显示API函数:
//z_len为整数显示位数,f_len为小数显示位数,size2为字体大小
void OLED_Showdecimal(u8 x,u8 y,float num,u8 z_len,u8 f_len,u8 size2)
{
u8 t,temp;
u8 enshow;
int z_temp,f_temp;
z_temp=(int)num;
//整数部分
for(t=0;t
5.4 main函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_ADC1_Init();
MX_TIM2_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_CLS();
Hcsr04Init(&htim2, TIM_CHANNEL_1); // 通道选择
Hcsr04Start(); // 启动超声波
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//显示实验对比
OLED_ShowCN_STR(30,0,0,4);
//红外测距显示
HAL_ADC_Start(&hadc1);
adcx=HAL_ADC_GetValue(&hadc1);
distance=DistanceSensor_Get_Val();
OLED_ShowCN_STR(0,2,4,5);
OLED_Showdecimal(80,2,distance,3,2,16);
//超声波测距显示
Hcsr04Start();
OLED_ShowCN_STR(0,4,9,5);
OLED_Showdecimal(80,4,Hcsr04Read(),3,2,16);
//显示单位
OLED_ShowCN_STR(30,6,14,5);
}
/* USER CODE END 3 */
}
六、实验效果
测距模块对比实验
经过本人由于拍摄不方便,但是效果还是很明显的。经过笔者反复标定测试发现,超声波HC-SR04测距的效果从反应速度,测量范围,精度等都略优于红外测距模块。而且,超声波模块比红外测距模块便宜很多,emmm,不是很理解其意义所作。当然,红外模块受外界因素的影响比超声波模块少很多,超声波模块在很多情况下是需要补偿校准的。
代码开源
链接:https://pan.baidu.com/s/1ouwFLF8ru9DROIFSZnEITg 提取码:dktp