1. 项目概述与硬件准备
超声波测距作为嵌入式系统中常见的非接触式距离检测方案,在智能小车避障、工业测距等领域应用广泛。HC-SR04模块以其性价比高、接口简单著称,配合STM32的定时器输入捕获功能,可以实现高精度的距离测量。本文将基于STM32F103C8T6(蓝桥杯常用开发板)和HAL库,详细讲解从工程配置到代码实现的完整流程。
1.1 硬件连接要点
HC-SR04模块共有四个引脚:
- VCC:接5V电源(注意部分STM32开发板的3.3V可能驱动不足)
- GND:接地
- Trig:触发信号输入(接STM32的GPIO输出)
- Echo:回响信号输出(接STM32的定时器输入捕获通道)
特别注意:Echo引脚输出的是5V电平信号,而STM32的GPIO通常只耐受3.3V。若直接连接可能损坏芯片,建议:
- 使用电阻分压电路(如1kΩ+2kΩ)
- 或选用支持5V输入的STM32型号(如STM32F103ZET6)
1.2 定时器选型策略
选择定时器2(TIM2)的原因:
- 通用定时器资源丰富,不会占用高级定时器(TIM1/TIM8)
- 支持输入捕获功能且中断优先级可配置
- 在72MHz系统时钟下,预分频设置为71(72-1)时,计数周期正好为1μs,方便时间计算
2. STM32CubeMX工程配置详解
2.1 时钟树配置关键步骤
- 在RCC配置中选择HSE(外部高速晶振)
- 在Clock Configuration界面:
- 输入72并回车
- 确保APB1和APB2总线时钟均为72MHz
- 检查HCLK、PCLK1、PCLK2的时钟显示为72MHz
常见问题:若时钟配置错误会导致定时器计时不准。建议使用示波器验证定时器输出是否与预期一致。
2.2 定时器参数设置
TIM2配置要点:
| 参数项 | 设置值 | 物理意义 |
|---|---|---|
| Clock Source | Internal Clock | 使用内部时钟源 |
| Prescaler | 71 | 72分频→1MHz计数频率 |
| Counter Mode | Up | 向上计数模式 |
| AutoReload | 0xFFFF | 最大计数值(65535μs) |
| CH1 Mode | Input Capture | 输入捕获模式 |
输入捕获配置:
- Polarity Selection:Rising Edge(初始设置为上升沿捕获)
- IC Selection:Direct(直接模式,不经过滤波)
- IC Prescaler:DIV1(每个边沿都捕获)
2.3 GPIO配置技巧
-
Trig引脚(PA5)配置:
- Mode:GPIO_Output
- Output Level:Low
- Pull-up/Pull-down:No pull
- 建议在Label中命名为"Trig"方便代码引用
-
Echo引脚(PA0)配置:
- 通过TIM2_CH1自动配置,无需单独设置GPIO模式
- 可在System Core→GPIO中设置上拉电阻增强稳定性
3. HAL库代码实现解析
3.1 中断回调机制实现
输入捕获的核心逻辑在HAL_TIM_IC_CaptureCallback中实现:
c复制void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim == &htim2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
if (!echo_flag) { // 上升沿捕获
rising_cnt = __HAL_TIM_GET_COMPARE(htim, TIM_CHANNEL_1);
echo_flag = 1;
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1,
TIM_INPUTCHANNELPOLARITY_FALLING);
} else { // 下降沿捕获
falling_cnt = __HAL_TIM_GET_COMPARE(htim, TIM_CHANNEL_1);
echo_flag = 0;
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1,
TIM_INPUTCHANNELPOLARITY_RISING);
// 可在此处直接计算距离,避免主循环延迟
}
}
}
3.2 距离计算优化方案
原始计算公式(falling_cnt - rising_cnt)*0.017的物理含义:
- 声速340m/s = 34000cm/s = 0.034cm/μs
- 往返距离需除以2:0.017cm/μs
改进方案:
-
加入温度补偿(声速随温度变化):
c复制float temperature = 25.0; // 实际应读取温度传感器 float speed_of_sound = 331.4 + 0.6 * temperature; // m/s distance = (falling_cnt - rising_cnt) * (speed_of_sound / 20000.0); -
多次测量取中值滤波:
c复制#define SAMPLE_NUM 5 uint16_t get_median_distance() { uint16_t samples[SAMPLE_NUM]; for(int i=0; i<SAMPLE_NUM; i++){ samples[i] = distance_read(); HAL_Delay(50); // 防止超声波回波干扰 } // 排序算法省略... return samples[SAMPLE_NUM/2]; }
4. 实战调试技巧与问题排查
4.1 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 测量值始终为0 | Trig信号宽度不足 | 确保触发脉冲≥10μs |
| 测量值波动大 | 电源噪声/物体表面吸收 | 增加滤波电容/使用平整反射面 |
| 返回超大数值 | 超时未收到回波 | 设置超时判断逻辑 |
| 定时器计数不准确 | 时钟配置错误 | 检查APB1时钟和预分频设置 |
4.2 示波器调试技巧
-
双通道观察:
- CH1:Trig引脚(应看到10μs脉冲)
- CH2:Echo引脚(应看到与距离成正比的高电平)
-
关键参数测量:
- 触发脉冲实际宽度(要求≥10μs)
- Echo信号上升/下降时间(应<1μs)
- 信号幅值(需确认在MCU可接受范围)
-
异常波形分析:
- 回波信号抖动→检查电源稳定性
- 无回波信号→检查模块是否正常工作
- 信号幅值不足→检查电平转换电路
5. 性能优化与扩展应用
5.1 低功耗设计方案
- 间歇工作模式:
c复制void enter_low_power_mode() {
HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_1);
HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_RESET);
// 配置MCU进入低功耗模式
}
void wakeup_measure() {
// 唤醒外设
distance_init();
uint16_t d = distance_read();
// 处理数据后再次休眠
}
5.2 多模块协同工作
当需要同时使用多个超声波模块时:
-
分时复用方案:
- 为每个模块分配独立的Trig引脚
- 共享同一个定时器的不同通道(如TIM2_CH1/CH2)
- 依次触发并记录各模块的Echo时间
-
硬件优化:
- 使用74HC138等译码器减少GPIO占用
- 采用模拟开关切换Echo信号
c复制void multi_module_read() {
// 模块1测量
HAL_GPIO_WritePin(Trig1_GPIO_Port, Trig1_Pin, GPIO_PIN_SET);
delay_us(11);
HAL_GPIO_WritePin(Trig1_GPIO_Port, Trig1_Pin, GPIO_PIN_RESET);
// 模块2测量(需间隔足够时间)
HAL_Delay(100);
HAL_GPIO_WritePin(Trig2_GPIO_Port, Trig2_Pin, GPIO_PIN_SET);
// ...后续处理
}
在实际项目中,超声波测距模块的稳定性往往取决于细节处理。通过合理设置定时器参数、添加软件滤波算法、优化硬件电路设计,可以将测量误差控制在1cm以内。建议在正式应用前进行充分的温度和环境测试,特别是对于户外或工业场景的应用。