拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于STM32与HC-SR04的便携式超声波测距仪设计与实现

1. 项目概述打造你的便携式测距仪最近在整理工作室的物料翻出来几个闲置的HC-SR04超声波模块和一块1602字符型LCD屏想着不能浪费就顺手搭了一个便携式的距离测量小工具。这东西别看简单从传感器原理、单片机选型、到LCD驱动和电源管理每一步都有不少可以琢磨的地方。它非常适合用来测量一些非接触的距离比如检查小车离墙还有多远或者DIY一个简易的防撞提醒装置。无论你是刚接触单片机的新手还是想找个周末小项目练练手的爱好者跟着这个思路走一遍不仅能做出一个实用的工具更能把超声波测距和显示驱动的核心流程彻底搞明白。整个项目的核心就是利用HC-SR04超声波传感器发射并接收声波通过计算时间差来得到距离然后将这个距离值实时显示在LCD屏幕上并且做成一个可以拿在手里、用电池供电的独立设备。下面我就把从构思到实现的完整过程包括硬件连接、代码编写、调试技巧以及如何让它真正“便携”起来的细节毫无保留地分享出来。2. 核心硬件选型与电路设计思路2.1 传感器模块HC-SR04深度解析HC-SR04几乎是电子爱好者入门测距的首选成本低、资料多。它的工作原理是典型的“发射-接收-计时”模式。模块上有四个引脚VCC、Trig触发、Echo回响和GND。其工作流程是这样的我们需要给Trig引脚一个至少10微秒的高电平脉冲这个脉冲会触发模块内部的超声波发射器发出一串8个40kHz的超声波。发射完成后模块会自动将Echo引脚拉高。当超声波遇到障碍物反射回来并被接收器捕捉到后Echo引脚才会被拉低。因此Echo引脚高电平的持续时间就是超声波从发射到返回所经历的时间。这里有一个关键的计算公式距离 (高电平时间 * 声速) / 2。声速在常温下可以取340米/秒但更精确一点可以按344米/秒25°C时计算。除以2是因为声音走了一个来回。假设我们测到的高电平时间是5800微秒即5.8毫秒那么距离 (0.0058秒 * 344 m/s) / 2 ≈ 0.9976米差不多就是1米。注意HC-SR04的测量范围官方标称是2cm到400cm但实际使用中对于太近的物体小于2cm回波可能会和发射波重叠导致无法识别对于太远或表面吸音的材料如棉布、海绵可能收不到回波。模块的测量角度大约为15度这意味着它探测的是一个锥形区域而不是一个点。2.2 显示单元1602 LCD屏的驱动方式我们选用的是经典的1602液晶屏即2行16字符。驱动它主要有两种方式8位并行和4位并行。为了节省单片机的IO口我们通常选择4位模式。1602屏本身是一个“慢速”设备它内部有自己的控制器和显示RAM。我们单片机需要做的就是按照特定的时序通过几条数据线DB4-DB7和指令/数据选择线RS、读写选择线R/W、使能线E来与它通信告诉它要显示什么字符。每个字符都有对应的ASCII码我们只需要发送这个码值屏就会从自带的字库里找到对应的点阵图形显示出来。在便携设备中我们需要关注LCD的背光功耗。很多1602屏的背光是通过一个限流电阻直接接在VCC上的这意味着只要上电背光就一直亮着比较耗电。一个优化的做法是将背光的阳极通常标为A或LED通过一个三极管或MOS管来控制单片机用一个IO口来控制其开关在不需要看的时候关闭背光能显著延长电池续航。2.3 主控与电源方案抉择对于这个项目主控的选择非常灵活。你可以用最基础的51单片机如STC89C52也可以用更强大的STM32如STM32F103C8T6甚至是用Arduino Uno来快速原型验证。它们的主要区别在于开发环境、代码复杂度和性能冗余。Arduino方案最快捷。有现成的NewPing和LiquidCrystal库几十行代码就能跑起来。适合纯粹想实现功能、快速上手的初学者。51单片机方案需要自己用C语言编写延时函数来模拟时序驱动HC-SR04和LCD。这是理解底层时序的绝佳练习但对定时器中断的精度有要求。STM32方案性能最强。可以使用硬件定时器精准捕获Echo高电平时间用SPI或I2C接口的LCD模块可以进一步节省IO。但开发难度稍高适合想深入学习ARM架构或项目后续可能扩展功能如无线传输、数据记录的开发者。我个人的选择与理由为了平衡学习深度和便携性需求我这次选择了STM32系列中资源适中且性价比高的型号。原因有三第一其硬件定时器可以轻松实现微秒级精度的脉冲时间测量比软件延时更可靠第二丰富的IO口和通信接口为未来升级留足空间第三其低功耗模式在搭配合理的电源管理时能让设备待机更久。电源是便携设备的生命线。我们不可能一直连着USB线。常见的方案有9V方块电池 线性稳压器如LM7805简单但线性稳压效率低大部分能量以热量耗散电池利用率不高。3节AAA/AA电池4.5V电压适合很多单片机工作范围3.3V-5V但需要关注电压跌落。当电池电量下降后可能导致单片机复位或LCD显示异常。单节锂电池3.7V 升压稳压模块这是我最推荐的方案。锂电池能量密度高充电方便。通过一个高效的DC-DC升压模块如基于TP4056充电和MT3608升压的模块套装可以稳定输出5V或3.3V效率通常超过85%能最大程度利用电池电量。记得在电源入口加一个开关彻底断电。3. 系统搭建与核心代码实现3.1 硬件连接图与布线要点我们先以最通用的5V单片机系统为例列出核心连接关系元件引脚连接至单片机引脚说明HC-SR04VCC5VTrigPA1 (示例)输出触发信号EchoPA2 (示例)输入捕获高电平时间GNDGND1602 LCDVSSGND电源地VDD5V电源正VO电位器中端对比度调节接可调电阻RSPB0寄存器选择 (高:数据, 低:指令)R/WGND接地始终写入模式EPB1使能信号DB4PB4数据位4DB5PB5数据位5DB6PB6数据位6DB7PB7数据位7A (LED)通过三极管控制背光正极可接限流电阻K (LED-)GND背光负极布线实操心得电源去耦务必在HC-SR04和单片机的VCC与GND之间就近焊接一个0.1uF的陶瓷电容和一个10uF的电解电容。超声波模块在发射瞬间电流较大可能引起电源电压抖动导致单片机复位。加上去耦电容能有效平滑电源。Echo引脚上拉虽然HC-SR04的Echo输出能力尚可但为了确保稳定最好在单片机端的Echo引脚上接一个4.7kΩ或10kΩ的上拉电阻到VCC避免悬空时的误触发。对比度调节LCD的VO引脚是对比度调节端通常通过一个10kΩ的可调电阻接在VCC和GND之间从中点引出接到VO。第一次上电如果看不到字符首先调节这个电位器。3.2 软件驱动从定时器捕获到LCD显示软件部分可以分为三个核心模块超声波测距驱动、距离计算与滤波、LCD显示驱动。3.2.1 超声波测距的精准定时以STM32为例利用其输入捕获功能可以非常优雅地测量Echo高电平时间。// 伪代码流程示意 1. 初始化一个定时器如TIM2将其时钟源设置为内部时钟预分频后达到1MHz的频率即1个计数1微秒。 2. 配置与Echo引脚相连的通道如PA2对应TIM2_CH3为输入捕获模式上升沿触发。 3. 在Trig引脚发出10us高电平触发信号后开启定时器并清零计数器。 4. 当捕获到上升沿时在中断中记录当前计数器值T1并将捕获边沿改为下降沿。 5. 当捕获到下降沿时记录计数器值T2。高电平时间 (T2 - T1) 微秒。 6. 如果长时间未捕获到下降沿超过一定阈值对应最大测量距离则产生超时错误。这种硬件捕获的方式不占用CPU资源精度远高于软件循环等待。3.2.2 距离计算与软件滤波得到时间t单位微秒后计算距离distance_cm// 声速按340m/s计算单位转换34000 cm/s - 0.034 cm/微秒 // 距离 (时间 * 声速) / 2 float distance_cm (t * 0.034) / 2.0; // 简化后为 t * 0.017然而单次测量很容易受到环境噪声、测量对象表面特性影响导致数值跳动。因此必须加入滤波算法。对于这种实时显示一个简单有效的办法是移动平均滤波。#define FILTER_LEN 5 static float distance_buf[FILTER_LEN] {0}; static uint8_t buf_index 0; // 每次得到新距离new_dist后 distance_buf[buf_index] new_dist; buf_index (buf_index 1) % FILTER_LEN; // 计算平均值 float filtered_dist 0; for(int i0; iFILTER_LEN; i) { filtered_dist distance_buf[i]; } filtered_dist / FILTER_LEN;这样显示出来的距离值就会平滑很多不会频繁跳动。3.2.3 LCD 1602的4线驱动编写驱动LCD的本质就是模拟其时序。这里给出4位模式初始化和写一个字符的关键函数示意// 定义引脚 #define LCD_RS_PIN PB0 #define LCD_E_PIN PB1 #define LCD_D4_PIN PB4 // ... 类似定义D5, D6, D7 // 发送半个字节高4位或低4位到数据线 void LCD_SendHalfByte(uint8_t data) { // 根据data的位0-3设置D4-D7引脚的电平 HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_PIN, (data 0x01)? GPIO_PIN_SET:GPIO_PIN_RESET); // ... 类似设置D5, D6, D7 // 产生一个使能信号E的下降沿先拉高延时再拉低 HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_PIN, GPIO_PIN_SET); Delay_us(1); // 保持时间通常450ns HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_PIN, GPIO_PIN_RESET); Delay_us(1); } // 发送一个字节指令或数据 void LCD_SendByte(uint8_t data, uint8_t isData) { HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_PIN, isData? GPIO_PIN_SET:GPIO_PIN_RESET); // RS选择 LCD_SendHalfByte(data 4); // 先发高4位 LCD_SendHalfByte(data 0x0F); // 再发低4位 Delay_us(50); // 等待指令执行完成比忙检测简单 } // 初始化序列必须严格按照数据手册 void LCD_Init(void) { Delay_ms(50); // 上电等待 // 三次特殊的8位模式设置确保进入4位模式 LCD_SendHalfByte(0x03); Delay_ms(5); LCD_SendHalfByte(0x03); Delay_us(150); LCD_SendHalfByte(0x03); LCD_SendHalfByte(0x02); // 从此进入4位模式 // 后续发送标准初始化指令功能设置、显示开关、清屏等 LCD_SendByte(0x28, 0); // 4位2行5x8点阵 LCD_SendByte(0x0C, 0); // 开显示关光标不闪烁 LCD_SendByte(0x06, 0); // 写入后光标右移 LCD_SendByte(0x01, 0); // 清屏 Delay_ms(2); }初始化成功后就可以用LCD_SendByte(A, 1)来显示字符‘A’了。3.3 主程序逻辑与低功耗考量主循环的逻辑很清晰定时触发测量 - 计算并滤波距离 - 格式化字符串 - 更新LCD显示。int main(void) { // 系统初始化时钟、GPIO、定时器、LCD等 System_Init(); LCD_Init(); LCD_SendString(Distance: ); // 显示固定标题 while(1) { // 1. 触发一次超声波测量 float dist Get_Filtered_Distance(); // 2. 格式化字符串例如 123.4 cm char disp_buf[16]; sprintf(disp_buf, %6.1f cm, dist); // 3. 将光标定位到第二行开头地址0x40 LCD_SendByte(0xC0, 0); // 4. 显示距离字符串 LCD_SendString(disp_buf); // 5. 延时一段时间控制刷新率如200ms HAL_Delay(200); } }关于低功耗如果希望设备更省电可以在主循环的HAL_Delay(200)期间让单片机进入睡眠模式Sleep或Stop模式通过一个定时器中断来唤醒而不是单纯空转。同时可以增加一个按键检测长时间无操作后自动关闭LCD背光甚至整个显示。4. 组装调试与性能优化实录4.1 从面包板到成品结构组装建议调试阶段在面包板上进行非常方便。但要做成“便携式”就需要一个外壳。你可以使用3D打印一个定制外壳或者找一个大小合适的塑料盒如旧的手机充电宝盒子进行改造。组装步骤固定核心板将单片机最小系统板或开发板用螺丝或热熔胶固定在盒子底部。安装传感器在盒子前方开一个圆孔将HC-SR04的超声波收发面露出来。务必确保传感器表面与外壳开口平齐且前方没有塑料挡片否则会严重影响声波传播。可以用热熔胶从内部固定传感器。安装LCD在盒子上盖开一个矩形窗将LCD从内侧安装用卡槽或胶水固定。注意LCD的视角确保使用时能看清。布局与走线将电池、升压模块等合理安排在剩余空间。所有连接尽量使用杜邦线焊接而不是插接以提高可靠性。用扎带或胶固定线束避免内部短路。开孔在侧面或背面为电源开关、充电接口如果用锂电池开孔。4.2 校准与精度提升实战出厂设置下测距精度可能受温度影响。声速v会随温度T摄氏度变化近似公式为v 331.4 0.6 * T(m/s)。如果你对精度要求较高可以加入一个温度传感器如DS18B20进行实时声速补偿。float get_speed_of_sound(float temperature_c) { return 331.4 0.6 * temperature_c; // 单位: m/s } float calculate_distance_cm(uint32_t echo_time_us, float temperature_c) { float speed_m_per_s get_speed_of_sound(temperature_c); float speed_cm_per_us speed_m_per_s * 100.0 / 1000000.0; // 转换为 cm/微秒 return (echo_time_us * speed_cm_per_us) / 2.0; }此外可以用一把标准卷尺在多个已知距离点如10cm, 50cm, 100cm, 200cm测量记录下单片机读出的原始时间值或计算距离。如果发现存在固定的线性误差比如所有读数都偏大2%可以在最终计算公式里乘以一个校准系数进行修正。4.3 常见问题排查与解决在制作过程中你几乎一定会遇到下面这些问题现象可能原因排查与解决方法LCD白屏或全黑电源未接通对比度调节不当背光问题1. 检查VCC和GND。2. 仔细调节VO引脚上的电位器。3. 检查背光引脚A/K是否接对背光限流电阻是否烧毁。LCD显示乱码初始化时序不对数据线接触不良供电电压不稳1. 检查初始化代码尤其是上电后的延时和三次8位设置序列。2. 按压或重新连接数据线。3. 用万用表测量给LCD供电的电压是否稳定在5V。测距值始终为0或极大HC-SR04未工作Echo信号未捕获到1. 用示波器或逻辑分析仪查看Trig和Echo引脚波形。确认Trig有10us脉冲Echo是否有高电平输出。2. 检查Echo引脚连接和上拉电阻。3. 确保测量对象在量程内且表面平整。数值跳动剧烈电源噪声环境干扰未滤波1. 加强电源去耦靠近模块加电容。2. 让传感器远离电机、继电器等噪声源。3. 在软件中实现如前所述的移动平均滤波。测量距离明显不准声速未校准传感器前方有遮挡1. 进行温度补偿或手动校准。2. 清洁传感器表面确保开口无遮挡。设备耗电快背光常亮单片机未进入低功耗模式1. 增加背光开关控制。2. 在测量间隔让MCU进入休眠。3. 检查是否有短路或元件异常发热。一个关键的调试技巧如果没有示波器可以用一个简单的办法判断HC-SR04是否工作在触发后用万用表的电压档测量Echo引脚对地电压。如果能看到一个从0V跳变到5V并持续一段时间再跳回0V的过程说明传感器工作基本正常。那个高电平的持续时间用万用表可能无法精确读取但可以定性观察。5. 功能扩展与进阶玩法基础功能实现后这个便携式测距仪的平台还有很多可以挖掘的潜力。5.1 增加测量模式单位切换通过一个按键在厘米、英寸、米之间循环切换显示单位。最大值/最小值保持记录一次测量过程中的最远和最近距离并显示出来适合测量不规则物体的厚度或深度。连续测量与阈值报警当距离小于某个设定值时让一个LED灯闪烁或蜂鸣器鸣叫实现简易的防撞或 proximity 报警功能。5.2 升级显示与交互更换显示模块将1602 LCD换成OLED屏I2C或SPI接口可以显示更丰富的图形比如一个模拟的距离条。这需要重写显示驱动但IO占用更少。增加用户输入加入旋转编码器或按键用来设置报警阈值、切换模式等使设备可交互性更强。5.3 数据记录与输出添加存储加入一个Micro SD卡模块可以将带有时间戳的距离数据记录到txt文件中用于后期分析。增加通信接口加入蓝牙模块如HC-05或Wi-Fi模块如ESP-01S将实时测量数据无线发送到手机APP或电脑上位机实现远程监控。5.4 提升便携性与专业性设计专用PCB将单片机、传感器接口、电源管理、LCD接口全部集成到一块自己设计的PCB上可以大幅缩小体积提高可靠性。这是从“项目”走向“产品”的关键一步。优化电源管理设计完整的锂电池充电、升压、电量指示电路并编写软件实现低电量预警和自动关机。我个人在完成第一个可用的原型后最深刻的体会是硬件项目的稳定性一半靠电路一半靠软件滤波和异常处理。最初我的读数总是偶尔跳变加了硬件滤波电容效果也不明显最后是在软件里加入了“野值剔除”连续采样多次去掉一个最大值和一个最小值再平均和“超时判断”显示才真正稳定下来。另一个收获是关于电源的一开始我用的是线性稳压设备发热明显后来换成了开关电源方案的升压模块不仅不热了同一批电池的续航时间几乎翻了一倍。所以在动手之前多花点时间在方案选型和细节思考上往往能事半功倍。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门