STM32定时器输入捕获与超声波测距实战指南
超声波测距技术因其成本低廉、实现简单且精度适中,在智能小车避障、机器人定位、液位检测等领域广泛应用。本文将深入讲解如何利用STM32的定时器输入捕获功能驱动HC-SR04超声波模块实现高精度距离测量。
1. 超声波测距原理与硬件连接
HC-SR04超声波模块通过发射超声波并接收其反射信号来测量距离。模块包含四个引脚:
- VCC:5V电源输入
- GND:接地
- Trig:触发信号输入
- Echo:回波信号输出
测距原理如下:
- STM32向Trig引脚发送至少10μs的高电平脉冲
- 模块自动发射8个40kHz的超声波脉冲
- 超声波遇到障碍物反射后被模块接收
- 模块通过Echo引脚输出高电平,持续时间与距离成正比
距离计算公式:
距离(cm) = (高电平时间(μs) × 0.0343) / 2硬件连接示例:
Trig引脚 --- PA0 (通用输出) Echo引脚 --- PA1 (定时器输入捕获通道) VCC --- 5V GND --- GND2. STM32定时器输入捕获配置
输入捕获是测量脉冲宽度的关键技术。我们以TIM2为例,配置输入捕获功能:
void TIM2_Cap_Init(u16 arr, u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM2_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 输入下拉 GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler = psc; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 输入捕获配置 TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM2_ICInitStructure.TIM_ICFilter = 0x00; TIM_ICInit(TIM2, &TIM2_ICInitStructure); // 中断配置 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update|TIM_IT_CC2, ENABLE); TIM_Cmd(TIM2, ENABLE); }关键参数说明:
| 参数 | 说明 | 典型值 |
|---|---|---|
| arr | 自动重装载值 | 65535 |
| psc | 预分频系数 | 71 (1MHz计数频率) |
| TIM_ICPolarity | 捕获极性 | 初始设为上升沿 |
| TIM_ICFilter | 输入滤波器 | 0表示不滤波 |
3. 中断服务函数实现
中断服务函数需要处理定时器溢出和捕获事件,准确计算高电平持续时间:
volatile u8 TIM2CH1_CAPTURE_STA = 0; // 状态标志 volatile u16 TIM2CH1_CAPTURE_VAL; // 捕获值 void TIM2_IRQHandler(void) { if((TIM2CH1_CAPTURE_STA & 0X80) == 0) { // 未完成捕获 if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { // 溢出中断 if(TIM2CH1_CAPTURE_STA & 0X40) { // 已捕获到高电平 if((TIM2CH1_CAPTURE_STA & 0X3F) == 0X3F) { // 高电平过长 TIM2CH1_CAPTURE_STA |= 0X80; // 标记完成 TIM2CH1_CAPTURE_VAL = 0XFFFF; } else { TIM2CH1_CAPTURE_STA++; } } } if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET) { // 捕获中断 if(TIM2CH1_CAPTURE_STA & 0X40) { // 下降沿 TIM2CH1_CAPTURE_STA |= 0X80; // 标记完成 TIM2CH1_CAPTURE_VAL = TIM_GetCapture2(TIM2); TIM_OC2PolarityConfig(TIM2, TIM_ICPolarity_Rising); // 重置为上升沿 } else { // 上升沿 TIM2CH1_CAPTURE_STA = 0; TIM2CH1_CAPTURE_VAL = 0; TIM_SetCounter(TIM2, 0); TIM2CH1_CAPTURE_STA |= 0X40; // 标记捕获到上升沿 TIM_OC2PolarityConfig(TIM2, TIM_ICPolarity_Falling); // 设为下降沿 } } } TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); }状态标志TIM2CH1_CAPTURE_STA的位定义:
- 位7:捕获完成标志
- 位6:已捕获到上升沿标志
- 位0-5:溢出计数器
4. 距离计算与温度补偿
完成高电平时间测量后,可计算实际距离并考虑温度补偿:
float Get_Distance(void) { float distance = 0; u32 time = 0; // 发送触发信号 GPIO_SetBits(GPIOA, GPIO_Pin_0); delay_us(20); GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 等待捕获完成 while((TIM2CH1_CAPTURE_STA & 0X80) == 0); // 计算总时间 time = TIM2CH1_CAPTURE_STA & 0X3F; time *= 65536; // 溢出时间总和 time += TIM2CH1_CAPTURE_VAL; // 计算距离(25℃标准声速) distance = time * 0.0343 / 2; // 温度补偿(可选) // float temperature = Get_Temperature(); // 从温度传感器读取 // float sound_speed = 331.5 + 0.6 * temperature; // distance = (time * sound_speed / 10000) / 2; TIM2CH1_CAPTURE_STA = 0; // 准备下一次测量 return distance; }温度对声速的影响:
| 温度(℃) | 声速(m/s) | 误差(100cm时) |
|---|---|---|
| 0 | 331.5 | +3.5cm |
| 25 | 343.0 | 基准 |
| 50 | 361.5 | -5.4cm |
5. 实战优化与常见问题
5.1 测量稳定性优化
- 多次测量取平均:连续测量5次,去掉最大最小值后取平均
- 软件滤波:采用滑动窗口滤波或卡尔曼滤波算法
- 盲区处理:忽略2-4cm内的测量结果(超声波模块固有盲区)
#define SAMPLE_TIMES 5 float Get_Stable_Distance(void) { float distances[SAMPLE_TIMES]; float sum = 0; for(int i=0; i<SAMPLE_TIMES; i++) { distances[i] = Get_Distance(); delay_ms(100); // 两次测量间隔至少60ms } // 排序并去掉最大最小值 Bubble_Sort(distances, SAMPLE_TIMES); for(int i=1; i<SAMPLE_TIMES-1; i++) { sum += distances[i]; } return sum / (SAMPLE_TIMES-2); }5.2 常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 测量值恒为0 | Echo引脚未连接正确 | 检查硬件连接 |
| 测量值波动大 | 环境干扰或电源不稳 | 添加软件滤波,检查电源 |
| 测量距离偏小 | 温度补偿未启用 | 添加温度传感器补偿 |
| 无法触发测量 | Trig脉冲宽度不足 | 确保Trig脉冲≥10μs |
5.3 扩展应用
结合STM32其他外设可实现更丰富的功能:
- LCD显示:实时显示测量距离
- 无线传输:通过蓝牙/WiFi上传数据
- 电机控制:根据距离自动调整电机速度
- 多传感器融合:结合红外、激光等传感器提高可靠性
// LCD显示示例 void Display_Distance(float distance) { char buf[20]; sprintf(buf, "Dist: %.1fcm", distance); LCD_DisplayString(0, 0, (u8*)buf); }通过本指南的系统学习,开发者可以掌握STM32定时器输入捕获的核心技术,并灵活应用于各种超声波测距场景。实际项目中,建议结合具体需求优化测量算法和硬件设计,以获得最佳性能。