1. 项目概述
SRF05超声波测距传感器在嵌入式系统中有着广泛应用,从智能小车避障到工业测距都可见其身影。这次我选择STM32F103C8T6这款经典的Cortex-M3内核MCU来驱动SRF05,主要看中其丰富的外设资源和72MHz的主频性能。在实际项目中,这种组合既能满足高精度测距需求,又保持了较低的成本。
这个驱动方案的核心在于精确计时——通过定时器捕获ECHO信号的高电平持续时间,再结合声速计算出实际距离。我特别加入了超时保护机制,这在复杂环境中尤为重要,能有效避免因物体超出测距范围或传感器故障导致的系统卡死问题。
2. 硬件设计与连接
2.1 硬件选型解析
STM32F103C8T6作为驱动核心有几个明显优势:
- 72MHz主频提供足够计时精度
- 多达7个定时器(TIM1-TIM7)可供选择
- GPIO支持最高50MHz的翻转速度
- 内置的NVIC中断控制器确保实时响应
SRF05传感器的主要参数:
- 测距范围:2cm-450cm
- 工作电压:5V DC
- 工作电流:15mA
- 分辨率:0.3cm
- 测量角度:15度
2.2 电路连接方案
实际连接时需要注意几个关键点:
-
电源部分:
- STM32的3.3V与SRF05的5V需分开供电
- 建议在VCC与GND间加0.1μF去耦电容
-
信号连接:
- TRIG引脚 → PA0(推挽输出)
- ECHO引脚 → PA1(浮空输入)
- 由于STM32是3.3V电平,ECHO信号需要电平转换:
- 简单方案:1kΩ与2kΩ电阻分压
- 更可靠方案:使用TXS0108E等电平转换芯片
注意:直接连接5V信号到STM32的GPIO可能损坏芯片,务必做好电平转换!
3. 软件实现细节
3.1 定时器配置
我选择TIM2作为计时基准,配置步骤如下:
c复制void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 72MHz/(71+1)=1MHz → 1μs分辨率
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
}
关键参数说明:
- 预分频值71:将72MHz主频分频为1MHz(每个计数1μs)
- 自动重装载值0xFFFF:最大计时周期约65ms
- 向上计数模式:简单直观
3.2 触发信号生成
SRF05需要至少10μs的高电平触发信号:
c复制void SRF05_Trigger(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_0); // TRIG高电平
delay_us(15); // 保持15μs
GPIO_ResetBits(GPIOA, GPIO_Pin_0);// TRIG低电平
}
实测技巧:实际保持时间可以略大于10μs(如15μs),确保可靠触发但不过长。
3.3 回波信号捕获
使用输入捕获方式测量高电平持续时间:
c复制void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1))
{
// 上升沿:开始计时
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
}
else
{
// 下降沿:停止计时
TIM_Cmd(TIM2, DISABLE);
distance = TIM_GetCounter(TIM2) * 0.017; // 计算距离(cm)
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
距离计算公式:
距离(cm) = (计时值×声速)/2 = (t×34000cm/s)/2 ≈ t×0.017
4. 系统优化与异常处理
4.1 超时保护机制
为避免无回波信号时系统阻塞,加入超时判断:
c复制uint32_t timeout = 0;
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1))
{
if(++timeout > 30000) // 约30ms超时
{
distance = 0; // 标记为无效测量
break;
}
delay_us(1);
}
超时值30ms对应最大测距:
(30000μs × 340m/s)/2 = 5.1m > SRF05的4.5m量程
4.2 软件滤波处理
针对测量噪声,采用中值滤波:
c复制#define FILTER_SIZE 5
uint16_t filter_buf[FILTER_SIZE];
uint16_t MedianFilter(uint16_t new_val)
{
// 滑动窗口更新
for(int i=0; i<FILTER_SIZE-1; i++)
filter_buf[i] = filter_buf[i+1];
filter_buf[FILTER_SIZE-1] = new_val;
// 排序取中值
uint16_t temp[FILTER_SIZE];
memcpy(temp, filter_buf, sizeof(temp));
bubble_sort(temp, FILTER_SIZE);
return temp[FILTER_SIZE/2];
}
5. 完整代码实现
5.1 头文件定义(SRF05.h)
c复制#ifndef __SRF05_H
#define __SRF05_H
#include "stm32f10x.h"
#define TRIG_PIN GPIO_Pin_0
#define ECHO_PIN GPIO_Pin_1
#define SRF05_GPIO GPIOA
void SRF05_Init(void);
float SRF05_GetDistance(void);
void TIM2_Configuration(void);
void SRF05_Trigger(void);
#endif
5.2 主程序实现(main.c)
c复制#include "SRF05.h"
#include "delay.h"
volatile uint16_t distance_cm = 0;
int main(void)
{
SystemInit();
delay_init();
SRF05_Init();
TIM2_Configuration();
while(1)
{
SRF05_Trigger();
delay_ms(100); // 测量间隔100ms
printf("Distance: %d cm\r\n", distance_cm);
}
}
6. 实际调试经验
6.1 常见问题排查
-
无回波信号:
- 检查TRIG信号是否正常(示波器观察)
- 确认电源电压稳定(5V±10%)
- 测试传感器是否完好(可用另一个SRF05交叉验证)
-
测量值不稳定:
- 添加硬件滤波(ECHO引脚对地加100pF电容)
- 优化软件滤波参数
- 确保被测物体表面平整(粗糙表面会导致散射)
-
测量值偏小:
- 检查温度补偿(声速随温度变化)
- 确认定时器配置正确(1μs分辨率)
- 检查电平转换电路是否造成信号延迟
6.2 性能优化技巧
-
提高测量频率:
- 最小测量间隔=测距时间+60ms(SRF05恢复时间)
- 在450cm最大量程时,间隔可设置为:
- 测距时间:450cm×2/34000cm/s≈26ms
- 总间隔:26+60=86ms → 实际可取100ms
-
降低功耗:
- 不测量时关闭传感器电源(通过MOS管控制)
- 使用定时器唤醒代替delay延时
-
多传感器协同:
- 分时复用GPIO(需加模拟开关如CD4051)
- 每个传感器分配独立触发间隔
7. 扩展应用方向
这个基础驱动可以扩展出多种实用场景:
-
智能小车避障系统:
- 前向安装2-3个SRF05
- 实现区域扫描(每100ms切换传感器)
-
液位监测:
- 固定安装于容器顶部
- 加入温度补偿(声速修正)
- 计算公式:距离=计数×(331.4+0.6×T)/2×100
-
人体检测:
- 设置距离阈值(如50-100cm)
- 结合滤波算法消除误触发
我在一个自动化仓储项目中就使用了类似的方案,通过6个SRF05实现了货架间距的自动检测。关键是要处理好多个传感器之间的干扰问题——我们最终采用了分时触发的方式,每个传感器间隔5ms依次工作,配合方向性较强的安装角度,完美解决了交叉干扰问题。
