最近在做一个 STM32 智能垃圾桶的项目,其中核心模块之一就是利用 HC-SR04 超声波模块来检测桶内垃圾的填充高度,实现自动开盖和满溢报警功能。 在选型过程中,也踩了不少坑,比如精度问题、稳定性问题等等。今天就来分享一下 HC-SR04 的原理、驱动实现,以及一些实战经验。
HC-SR04 超声波模块原理详解
HC-SR04 超声波测距模块利用的是超声波在空气中的传播速度恒定的特性(大约340m/s),通过计算超声波发射到接收的时间差来确定距离。模块主要由一个超声波发射器和一个超声波接收器组成。
其工作流程如下:
- 触发信号:给模块的 Trig 引脚至少 10us 的高电平触发信号。
- 发射超声波:模块内部的发射器发射 8 个 40kHz 的超声波脉冲。
- 接收回波:超声波遇到障碍物后反射回来,被接收器接收。
- 输出回响信号:模块的 Echo 引脚输出高电平,高电平的持续时间就是超声波从发射到接收的时间。
- 计算距离:根据公式
距离 = (高电平时间 * 声速) / 2计算出距离。 注意除以 2 是因为声波走了两个来回。
STM32 驱动 HC-SR04 的硬件连接
硬件连接非常简单,只需要将 HC-SR04 的 VCC、GND、Trig、Echo 引脚分别连接到 STM32 的 3.3V 电源、GND、一个输出 GPIO 引脚和一个输入 GPIO 引脚即可。推荐使用 STM32 的硬件定时器捕获功能来精确测量 Echo 引脚的高电平持续时间。
STM32 代码实现:定时器捕获法
下面是使用 STM32 HAL 库驱动 HC-SR04 的示例代码。这里使用定时器 TIM2 的通道 1 来捕获 Echo 引脚的高电平时间。
#include "stm32f1xx_hal.h"
// 定义 HC-SR04 的引脚
#define HCSR04_TRIG_Pin GPIO_PIN_5
#define HCSR04_TRIG_GPIO_Port GPIOA
#define HCSR04_ECHO_Pin GPIO_PIN_6
#define HCSR04_ECHO_GPIO_Port GPIOA
// 定义定时器
TIM_HandleTypeDef htim2;
// 变量
volatile uint32_t IC_Val1 = 0; // 捕获的第一个值
volatile uint32_t IC_Val2 = 0; // 捕获的第二个值
volatile uint32_t IC_Diff = 0; // 两次捕获的差值
volatile uint8_t Is_First_Captured = 0; // 第一次捕获标志
volatile float Distance = 0; // 距离
// 初始化定时器
void TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // 72MHz / 72 = 1MHz (1 us resolution)
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xffff; // 最大计数
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_IC_Init(&htim2);
}
// 发送触发信号
void HCSR04_Trigger(void)
{
HAL_GPIO_WritePin(HCSR04_TRIG_GPIO_Port, HCSR04_TRIG_Pin, GPIO_PIN_SET);
HAL_Delay(10); // 至少 10us
HAL_GPIO_WritePin(HCSR04_TRIG_GPIO_Port, HCSR04_TRIG_Pin, GPIO_PIN_RESET);
}
// 定时器中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
if (Is_First_Captured == 0) // 第一次捕获
{
IC_Val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
Is_First_Captured = 1; // 设置第一次捕获标志
// Start the second measurement
__HAL_TIM_RESET_IT(htim, TIM_IT_CC1); // 清除中断标志
}
else // 第二次捕获
{
IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if (IC_Val2 > IC_Val1)
{
IC_Diff = IC_Val2 - IC_Val1;
}
else if (IC_Val2 < IC_Val1)
{
IC_Diff = (0xffff - IC_Val1) + IC_Val2;
}
else
{
IC_Diff = 0;
}
Distance = (IC_Diff * 0.34) / 2; // 单位:mm
Is_First_Captured = 0; // 重置第一次捕获标志
}
}
}
int main(void)
{
// 初始化 HAL 库
HAL_Init();
// 初始化时钟
// ...
// 初始化 GPIO
// ...
// 初始化定时器
TIM2_Init();
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动定时器捕获中断
while (1)
{
HCSR04_Trigger(); // 发送触发信号
HAL_Delay(100); // 测量间隔
// 在这里使用 Distance 变量进行后续处理,例如控制垃圾桶的开盖
}
}
实战避坑经验
- 电源滤波:HC-SR04 对电源噪声比较敏感,建议在 VCC 引脚处添加一个 100nF 的陶瓷电容进行滤波。
- 盲区问题:HC-SR04 存在盲区,一般在 2-3cm 左右。在这个范围内,模块无法准确测量距离。在设计垃圾桶时,需要考虑这个盲区,避免垃圾桶距离传感器太近导致误判。
- 角度问题:超声波具有一定的指向性,如果垃圾桶内的垃圾堆积不均匀,可能会导致超声波反射不回来,造成测量误差。可以通过调整传感器的角度或者使用多个传感器来解决这个问题。
- 温度影响:声速受温度影响,温度升高,声速会加快。在精度要求较高的场合,需要对温度进行补偿。可以添加一个温度传感器,根据温度计算声速,然后修正距离值。
- HAL 库配置:使用 HAL 库时,务必确保定时器的时钟源配置正确,预分频系数也要根据实际情况进行调整,以获得合适的计数分辨率。如果预分频系数设置不当,会导致测量精度降低。
智能垃圾桶项目总结
通过以上步骤,我们就可以成功地驱动 HC-SR04 超声波模块,并将其应用到 STM32 智能垃圾桶项目中。 当然,这只是一个基础的实现,后续还可以加入更多的功能,例如 WIFI 联网、APP 控制、垃圾分类识别等等。希望这篇文章能够帮助到正在学习 STM32 和超声波测距的朋友们。
冠军资讯
加班到秃头