news 2026/4/17 21:40:23

STM32超声波测距实战:HC-SR04模块避坑指南(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32超声波测距实战:HC-SR04模块避坑指南(附完整代码)

STM32超声波测距实战:HC-SR04模块避坑指南(附完整代码)

在智能小车和避障机器人开发中,超声波测距模块因其成本低廉、使用简单而广受欢迎。HC-SR04作为最常见的超声波模块之一,虽然原理简单,但在实际STM32项目应用中却存在不少"坑"。本文将从一个实际项目案例出发,分享我在使用STM32驱动HC-SR04模块时积累的经验和解决方案。

1. HC-SR04模块工作原理与硬件连接

HC-SR04超声波测距模块通过发射40kHz的超声波并接收回波来测量距离。其工作流程可分为三个关键阶段:

  1. 触发阶段:向Trig引脚发送至少10μs的高电平脉冲
  2. 发射接收阶段:模块自动发射8个40kHz超声波脉冲并等待回波
  3. 回波检测阶段:Echo引脚输出高电平,其持续时间与距离成正比

典型硬件连接方式

STM32引脚HC-SR04引脚说明
5VVCC电源正极
GNDGND电源地
GPIO输出TRIG触发信号输入
GPIO输入ECHO回波信号输出

注意:虽然HC-SR04标称工作电压为5V,但ECHO引脚输出信号为5V TTL电平,直接连接3.3V STM32可能损坏IO口。建议使用电平转换电路或电阻分压。

常见硬件问题排查:

  • 测量不准确:检查VCC供电是否稳定(建议使用示波器观察)
  • 无响应:确认Trig信号脉冲宽度≥10μs
  • 数据跳动:确保GND连接良好,必要时增加滤波电容

2. 三种驱动方案对比与选择

根据项目需求和资源占用情况,STM32驱动HC-SR04主要有三种实现方式:

2.1 轮询方式

// 简化示例代码 void GetDistance_Polling(void) { // 发送触发信号 HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); delay_us(20); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); // 等待回波信号 while(HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_RESET); // 开始计时 uint32_t start = TIM5->CNT; while(HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_SET); uint32_t end = TIM5->CNT; // 计算距离 float distance = (end - start) * 0.034 / 2; // 单位:cm }

适用场景:简单测试、对实时性要求不高的应用

2.2 外部中断+定时器方式

// 中断处理示例 void EXTI15_10_IRQHandler(void) { if(__HAL_GPIO_EXTI_GET_IT(ECHO_Pin) != RESET) { if(HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin)) { // 上升沿:开始计时 __HAL_TIM_SET_COUNTER(&htim3, 0); HAL_TIM_Base_Start(&htim3); } else { // 下降沿:停止计时并计算距离 HAL_TIM_Base_Stop(&htim3); uint32_t pulse = __HAL_TIM_GET_COUNTER(&htim3); g_distance = pulse * 0.034 / 2; } __HAL_GPIO_EXTI_CLEAR_IT(ECHO_Pin); } }

优势:响应及时,CPU占用率低

2.3 输入捕获方式(推荐)

// 定时器输入捕获配置 void MX_TIM2_Init(void) { htim2.Instance = TIM2; htim2.Init.Prescaler = 71; // 1MHz计数频率 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFFFFFF; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_IC_Init(&htim2); TIM_IC_InitTypeDef sConfigIC; sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); } // 获取距离 float GetDistance_InputCapture(void) { // 发送触发信号 HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); delay_us(20); HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); // 等待上升沿 while(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_CC1) == RESET); uint32_t rise = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // 配置为下降沿捕获 TIM2->CCER ^= TIM_CCER_CC1P; // 等待下降沿 while(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_CC1) == RESET); uint32_t fall = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // 恢复上升沿捕获 TIM2->CCER ^= TIM_CCER_CC1P; // 计算距离 return (fall - rise) * 0.034 / 2; }

推荐理由:精度高,硬件自动记录时间,适合精确测量

3. 精度优化与误差处理

在实际项目中,HC-SR04的测量结果常受以下因素影响:

3.1 温度补偿

声速随温度变化,修正公式:

声速 = 331.4 + 0.6×温度(℃) m/s

实现代码:

float GetDistanceWithTempComp(float temperature) { float speed = 331.4 + 0.6 * temperature; // m/s return (g_pulse_width * speed) / 20000.0; // 单位:cm }

3.2 数字滤波算法

常用滤波方法对比:

滤波算法适用场景实现复杂度效果
滑动平均平稳环境一般
中值滤波突发干扰较好
卡尔曼滤波动态环境优秀

滑动平均实现示例

#define FILTER_SIZE 5 float distance_filter[FILTER_SIZE]; uint8_t filter_index = 0; float MovingAverageFilter(float new_distance) { distance_filter[filter_index] = new_distance; filter_index = (filter_index + 1) % FILTER_SIZE; float sum = 0; for(int i = 0; i < FILTER_SIZE; i++) { sum += distance_filter[i]; } return sum / FILTER_SIZE; }

3.3 异常值处理

常见问题及解决方案:

  • 超量程读数:增加超时检测,超过400cm返回无效值
  • 多次反射干扰:设置最小有效距离(通常2cm)
  • 信号丢失:增加重试机制,连续多次失败后报错

4. 实际项目集成技巧

在智能小车等实际应用中,还需考虑以下问题:

4.1 多模块协同工作

当使用多个HC-SR04模块时:

  1. 分时触发不同模块,避免超声波互相干扰
  2. 为每个模块分配独立的定时器资源
  3. 增加模块间触发间隔(建议≥50ms)

4.2 实时显示与通信

典型数据输出格式:

// 通过串口输出JSON格式数据 void SendDistanceData(float distance) { printf("{\"sensor\":\"ultrasonic\",\"distance\":%.2f,\"unit\":\"cm\"}\r\n", distance); }

4.3 低功耗优化

对于电池供电设备:

  • 仅在需要测量时给模块供电
  • 降低采样频率(根据应用需求调整)
  • 使用DMA传输减少CPU唤醒时间

5. 完整示例代码(基于HAL库)

以下是一个经过实际项目验证的完整驱动实现:

hc_sr04.h

#ifndef __HC_SR04_H #define __HC_SR04_H #include "stm32f1xx_hal.h" #define TRIG_PIN GPIO_PIN_0 #define TRIG_PORT GPIOA #define ECHO_PIN GPIO_PIN_1 #define ECHO_PORT GPIOA void HC_SR04_Init(void); float HC_SR04_GetDistance(void); void HC_SR04_Delay_us(uint16_t us); #endif

hc_sr04.c

#include "hc_sr04.h" #include "tim.h" volatile uint32_t pulse_start = 0; volatile uint32_t pulse_end = 0; volatile uint8_t measurement_done = 0; void HC_SR04_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // TRIG引脚配置 GPIO_InitStruct.Pin = TRIG_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(TRIG_PORT, &GPIO_InitStruct); // ECHO引脚配置 GPIO_InitStruct.Pin = ECHO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(ECHO_PORT, &GPIO_InitStruct); // 配置外部中断 HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI1_IRQn); // 初始化定时器 HAL_TIM_Base_Start(&htim2); } float HC_SR04_GetDistance(void) { // 发送触发脉冲 HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); HC_SR04_Delay_us(20); HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // 等待测量完成 uint32_t timeout = 100000; // 超时计数 while(!measurement_done && timeout--); if(!measurement_done) { return -1.0f; // 超时返回错误 } measurement_done = 0; uint32_t pulse_width = pulse_end - pulse_start; // 计算距离(cm) return pulse_width * 0.034 / 2; } void EXTI1_IRQHandler(void) { if(__HAL_GPIO_EXTI_GET_IT(ECHO_PIN) != RESET) { if(HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN)) { // 上升沿 pulse_start = __HAL_TIM_GET_COUNTER(&htim2); } else { // 下降沿 pulse_end = __HAL_TIM_GET_COUNTER(&htim2); measurement_done = 1; } __HAL_GPIO_EXTI_CLEAR_IT(ECHO_PIN); } } // 简易微秒延时 void HC_SR04_Delay_us(uint16_t us) { __HAL_TIM_SET_COUNTER(&htim3, 0); while(__HAL_TIM_GET_COUNTER(&htim3) < us); }

在调试过程中发现,模块的安装位置对测量结果影响很大。最好将传感器安装在远离电机等干扰源的位置,并确保检测方向无障碍物遮挡。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 21:39:23

SukiUI深度解析:如何为AvaloniaUI构建现代化桌面应用界面

SukiUI深度解析&#xff1a;如何为AvaloniaUI构建现代化桌面应用界面 【免费下载链接】SukiUI UI Theme for AvaloniaUI 项目地址: https://gitcode.com/gh_mirrors/su/SukiUI 在当今的跨平台桌面应用开发领域&#xff0c;AvaloniaUI以其强大的渲染能力和跨平台特性成为…

作者头像 李华
网站建设 2026/4/17 21:35:10

Java方法重写完全指南:从概念到规则,一篇搞懂继承中的方法覆盖

子类继承了父类的方法&#xff0c;但如果父类的方法不满足子类的需求怎么办&#xff1f;比如第一代手机只能打电话&#xff0c;第三代智能手机需要视频通话。这时候就需要“方法重写”——子类把父类的方法重新写一遍&#xff0c;让它更符合自己的需求。今天这篇文章&#xff0…

作者头像 李华
网站建设 2026/4/17 21:34:16

企业级 AI Coding 设计规范

以上是一套完整的企业级 AI Coding 设计思路规范&#xff0c;点击每个阶段可展开详细内容。以下是各阶段的核心思路概括&#xff1a; 整体哲学&#xff1a;人机协作&#xff0c;人主机辅。 AI 负责提效&#xff0c;人负责 ownership。 8 个阶段的核心要点&#xff1a;需求建立 …

作者头像 李华
网站建设 2026/4/17 21:32:20

告别终端启动:为Ubuntu上的Pycharm创建桌面快捷方式并管理虚拟环境

高效开发者的UbuntuPycharm终极配置指南 每次打开终端输入命令启动Pycharm的日子该结束了。作为专业Python开发者&#xff0c;我们值得拥有更流畅的工作体验。本文将带你从零开始&#xff0c;在Ubuntu系统上打造一套完整的Pycharm高效工作环境&#xff0c;涵盖桌面快捷方式创建…

作者头像 李华