当前位置: 首页 > news >正文

超声波传感器模块

欢迎来到 破晓的历程的 博客

⛺️不负时光,不负己✈️

文章目录

    • 1.`HC-SR04`介绍
    • 2.`HC-SR04`原理介绍
      • 2.1原理概述
        • 3.2原理详解
    • 4驱动代码编写
      • 4.1写前思考
      • 4.2硬件连线
    • 5.总结
      • hcsr04.h
      • hcsr04.c

1.HC-SR04介绍

超声波传感器有很多种类的型号:HC-SR04、UC-025、UC-026、UC-015、US-100等等,但是他们都大同小异。他们的主要区别是工作参数有点不一样,像是工作的电压或者温度,探测距离或精度有点差别。引脚是一样的,都是4个引脚(us-100多了一个GND引脚),引脚的工作和作用也是一样的。

image-20250513180231393

其中我们最常用的为

image-20250513180319082

接线如下:

HC-SR04STM32备注
VCC3.3V/5V外接直流电源
Trig任意一个GPIO输入端
ECHO任意一个GPIO输出端
GNDGND接地

2.HC-SR04原理介绍

2.1原理概述

超声波测距的工作原理其实很简单,传感器发送超声波,超声波碰到障碍物反弹回来,被传感器接收到。芯片算出发送和接收的时间间隔,再利用公式:s=vxt,看下面示意图,所以实际距离=测量距离/2= 速度 x时间/2。
顺便一提,超声波在空气中的传播速度大概是 343m/,传播速度受到环境条件的影响,如温度、湿度和气压等

image-20250513182145684

超声波模块有两个超声波探头,一个是发送端,负责发送超声波,一个是接受端,负责接收超声波。

3.2原理详解

接下来我们用时序图的方式来介绍超声波发送和接收的过程以及如何计算距离的。

  • 正常测距时的时序:
image-20250513182520277
  1. 单片机会给超声波模块发送大于10us的高电平的触发信号;
  2. 超声波模块接收到触发信号后 Trig端发送8个40KHz的超声波脉冲。
  3. Echo端由低电平转为高电平,并同时开始发送超声波。
  4. 超声波模块接收到返回信号后,Echo端由高电平转为低电平。
  5. Echo的高电平宽度即为超声波发出的时间。

4驱动代码编写

明白了超声波测距的原理,我们知道了超声波测距的重点是测量超声波在空气中的时间。接下来我们来写超声波传感器的驱动代码。

4.1写前思考

我们计算差超声波往返所需时间,然后乘于超声波的速度,计算出距离,所以我们需要一个类似于秒表的东西,来测我们的时间。

所以我们可以先用定时器来做一个以微妙为单位的计时。为了方便使用,我们再封装若干个使用函数以便于我们使用这个定时器。

程序如下:

// TIM2 初始化句柄
TIM_HandleTypeDef tim2_handle;/*** @brief TIM2 定时器初始化函数*        设置定时器基本参数,并调用 HAL 库进行初始化*/
void tim2_init(void)
{// 指定定时器实例为 TIM2(即使用 TIM2 作为定时器)tim2_handle.Instance = TIM2;// 设置分频器:72-1 = 71// 如果主频为 72MHz,那么定时器时钟频率为 72MHz / 72 = 1MHz// 即定时器每计数一次所需时间为 1 微秒tim2_handle.Init.Prescaler = 72 - 1;// 设置自动重装载值(ARR):65536-1 = 65535// 当定时器计数到 65535 后溢出,重新从 0 开始// 若计数频率为 1MHz,则溢出周期为 65536 微秒(即 65.536 毫秒)tim2_handle.Init.Period = 65536 - 1;// 向上计数模式(从0计数到ARR)tim2_handle.Init.CounterMode = TIM_COUNTERMODE_UP;// 关闭自动重装载寄存器的预装载功能tim2_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;// 调用 HAL 库函数初始化定时器 TIM2HAL_TIM_Base_Init(&tim2_handle);
}/*** @brief TIM2 的 MSP(MCU Support Package)初始化函数*        一般用于配置定时器的时钟及中断* @param htim TIM句柄指针*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{// 判断当前初始化的是不是 TIM2if(htim->Instance == TIM2){// 使能 TIM2 定时器时钟__HAL_RCC_TIM2_CLK_ENABLE();}
}/*** @brief 启动 TIM2 定时器*/
void tim2_start(void)
{// 启动基础定时器,不带中断HAL_TIM_Base_Start(&tim2_handle);
}/*** @brief 停止 TIM2 定时器*/
void tim2_stop(void)
{// 停止基础定时器HAL_TIM_Base_Stop(&tim2_handle);
}/*** @brief 获取当前 TIM2 定时器计数器的值* @return 当前计数器的值(0~65535)*/
uint16_t tim2_get_cnt(void)
{// 使用 HAL 宏获取定时器当前计数器值return __HAL_TIM_GetCounter(&tim2_handle);
}/*** @brief 设置 TIM2 定时器计数器的值* @param val 要设置的计数器值*/
void tim2_set_cnt(uint16_t val)
{// 使用 HAL 宏设置定时器的当前计数器值__HAL_TIM_SetCounter(&tim2_handle, val);
}

4.2硬件连线

HC-SR04C8T6
VCCVCC
GNDGND
TrigPB6
echoPB7

因为我们涉及到对个别引脚的使用,所以我们需要对引脚进行一些简单的配置(为了方便对引脚进行修改,我们对引脚进行了一些简单的宏定义):

hcsrc04.h:

#define TRIG_PORT  GPIOB
#define TRIG_PIN   GPIO_PIN_6 
#define TRIG_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOB_CLK_ENABLE()
#define TRIG_HIGH()         HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_SET)
#define TRIG_LOW()          HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_RESET)#define ECHO_PORT GPIOB
#define ECHO_PIN  GPIO_PIN_7
#define ECHO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define ECHO_STATUS()     HAL_GPIO_ReadPin(ECHO_PORT,ECHO_PIN)

hcsrc04.c

/*** @brief 初始化 HC-SR04 超声波模块所使用的 GPIO 引脚*        Trig 为输出模式,用于发出超声波脉冲*        Echo 为输入模式,用于接收返回的超声波信号*/
void hcsr04_gpio_init(void)
{// 定义 GPIO 初始化结构体,用于配置引脚属性GPIO_InitTypeDef gpio_initstruct;// 使能 Trig 引脚所在 GPIO 端口的时钟TRIG_GPIO_CLK_ENABLE();// 使能 Echo 引脚所在 GPIO 端口的时钟ECHO_GPIO_CLK_ENABLE();/*************** 配置 Trig 引脚 ***************/// 设置 Trig 引脚号(例如 GPIO_PIN_1)gpio_initstruct.Pin = TRIG_PIN;// 设置为推挽输出模式(用于产生控制信号)gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;// 上拉电阻(防止悬空引起不稳定)gpio_initstruct.Pull = GPIO_PULLUP;// 设置 IO 速度为中速(可根据需要选择低、中、高)gpio_initstruct.Speed = GPIO_SPEED_MEDIUM;// 初始化 Trig 引脚所在端口HAL_GPIO_Init(TRIG_PORT, &gpio_initstruct);/*************** 配置 Echo 引脚 ***************/// 设置 Echo 引脚号(例如 GPIO_PIN_2)gpio_initstruct.Pin = ECHO_PIN;// 设置为输入模式(用于接收超声波返回信号)gpio_initstruct.Mode = GPIO_MODE_INPUT;// Echo 通常不需要特别配置上拉/下拉,默认即可(如需设置可加上)// gpio_initstruct.Pull = GPIO_NOPULL;// 初始化 Echo 引脚所在端口HAL_GPIO_Init(ECHO_PORT, &gpio_initstruct);
}

为了方便我们使用,我们也需要封装一个函数,这个函数名我们定义为:hcsr04_get_length,相信大家通过这个函数名就可以知道这个函数的作用,在前文中,我们介绍了超声波是如何发出的,四个 引脚的作用有什么不同。简单梳理一下:

  1. 单片机给Trig引脚至少10us长的一个高电平。
  2. ECHO引脚从低电平到高电平,表示开始发送波;波发出的那一刻,开启软件定时器(开始计时)。
  3. ECHO引脚,由高电平转为低电平,表示波回来了,此时我们停止计时器,计算经过时长。
  4. 获取经过的时间,并根据计算公式算出距离。

基于此,我们写出:

/*** @brief 获取 HC-SR04 超声波测距模块测得的距离(单位:厘米)* @return 距离值(cm)*/
float hcsr04_get_length(void)
{// 用于保存 Echo 信号持续的时间(单位:微秒)uint16_t totol_time = 0;// 用于保存最终计算出的距离值(单位:厘米)float distance = 0;/*************** 第1步:发送Trig脉冲 ***************/// 给 Trig 引脚发送一个 10 微秒以上的高电平脉冲,触发超声波发射TRIG_HIGH();       // 设置 Trig 引脚为高电平delay_us(15);      // 延迟至少 10us,这里用 15us 更加保险TRIG_LOW();        // 设置 Trig 引脚为低电平,发送完成/*************** 第2步:等待 Echo 信号拉高(开始接收) ***************/// 当 Echo 引脚由低电平变为高电平时,表示超声波已经发射出去// 此时开始计时while(ECHO_STATUS() == GPIO_PIN_RESET);  // 等待 Echo 变为高电平tim2_start();        // 启动定时器tim2_set_cnt(0);     // 将定时器计数清零/*************** 第3步:等待 Echo 信号拉低(接收到回波) ***************/// 当 Echo 引脚由高电平变为低电平时,表示回波已经接收完成// 此时停止计时while(ECHO_STATUS() == GPIO_PIN_SET);    // 等待 Echo 变为低电平tim2_stop();         // 停止定时器/*************** 第4步:获取 Echo 高电平持续时间 ***************/// 获取定时器计数值(单位是 us,取决于定时器配置)totol_time = tim2_get_cnt();/*************** 第5步:根据声速计算距离 ***************/// 声速约为 34300 cm/s,即 0.0343 cm/us// 因为往返距离,所以除以 2// distance = (totol_time × 0.0343) / 2 ≈ totol_time * 0.01715distance = totol_time * 0.01715f;// 返回测得的距离(单位:cm)return distance;
}

5.总结

本文章中,我们介绍了超声波传感器的原理,并以其中较为经典的 HC-SR04为例,给出了驱动代码。现将完整代码粘贴如下:

hcsr04.h

#ifndef __HCSR04_H__#define __HCSR04_H__#define TRIG_PORT  GPIOB
#define TRIG_PIN   GPIO_PIN_6 
#define TRIG_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOB_CLK_ENABLE()
#define TRIG_HIGH()         HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_SET)
#define TRIG_LOW()          HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_RESET)#define ECHO_PORT GPIOB
#define ECHO_PIN  GPIO_PIN_7
#define ECHO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define ECHO_STATUS()     HAL_GPIO_ReadPin(ECHO_PORT,ECHO_PIN)
void hcsr04_init(void);
float hcsr04_get_length(void);
#endif 

hcsr04.c

#include "hcsr04.h"
#include "sys.h"
#include "delay.h"
TIM_HandleTypeDef tim2_handle = {0};//定时器初始化函数
void tim2_init(void)
{tim2_handle.Instance = TIM2;tim2_handle.Init.Prescaler = 72-1;tim2_handle.Init.Period = 65536-1;tim2_handle.Init.CounterMode = TIM_COUNTERMODE_UP;tim2_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_Base_Init(&tim2_handle);}//msp函数
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){__HAL_RCC_TIM2_CLK_ENABLE();}
}void tim2_start(void)
{HAL_TIM_Base_Start(&tim2_handle);
}
void tim2_stop(void)
{HAL_TIM_Base_Stop(&tim2_handle);
}
uint16_t tim2_get_cnt(void)
{return __HAL_TIM_GetCounter(&tim2_handle);
}
void tim2_set_cnt(uint16_t val)
{__HAL_TIM_SetCounter(&tim2_handle,val); 
}/******************************************************** */
/*初始化超声波传感器*/
void hcsr04_gpio_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟TRIG_GPIO_CLK_ENABLE();ECHO_GPIO_CLK_ENABLE();gpio_initstruct.Pin=TRIG_PIN;gpio_initstruct.Mode=GPIO_MODE_OUTPUT_PP;gpio_initstruct.Pull=GPIO_PULLUP;gpio_initstruct.Speed=GPIO_SPEED_MEDIUM;HAL_GPIO_Init(TRIG_PORT,&gpio_initstruct);gpio_initstruct.Pin=ECHO_PIN;gpio_initstruct.Mode=GPIO_MODE_INPUT;HAL_GPIO_Init(ECHO_PORT,&gpio_initstruct);
}
void hcsr04_init(void)
{tim2_init();hcsr04_gpio_init();}
float hcsr04_get_length(void)
{uint16_t totol_time=0;float distance=0;//1Trig引脚,给Trig引脚至少10us的高电平TRIG_HIGH();delay_us(15);TRIG_LOW();//2ECHO引脚,由低电平跳转到高电平,表示开始发送波,波发出去的那一下,开启定时器while(ECHO_STATUS()==GPIO_PIN_RESET);tim2_start();tim2_set_cnt(0);//3ECHO,由高电平条转低电平,表示波回来了,波回来的那一下,我们开始停止定时器,计算出中间经过//多长时间while(ECHO_STATUS()==GPIO_PIN_SET);tim2_stop();//4.计算出中间经过的时间totol_time=tim2_get_cnt();//5.距离=速度(343m/s)*时间/2;distance=totol_time*0.01715;return distance;}

相关文章:

  • llamafactory-记录一次消除模型随机性的成功过程
  • ElasticSearch深入解析(十一):分页
  • coze平台实现文生视频和图生视频(阿里云版)工作流
  • C++标准流详解:cin/cout的绑定机制与cerr/clog的缓冲差异
  • Landsat 5介绍
  • [超级简单]讲解如何用PHP实现LINE Pay API!
  • ELF文件详解
  • 解密企业级大模型智能体Agentic AI 关键技术:MCP、A2A、Reasoning LLMs- GPT源代码解析
  • 笔试强训(十七)
  • JMeter同步定时器 模拟多用户并发访问场景
  • Google Earth Engine(GEE) 代码详解:批量计算_年 NDVI 并导出(附 Landsat 8 数据处理全流程)
  • Matlab基于SSA-MVMD麻雀算法优化多元变分模态分解
  • Python爬虫实战:研究JavaScript 环境补全逆向解密
  • 大模型MCP_MCP从流式SSE到流式HTTP_1.8.0支持流式HTTP交互_介绍_从应用到最优--人工智能工作笔记0245
  • 黑马Java跟学.最新AI+若依框架项目开发(一)
  • 手搓传染病模型(SEIARW)
  • AcroForm 格式化文本(域)字段
  • HttpSession 的运行原理
  • 2025-05-13 学习记录--Python-循环:while循环 + for循环 + 循环控制
  • Flannel vxlan模式的优缺点
  • A股午后拉升,沪指收复3400点:大金融发力,两市成交超1.3万亿元
  • 国务院办公厅印发《国务院2025年度立法工作计划》
  • 受美关税影响,本田预计新财年净利下降七成,并推迟加拿大建厂计划
  • “75万买299元路由器”事件进展:重庆市纪委等三部门联合介入调查
  • 牧原股份子公司与养殖户种猪买卖纠纷案一审胜诉
  • 在地球另一端的交流,架起2万公里间更多共赢的桥梁