超声波传感器模块

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

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

文章目录

    • 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;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/80646.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Effective Python》第2章 字符串和切片操作——深入理解Python 中的字符数据类型(bytes 与 str)的差异

引言 本篇博客基于学习《Effective Python》第三版 Chapter 2: Strings and Slicing 中的 Item 10: Know the Differences Between bytes and str 的总结与延伸。在 Python 编程中,字符串处理是几乎每个开发者都会频繁接触的基础操作。然而,Python 中的…

py7zr解压文件时报错CrcError(crc32, f.crc32, f.filename)

报错信息 Traceback (most recent call last):File "/home/hp/project/test/file_util.py", line 130, in extract_archive_7zarchive.extract(targets[fixed_file], pathoutput_dir, recursiveTrue)File "/home/hp/miniconda3/envs/celery/lib/python3.10/sit…

物理:由基本粒子组成的个体能否提炼和重组?

个体差异源于基本粒子组合的复杂性与随机性,这一假设若成立,确实可能为生物医学带来革命性突破——但需要突破技术、理论与系统层级的多重壁垒。以下从科学逻辑与技术路径展开分析: 一、随机组合中的共性与稳定结构 1. 自然界的自组织规律 涌现性(Emergence):尽管粒子组…

动态路由EIGRP的配置

动态路由EIGRP的配置 动态路由EIGRP:增强内部网关协议 为何收敛快、不成环? 路由计算的无环路和路由的收敛速度是路由计算的重要指标。EIGRP协议由于使用了DUAL算法,使得EIGRP协议在路由计算中不可能有环路路由产生,同时路由计…

组合问题(多条件)

39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; class Solution { private:vector<vector<int>>result;vector<int>path;void backtracking(vector<int>& candidates, int target,int sum,int startIndex){if(sum>target){return;}if(…

SimScape物理建模实例2--带控制的单质量弹簧阻尼系统

模型下载&#xff1a; 基于simscape&#xff0c;单质量系统带位置控制资源-CSDN文库 在实例1中&#xff0c;我们搭建了不带控制的单质量弹簧阻尼系统&#xff0c;该系统没有外界力量介入&#xff0c;只有弹簧的初始弹力&#xff0c;带着弹簧使劲弹来弹去。 SimScape物理建模实…

OpenAI Text 模型与 Chat 模型调用实战指南:从基础配置到创意花店命名

在 AI 应用开发的浪潮中&#xff0c;OpenAI 的大语言模型成为开发者实现创新功能的得力工具。其中&#xff0c;Text 模型和 Chat 模型作为核心接口&#xff0c;被广泛应用于文本生成、对话交互等场景。本文将以 “为花店起名” 为实际需求&#xff0c;手把手教你如何安全调用这…

网页常见水印实现方式

文章目录 1 明水印技术实现1.1 DOM覆盖方案1.2 Canvas动态渲染1.3 CSS伪元素方案2 暗水印技术解析2.1 空域LSB算法2.2 频域傅里叶变换3 防篡改机制设计3.1 MutationObserver防护3.2 Canvas指纹追踪4 前后端实现对比5 攻防博弈深度分析5.1 常见破解手段5.2 进阶防御策略6 选型近…

现代化QML组件开发教程

现代化QML组件开发教程 目录 QML基础介绍QML项目结构基本组件详解自定义组件开发状态与过渡高级主题最佳实践 QML基础介绍 什么是QML QML (Qt Meta Language) 是一种声明式语言&#xff0c;专为用户界面设计而创建。它是Qt框架的一部分&#xff0c;让开发者能够创建流畅、…

C/C++ 程序执行的主要过程

预处理&#xff08;Preprocessing&#xff09; 任务&#xff1a; 处理源代码中以 # 开头的预处理指令&#xff0c;包括&#xff1a; 头文件包含&#xff08;#include&#xff09;&#xff1a;将头文件&#xff08;如 stdio.h&#xff09;的内容直接插入到源文件中。宏替换&…

时间序列预测建模的完整流程以及数据分析【学习记录】

文章目录 1.时间序列建模的完整流程2. 模型选取的和数据集2.1.ARIMA模型2.2.数据集介绍 3.时间序列建模3.1.数据获取3.2.处理数据中的异常值3.2.1.Nan值3.2.2.异常值的检测和处理&#xff08;Z-Score方法&#xff09; 3.3.离散度3.4.Z-Score3.4.1.概述3.4.2.公式3.4.3.Z-Score与…

ValueError: Caught ValueError in DataLoader worker process 0.

参考链接&#xff1a; https://stackoverflow.com/questions/1841565/valueerror-invalid-literal-for-int-with-base-10 它提示我有个地方值错误空字符 果然因为格式处理没有传进去东西&#xff0c;找下原因&#xff0c;让它正常处理 原来是相对路径的.影响了程序运行 将v…

JavaScript性能优化实战,从理论到落地的全面指南

在前端开发领域&#xff0c;JavaScript的性能优化是提升用户体验的核心环节。随着Web应用复杂度的提升&#xff0c;开发者面临的性能瓶颈也日益多样化。本文将从理论分析、代码实践和工具使用三个维度&#xff0c;系统性地讲解JavaScript性能优化的实战技巧&#xff0c;并通过大…

SQL、Oracle 和 SQL Server 的比较与分析

SQL、Oracle 和 SQL Server 的比较与分析 一、基础概念 1. SQL (Structured Query Language) 定义&#xff1a;结构化查询语言&#xff0c;用于管理关系型数据库的标准语言类型&#xff1a; DDL (数据定义语言)&#xff1a;CREATE, ALTER, DROPDML (数据操作语言)&#xff1…

Telnet 类图解析

Telnet 类图&#xff08;文本描述&#xff09; --------------------------------------- | Telnet | --------------------------------------- | - host: str | # 目标主机 | - port: int …

Ansible安装与核心模块实战指南

Ansible安装与核心模块实战指南 自动化运维入门:从安装到模块化任务配置 Ansible作为一款无代理自动化工具,通过模块化设计实现高效管理,尤其适用于快速部署、配置和维护大规模系统。本文将从安装、核心模块使用到实际案例,全面解析其核心功能与最佳实践。 一、Ansible安装…

VLLM推理大模型显存不够后,导致程序引擎崩溃的调优方案尝试

背景介绍 硬件 A800 80G模型 chat-glm4-9b-128K环境 生产正常显存占用情况 glm4 占用32GB 其他显存工占用38GB左右 总共剩余10GB。 问题描述 推理时报错日志&#xff0c;由于内网环境无法拿出日志&#xff0c;与下面的类似。 File "/data/miniconda3_new/envs/vllm-new…

【Nacos】env NACOS_AUTH_IDENTITY_KEY must be set.

【Nacos】env NACOS_AUTH_IDENTITY_KEY must be set. 问题描述 env NACOS_AUTH_IDENTITY_KEY must be set.原因分析 在 .env 文件中设置 Nacos 身份验证相关的所有必要环境变量。 解决方案 添加到 .env 文件中 NACOS_AUTH_IDENTITY_KEYAuthorization NACOS_AUTH_IDENTITY…

C++语法基础(下)

&#xff08;注&#xff1a;在看本文是如果感觉内容有点突兀&#xff0c;请先浏览《C语法基础&#xff08;上&#xff09;》这篇文章帮助更好理解&#xff09; 一.缺省参数 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时&#xff0c;如果没有指定实参…

力扣Hot100(Java版本)

1. 哈希 1.1 两数之和 题目描述&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同…