1. 超声波测距原理与硬件准备
超声波测距作为嵌入式系统中常见的非接触式距离测量方案,其核心原理是通过计算超声波发射到接收的时间差来推算距离。在STM32平台上实现这一功能,需要合理配置TIM定时器的输入捕获功能。硬件上除了STM32最小系统板外,还需要HC-SR04超声波模块(工作电压5V,测量范围2cm-400cm)、若干杜邦线以及一个5V电源模块。
HC-SR04模块有四个引脚:VCC、Trig(触发信号输入)、Echo(回波信号输出)和GND。其中Trig引脚需要至少10μs的高电平脉冲来触发测距,Echo引脚则会输出与距离成正比的高电平脉冲。STM32的TIM通道捕获功能正是用来精确测量这个高电平脉冲的持续时间。
注意:HC-SR04的VCC建议单独供电,避免直接使用STM32开发板的3.3V电源,否则可能导致测量距离缩短或模块工作不稳定。
2. TIM输入捕获模式配置详解
2.1 定时器基础参数设置
在STM32CubeMX中配置TIM定时器时,首先需要确定定时器的时钟源和计数频率。以TIM2为例,当系统时钟为72MHz时,通过预分频器(PSC)可将计数频率降低到适合超声波测距的范围。通常建议设置预分频值为71,使计数器频率为1MHz(每个计数周期1μs),这样可以直接用计数值表示微秒数。
计数模式应选择向上计数(Up-counting),自动重装载值(ARR)设置为最大值0xFFFF,以获取最大的测量范围。超声波测距的理论最大测量距离约4米(对应约23ms的高电平时间),1MHz的计数频率完全满足需求。
2.2 输入捕获通道配置
选择TIM的一个通道(如CH1)作为输入捕获通道,配置为输入捕获模式。关键参数包括:
- 极性选择:上升沿捕获
- 输入分频:无分频(ICPS=00)
- 输入滤波:根据环境噪声情况设置(通常0x0或0x1足够)
需要特别注意,Echo信号的高电平时间可能超过定时器的自动重装载周期(ARR),因此必须启用定时器的溢出中断,并在中断服务函数中记录溢出次数。实际距离计算时需要结合捕获值和溢出次数:
code复制距离(cm) = (捕获值 + 溢出次数 * ARR) * 声速(340m/s) / 2 / 10000
2.3 NVIC中断配置
必须使能以下中断:
- TIM全局中断(用于处理溢出)
- 捕获/比较中断(用于处理边沿事件)
中断优先级应根据系统需求合理设置。在HAL库中,相关中断处理函数为:
c复制void TIM2_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim2);
}
3. 超声波测距完整实现流程
3.1 初始化序列
- 初始化GPIO:Trig引脚配置为推挽输出,Echo引脚配置为输入(无需上拉)
- 初始化TIM:按前述参数配置定时器和输入捕获通道
- 启动定时器和中断:
c复制HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
3.2 测距触发逻辑
每次测量需要先发送至少10μs的Trig脉冲:
c复制HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);
delay_us(15); // 略大于10μs
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
3.3 捕获中断处理
在HAL库中,需要重写捕获回调函数:
c复制void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM2) {
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
static uint32_t firstCapture = 0;
if(icState == RISING_EDGE) {
firstCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
icState = FALLING_EDGE;
} else {
distance = (HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) - firstCapture + overflowCount * 0xFFFF) * 0.034 / 2;
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
icState = RISING_EDGE;
overflowCount = 0;
}
}
}
}
3.4 定时器溢出处理
在定时器溢出中断中增加溢出计数:
c复制void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM2) {
overflowCount++;
}
}
4. 性能优化与误差处理
4.1 软件滤波算法
为提高测量稳定性,可采用以下滤波方法:
- 中值滤波:连续采样5次,取中间值
- 滑动平均滤波:维护一个长度为5的队列,计算平均值
实现示例:
c复制#define FILTER_SIZE 5
static uint32_t distanceBuffer[FILTER_SIZE] = {0};
static uint8_t bufferIndex = 0;
float getFilteredDistance(float newDistance) {
distanceBuffer[bufferIndex++] = newDistance * 100; // 转为mm便于处理
if(bufferIndex >= FILTER_SIZE) bufferIndex = 0;
uint32_t sum = 0;
for(int i=0; i<FILTER_SIZE; i++) {
sum += distanceBuffer[i];
}
return sum / FILTER_SIZE / 100.0f;
}
4.2 温度补偿
声速随温度变化(v=331.4+0.6T),精确测量需加入温度传感器(如DS18B20):
c复制float getSoundSpeed(float temperature) {
return 331.4f + 0.6f * temperature;
}
4.3 常见问题排查
-
无回波信号:
- 检查Trig脉冲宽度是否足够(示波器测量)
- 确认Echo引脚连接正确
- 测试模块电源电压是否达到5V
-
测量值跳动大:
- 增加软件滤波
- 检查是否有多个反射面
- 确保测量对象表面平整
-
测量值偏小:
- 检查定时器时钟配置
- 确认预分频值计算正确
- 测试环境温度是否过低
5. 进阶应用与扩展
5.1 多路超声波测距
通过分时复用可扩展多路测量:
- 使用多路模拟开关(如CD4051)切换Trig/Echo
- 为每个模块分配独立的GPIO和TIM通道
- 采用时间片轮询方式依次触发
5.2 三维空间定位
三个以上超声波模块可组成定位系统:
- 通过三角测量法计算目标位置
- 需要解决多径干扰问题
- 推荐使用TDOA(到达时间差)算法
5.3 低功耗设计
电池供电场景下的优化策略:
- 间歇工作模式(如每秒测量一次)
- 动态调整TIM时钟频率
- 使用LL库替代HAL库减少开销
在实际项目中,我发现TIM输入捕获的精度对测量结果影响很大。一个实用的技巧是在系统启动时校准定时器时钟,通过比对已知延迟信号来修正预分频值。另外,当测量距离小于2cm时,HC-SR04可能无法可靠工作,这时可以考虑改用TOF(飞行时间)传感器。