STM32CubeMX实战:用定时器3实现HC-SR04超声波测距(附完整代码)
·
STM32CubeMX实战:用定时器3实现HC-SR04超声波测距(附完整代码)
在嵌入式开发中,精确测量距离是一个常见需求,而超声波测距因其非接触、成本低和精度适中等特点,成为许多项目的首选方案。本文将详细介绍如何利用STM32CubeMX图形化配置工具和HAL库,通过定时器3的输入捕获功能实现HC-SR04超声波模块的高精度测距。
1. 硬件准备与原理分析
1.1 硬件组成
实现超声波测距需要以下硬件组件:
- 主控芯片:STM32F407ZET6(本文示例,其他STM32系列类似)
- 超声波模块:HC-SR04
- 调试工具:ST-Link调试器
- 其他:杜邦线、电源等
HC-SR04模块有四个引脚:
- VCC:5V电源输入
- GND:地线
- Trig:触发信号输入(需至少10μs高电平)
- Echo:回响信号输出(高电平持续时间与距离成正比)
1.2 测距原理
超声波测距的基本原理是测量声波从发射到反射回来的时间差(Time of Flight, ToF)。具体步骤如下:
- 触发阶段:向Trig引脚发送至少10μs的高电平脉冲
- 发射阶段:模块自动发射8个40kHz的超声波脉冲
- 接收阶段:模块检测回波并通过Echo引脚输出高电平
- 计算距离:高电平持续时间t(μs)与距离d(cm)的关系为:d = t × 0.017
注意:0.017这个系数来源于声速(340m/s)和单位换算。实际应用中可能需要根据环境温度进行校准。
2. CubeMX工程配置
2.1 定时器3输入捕获配置
定时器输入捕获是测量脉冲宽度的关键功能。以下是具体配置步骤:
-
时钟配置:确保系统时钟正确,TIM3挂载在APB1总线上
-
定时器基础配置:
- Prescaler(预分频器):71(72MHz时钟分频为1MHz,每个计数周期1μs)
- Counter Mode(计数模式):Up(向上计数)
- Counter Period(自动重装载值):65535(16位最大值)
- Clock Division(时钟分频):No division
- Auto-reload preload(自动重载预装载):Disable
-
输入捕获通道配置:
- Channel1:Input Capture direct mode
- Polarity:Rising Edge(初始设置为上升沿捕获)
- IC Selection:Direct
- IC Prescaler:No division
- IC Filter:0(不滤波)
-
NVIC设置:使能TIM3全局中断
2.2 GPIO配置
- Trig引脚:配置为GPIO输出(如PA7)
- Echo引脚:连接到TIM3_CH1(如PA6)
2.3 串口配置(可选)
建议配置一个串口用于调试输出,如USART1:
- Mode:Asynchronous
- Baud Rate:115200
- Word Length:8bit
- Parity:None
- Stop Bits:1
3. 代码实现与优化
3.1 核心数据结构
首先定义状态机和测量数据结构:
typedef enum {
TRIG_WAIT = 0, // 等待触发状态
RISING_EDGE, // 已捕获上升沿
FALLING_EDGE, // 已捕获下降沿
MEASURE_DONE // 测量完成
} MeasureState;
typedef struct {
MeasureState state;
uint32_t rise_time;
uint32_t fall_time;
float distance_cm;
} HCSR04_Data;
3.2 触发函数
发送10μs以上的触发脉冲:
void HCSR04_Trigger(GPIO_TypeDef* GPIOx, uint16_t Pin) {
HAL_GPIO_WritePin(GPIOx, Pin, GPIO_PIN_SET);
delay_us(20); // 20μs触发脉冲
HAL_GPIO_WritePin(GPIOx, Pin, GPIO_PIN_RESET);
}
3.3 输入捕获回调函数
这是测量精度的核心,处理定时器捕获中断:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM3) {
switch(hcsr04.state) {
case TRIG_WAIT:
case RISING_EDGE:
// 捕获上升沿
hcsr04.rise_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
hcsr04.state = FALLING_EDGE;
break;
case FALLING_EDGE:
// 捕获下降沿
hcsr04.fall_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
hcsr04.state = MEASURE_DONE;
break;
default:
break;
}
}
}
3.4 定时器溢出处理
为防止长距离测量时定时器溢出,需处理更新中断:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM3 && hcsr04.state == FALLING_EDGE) {
hcsr04.fall_time = 0xFFFF; // 标记溢出
hcsr04.state = MEASURE_DONE;
}
}
3.5 距离计算与滤波
为提高测量稳定性,建议实现简单的滤波算法:
#define FILTER_SIZE 5
float HCSR04_CalculateDistance(void) {
static float filter_buf[FILTER_SIZE] = {0};
static uint8_t filter_index = 0;
float sum = 0;
if(hcsr04.state == MEASURE_DONE) {
// 计算原始距离
uint32_t pulse_width = (hcsr04.fall_time - hcsr04.rise_time) & 0xFFFF;
float raw_distance = pulse_width * 0.017f; // 单位:cm
// 中值滤波
filter_buf[filter_index] = raw_distance;
filter_index = (filter_index + 1) % FILTER_SIZE;
// 计算平均值
for(uint8_t i=0; i<FILTER_SIZE; i++) {
sum += filter_buf[i];
}
hcsr04.distance_cm = sum / FILTER_SIZE;
hcsr04.state = TRIG_WAIT;
}
return hcsr04.distance_cm;
}
4. 主程序流程
完整的主程序应包含初始化、测量循环和数据处理:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
// 启动定时器及中断
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
HAL_TIM_Base_Start_IT(&htim3);
while (1) {
if(hcsr04.state == TRIG_WAIT) {
HCSR04_Trigger(TRIG_GPIO_Port, TRIG_Pin);
// 等待测量完成
while(hcsr04.state != MEASURE_DONE) {
float distance = HCSR04_CalculateDistance();
if(distance > 0) {
printf("Distance: %.2f cm\r\n", distance);
}
}
HAL_Delay(100); // 两次测量间隔至少60ms
}
}
}
5. 常见问题与优化技巧
5.1 测量误差分析
实际应用中可能遇到的误差源包括:
- 温度影响:声速随温度变化(v = 331.4 + 0.6T,T为摄氏温度)
- 多径反射:超声波遇到多个反射面
- 最小距离限制:HC-SR04最小测量距离约2cm
- 信号抖动:Echo信号边沿不干净
5.2 软件优化建议
-
动态调整滤波器大小:
if(pulse_width < 1000) { // 近距离时减少滤波 effective_size = 3; } else { effective_size = FILTER_SIZE; } -
自动温度补偿:
float speed_of_sound = 331.4f + 0.6f * current_temperature; float distance = (pulse_width * 1e-6) * speed_of_sound * 50.0f; -
超时处理机制:
uint32_t timeout = HAL_GetTick(); while(hcsr04.state != MEASURE_DONE) { if(HAL_GetTick() - timeout > 100) { // 100ms超时 hcsr04.state = TRIG_WAIT; break; } } -
低功耗优化:
void Enter_LowPowerMode(void) { HAL_TIM_Base_Stop_IT(&htim3); HAL_TIM_IC_Stop_IT(&htim3, TIM_CHANNEL_1); HAL_UART_DeInit(&huart1); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); // 唤醒后重新配置时钟 }
6. 进阶应用:多模块协同
当需要同时使用多个超声波模块时,可采用以下策略:
-
分时复用:为每个模块分配不同的触发时间槽
void MultiModule_Trigger(void) { static uint8_t current_module = 0; switch(current_module) { case 0: HCSR04_Trigger(MODULE1_TRIG_GPIO_Port, MODULE1_TRIG_Pin); break; case 1: HCSR04_Trigger(MODULE2_TRIG_GPIO_Port, MODULE2_TRIG_Pin); break; // 更多模块... } current_module = (current_module + 1) % MODULE_COUNT; } -
多定时器方案:为每个模块分配独立的定时器通道
-
信号仲裁:通过外部逻辑电路合并多个Echo信号
7. 性能测试与校准
为确保测量精度,建议进行系统校准:
- 固定距离测试:在已知距离(如50cm、100cm)下测量并记录结果
- 线性回归:通过最小二乘法拟合校准曲线
// 校准公式:actual = scale * raw + offset float calibrated_distance = CALIB_SCALE * raw_distance + CALIB_OFFSET; - 环境补偿:集成温度传感器实时修正声速
以下是一个简单的校准数据记录表示例:
| 实际距离(cm) | 原始测量值(cm) | 误差(%) |
|---|---|---|
| 10.0 | 10.5 | +5.0 |
| 50.0 | 49.3 | -1.4 |
| 100.0 | 98.7 | -1.3 |
| 200.0 | 203.2 | +1.6 |
通过分析这些数据,可以确定是否需要以及如何进行软件校准。
更多推荐
所有评论(0)