HC-SR04超声波测距模块驱动与精度优化实战指南
👁 8 阅读 · 2026-08-21 · 嵌入式 传感器
HC-SR04作为最常用的超声波测距传感器,在嵌入式项目中广泛应用。本文深入剖析其工作原理,从时序细节到驱动代码实现,再到多维度精度优化策略(温度补偿、触发方式、滤波算法等),帮助开发者规避常见陷阱,提升测距稳定性和准确性。适合有一定单片机基础的工程师快速上手并进阶。
## 一、HC-SR04工作原理与关键时序
HC-SR04采用IO触发-回波测量方式,内部集成超声波发射器、接收器和控制电路。其测距本质是测量超声波从发射到遇障碍物返回的时间差,再乘以声速除以2得到距离。
**核心时序要求:**
- 触发信号:至少10μs的高电平脉冲(建议20μs确保稳定)
- 回波信号:高电平持续时间与距离成正比
- 测量范围:2cm~400cm,精度理论可达3mm
- 声速:常温(20℃)下约343m/s,实际受温度影响显著
距离计算公式:
```c
distance_cm = (echo_time_us * 0.0343) / 2;
// 简化:distance_cm = echo_time_us / 58.0;
```
## 二、驱动电路与引脚连接
HC-SR04工作电压5V,但回波输出为5V电平,若MCU为3.3V系统(如STM32),需进行电平转换或分压,避免损坏IO口。
**典型连接(以STM32F103为例):**
- VCC → 5V(注意电流约15mA)
- GND → GND
- Trig → PA0(推挽输出)
- Echo → PA1(输入捕获或外部中断)
**电平转换方案:**
- 使用电阻分压(如1kΩ+2kΩ)将Echo降至3.3V
- 或使用电平转换芯片(如TXS0108)
## 三、驱动代码实现(STM32 HAL库)
### 1. 使用输入捕获模式测量脉宽
```c
// 定时器配置:TIM2 CH1(PA0)输入捕获,上升沿+下降沿捕获
void MX_TIM2_Init(void) {
TIM_IC_InitTypeDef sConfigIC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1; // 1MHz计数频率,1us分辨率
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFF; // 最大65535us,约1.1m
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_IC_Init(&htim2);
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0; // 不滤波
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_CC1);
}
// 中断回调中测量脉宽
volatile uint32_t echo_start, echo_end, echo_width;
volatile uint8_t echo_captured = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1)) {
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET) {
// 上升沿
echo_start = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
} else {
// 下降沿
echo_end = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
echo_width = echo_end > echo_start ? (echo_end - echo_start) : (0xFFFF - echo_start + echo_end);
echo_captured = 1;
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
}
}
}
}
}
// 触发一次测量
void HCSR04_Trigger(void) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
delay_us(20);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
}
// 获取距离(单位cm)
uint16_t HCSR04_GetDistance(void) {
if (echo_captured) {
echo_captured = 0;
return (uint16_t)(echo_width / 58.0);
}
return 0xFFFF; // 超时或未捕获
}
```
### 2. 使用外部中断+定时器计时(替代方案)
```c
// 外部中断配置:Echo引脚上升沿触发,开启定时器;下降沿触发,读取定时器值
void EXTI1_IRQHandler(void) {
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_1)) {
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)) {
__HAL_TIM_SET_COUNTER(&htim3, 0);
HAL_TIM_Base_Start(&htim3);
} else {
HAL_TIM_Base_Stop(&htim3);
echo_width = __HAL_TIM_GET_COUNTER(&htim3);
echo_captured = 1;
}
}
}
```
## 四、精度优化策略
### 1. 温度补偿
声速随温度变化明显:`v = 331.4 + 0.6 * T`(T为摄氏度)。若环境温度波动大,应使用温度传感器(如DS18B20)实时修正。
```c
float speed_of_sound(float temp_c) {
return 331.4f + 0.6f * temp_c; // m/s
}
uint16_t HCSR04_GetDistanceCompensated(float temp_c) {
if (echo_captured) {
echo_captured = 0;
float dist_m = (echo_width / 1e6f) * speed_of_sound(temp_c) / 2.0f;
return (uint16_t)(dist_m * 100); // cm
}
return 0xFFFF;
}
```
### 2. 多次测量与滤波
单次测量易受噪声干扰,建议连续测量5~10次,去除最大值和最小值后取平均。
```c
#define SAMPLE_COUNT 5
uint16_t HCSR04_GetDistanceFiltered(void) {
uint16_t samples[SAMPLE_COUNT];
for (int i = 0; i < SAMPLE_COUNT; i++) {
HCSR04_Trigger();
while (!echo_captured); // 等待捕获
samples[i] = HCSR04_GetDistance();
delay_ms(50); // 间隔至少60ms避免余振
}
// 冒泡排序去极值
for (int i = 0; i < SAMPLE_COUNT-1; i++) {
for (int j = 0; j < SAMPLE_COUNT-i-1; j++) {
if (samples[j] > samples[j+1]) {
uint16_t tmp = samples[j]; samples[j] = samples[j+1]; samples[j+1] = tmp;
}
}
}
uint32_t sum = 0;
for (int i = 1; i < SAMPLE_COUNT-1; i++) sum += samples[i];
return (uint16_t)(sum / (SAMPLE_COUNT-2));
}
```
### 3. 触发间隔控制
超声波模块需要等待余波消散,两次触发间隔应大于60ms(对应最大量程4m的往返时间约23ms,留足余量)。若测量距离较近,可适当缩短,但建议不低于30ms。
### 4. 测量角度与障碍物特性
- 超声波波束角约15°,障碍物表面应尽量垂直于波束方向,否则回波衰减严重。
- 软质材料(如海绵、布料)吸收超声波,导致测距失败,需考虑替代方案。
- 避免在强气流或风扇附近使用,气流会干扰声波传播。
## 五、常见问题与注意事项
- **Echo悬空问题**:若未检测到回波,Echo引脚会保持低电平,但模块内部可能输出高电平持续一段时间,需设置超时机制(如200ms)避免程序卡死。
- **电源稳定性**:HC-SR04对电源纹波敏感,建议在VCC和GND之间加100μF电解电容和0.1μF陶瓷电容。
- **多模块干扰**:多个超声波模块同时工作会互相干扰,可采用分时触发或不同频率(但HC-SR04固定40kHz,只能分时)。
- **测量盲区**:2cm以内无法可靠测量,设计时需考虑安装位置。
- **定时器溢出**:若使用16位定时器且分频后计数频率为1MHz,最大测量时间65535μs,对应距离约1.1m,超过则需使用32位定时器或扩展计数。
## 六、总结
HC-SR04虽然廉价易用,但要做到高精度测量,需从硬件电路、驱动时序、软件滤波和温度补偿等多方面综合优化。本文提供的代码框架可直接应用于STM32等主流MCU,通过输入捕获或外部中断实现精确脉宽测量。实际项目中,建议根据具体场景调整采样策略和滤波算法,并充分测试不同温度、角度下的表现。掌握这些要点,你就能在机器人避障、液位检测等应用中发挥HC-SR04的最大潜力。