1. 项目概述
最近在做一个基于STM32的超声波测距项目,使用HC-SR04传感器配合STM32的输入捕获功能实现精确测距。这个项目非常适合用来练习STM32定时器的输入捕获功能,同时也能学到很多实用的嵌入式开发技巧。
HC-SR04是一款常见的超声波测距模块,价格便宜、使用简单,测距范围在2cm-400cm之间,精度可达3mm。通过这个项目,我们可以实现当检测到物体距离小于20cm时自动点亮板载LED的实用功能。
2. HC-SR04超声波传感器原理
2.1 测距原理
超声波测距的基本原理很简单:通过测量超声波从发射到接收的时间差来计算距离。具体公式为:
距离 = (声速 × 传播时间) / 2
这里除以2是因为超声波走了往返两倍的距离。在常温下,声速约为340m/s。
2.2 传感器引脚说明
HC-SR04有4个引脚:
- VCC:电源正极(5V)
- GND:电源负极
- Trig:触发信号输入
- Echo:回响信号输出
2.3 工作时序
HC-SR04的工作时序如下:
- 向Trig引脚发送至少10μs的高电平脉冲
- 模块自动发送8个40kHz的超声波脉冲
- Echo引脚会输出一个高电平脉冲,其宽度与距离成正比
- 通过测量Echo引脚高电平的持续时间即可计算距离
3. 硬件连接
3.1 电路连接图
将HC-SR04与STM32连接如下:
- VCC → 5V
- GND → GND
- Trig → PA0
- Echo → PA1
板载LED连接在PC13引脚上。
3.2 注意事项
- HC-SR04的工作电压是5V,但Echo输出也是5V电平,需要确保STM32的PA1引脚能承受5V输入
- 如果STM32是3.3V系统,建议在Echo信号线上加一个分压电阻
- 超声波传感器对安装位置很敏感,要确保传感器前方没有遮挡
4. STM32CubeMX配置
4.1 创建新工程
- 打开STM32CubeMX,新建工程
- 选择STM32F103C8T6芯片
- 配置调试接口为Serial Wire(SWD模式)
4.2 GPIO配置
- 配置PA0为GPIO输出(Trig引脚)
- 配置PC13为GPIO输出(板载LED)
- PA1保持默认(后续用于输入捕获)
4.3 定时器配置
使用TIM1的输入捕获功能:
- 通道1配置为输入捕获,上升沿触发
- 通道2配置为输入捕获,下降沿触发
- 预分频器设置为71,使定时器时钟为1MHz(1μs分辨率)
- 自动重装载值设为65535(16位最大值)
4.4 时钟配置
系统时钟配置为72MHz,定时器时钟为72MHz,经过预分频后为1MHz。
5. 代码实现
5.1 初始化代码
在main.c中添加以下初始化代码:
c复制// 初始化定时器
HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_2);
5.2 测距流程
完整的测距流程如下:
- 复位定时器计数器
c复制__HAL_TIM_SET_COUNTER(&htim1, 0);
- 清除捕获标志位
c复制__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_CC1);
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_CC2);
- 发送Trig脉冲
c复制HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
HAL_Delay(1); // 10μs脉冲
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
- 等待捕获完成
c复制while(!(__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_CC1) &&
__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_CC2)));
- 读取捕获值
c复制uint16_t rise = __HAL_TIM_GET_COMPARE(&htim1, TIM_CHANNEL_1);
uint16_t fall = __HAL_TIM_GET_COMPARE(&htim1, TIM_CHANNEL_2);
- 计算距离
c复制float pulseWidth = (fall - rise) * 1e-6f; // 转换为秒
float distance = 340.0f * pulseWidth / 2.0f;
- 控制LED
c复制if(distance < 0.2f) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}
5.3 完整主循环
c复制while (1) {
// 1. 复位定时器
__HAL_TIM_SET_COUNTER(&htim1, 0);
// 2. 清除标志位
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_CC1);
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_CC2);
// 3. 发送Trig脉冲
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
// 4. 等待捕获完成
uint32_t timeout = HAL_GetTick() + 100;
while(!(__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_CC1) &&
__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_CC2))) {
if(HAL_GetTick() > timeout) break;
}
// 5. 读取捕获值
uint16_t rise = __HAL_TIM_GET_COMPARE(&htim1, TIM_CHANNEL_1);
uint16_t fall = __HAL_TIM_GET_COMPARE(&htim1, TIM_CHANNEL_2);
// 6. 计算距离
float pulseWidth = (fall - rise) * 1e-6f;
float distance = 340.0f * pulseWidth / 2.0f;
// 7. 控制LED
if(distance < 0.2f) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}
HAL_Delay(100); // 测量间隔
}
6. 常见问题与调试技巧
6.1 常见问题
-
测距结果不稳定
- 检查电源是否稳定
- 确保传感器安装牢固
- 添加软件滤波(如多次测量取平均)
-
无法触发测量
- 检查Trig引脚连接
- 确保Trig脉冲宽度足够(至少10μs)
-
捕获值异常
- 检查定时器配置是否正确
- 确保Echo引脚连接正确
- 检查是否设置了超时处理
6.2 调试技巧
- 使用逻辑分析仪观察Trig和Echo信号
- 在关键位置添加调试打印
- 逐步验证每个功能模块
- 使用HAL_Delay()进行简单延时调试
7. 性能优化
7.1 提高测量频率
当前实现中每次测量间隔100ms,可以通过以下方式提高测量频率:
- 减少测量间隔时间
- 使用中断代替轮询
- 优化代码执行效率
7.2 增加滤波算法
原始数据可能会有波动,可以增加以下滤波算法:
- 移动平均滤波
- 中值滤波
- 卡尔曼滤波
7.3 扩展功能
基于当前项目可以扩展以下功能:
- 添加串口输出距离值
- 实现多传感器测距
- 增加报警阈值可调功能
- 添加LCD显示距离
8. 项目总结
这个HC-SR04超声波测距项目完整展示了STM32定时器输入捕获功能的应用。通过这个项目,我们学到了:
- HC-SR04超声波传感器的工作原理和使用方法
- STM32定时器输入捕获功能的配置和使用
- 从硬件连接到软件实现的完整开发流程
- 常见问题的排查和解决方法
在实际应用中,超声波测距可以用于很多场景,如倒车雷达、物体检测、液位测量等。掌握了这个基础实现后,可以根据具体需求进行功能扩展和优化。