1. STM32F103C8T6与HC-SR04超声波测距系统概述
在嵌入式开发领域,超声波测距是一种常见且实用的距离测量方案。STM32F103C8T6作为一款性价比极高的Cortex-M3内核微控制器,配合HC-SR04超声波模块,可以构建一个稳定可靠的距离测量系统。这个组合特别适合机器人避障、液位检测、停车辅助等应用场景。
HC-SR04模块的工作原理其实非常巧妙。它通过TRIG引脚接收至少10μs的高电平触发信号后,会自动发射8个40kHz的超声波脉冲,然后通过ECHO引脚返回一个高电平信号,这个高电平的持续时间与测量距离成正比。这种设计使得外围电路非常简单,只需要两个GPIO引脚(一个输出一个输入)就能完成测距功能。
使用HAL库开发的优势在于其高度的抽象性和可移植性。HAL库提供了统一的硬件抽象层接口,使得代码在不同STM32系列间的移植变得更加容易。对于初学者来说,HAL库的初始化流程虽然看起来稍显复杂,但CubeMX工具可以自动生成大部分初始化代码,大大降低了开发门槛。
2. 硬件连接与配置
2.1 硬件准备清单
要完成这个项目,你需要准备以下硬件组件:
- STM32F103C8T6最小系统板(蓝色药丸板)
- HC-SR04超声波模块
- 杜邦线若干
- 5V电源(可以使用USB供电)
- 可选:OLED显示屏用于实时显示距离数据
2.2 引脚连接方案
HC-SR04模块有4个引脚:VCC、GND、TRIG和ECHO。具体连接方式如下:
| HC-SR04引脚 | STM32F103C8T6连接 |
|---|---|
| VCC | 5V电源 |
| GND | GND |
| TRIG | PA0(或其他GPIO) |
| ECHO | PA1(或其他GPIO) |
注意:ECHO引脚输出的是5V电平,而STM32的GPIO最大耐受电压是3.3V。虽然在实际测试中直接连接通常不会立即损坏芯片,但长期使用建议添加电平转换电路或使用电阻分压。
2.3 STM32时钟配置
使用CubeMX配置系统时钟时,建议采用外部8MHz晶振作为时钟源,通过PLL倍频到72MHz主频。这是STM32F103系列的典型工作频率,能够提供足够的性能来处理超声波测距的计算和响应。
3. 开发环境搭建
3.1 软件工具准备
需要安装以下开发工具:
- Keil MDK-ARM(建议使用V5版本)
- STM32CubeMX(最新版本)
- STM32F1 HAL库(通过CubeMX自动安装)
3.2 CubeMX工程配置
- 打开CubeMX,选择STM32F103C8T6芯片
- 配置系统时钟树,确保主频设置为72MHz
- 启用USART1用于调试输出(可选)
- 配置PA0为GPIO输出(用于TRIG信号)
- 配置PA1为GPIO输入(用于ECHO信号)
- 启用一个定时器(如TIM2)用于高精度时间测量
- 生成Keil工程代码
3.3 HAL库关键函数解析
HAL库中我们将主要使用以下几个关键函数:
HAL_GPIO_WritePin()- 控制TRIG引脚输出HAL_GPIO_ReadPin()- 读取ECHO引脚状态HAL_TIM_Base_Start()- 启动定时器__HAL_TIM_GET_COUNTER()- 获取定时器计数值
4. 超声波测距核心实现
4.1 测距时序控制
HC-SR04的完整测距流程如下:
- 给TRIG引脚至少10μs的高电平脉冲
- 模块自动发射8个40kHz超声波脉冲
- 模块检测回波,ECHO引脚输出高电平
- 高电平持续时间与距离成正比(每58μs对应1cm)
- 计算距离公式:距离(cm) = 高电平时间(μs) / 58
4.2 代码实现详解
以下是使用HAL库实现超声波测距的核心代码:
c复制#define TRIG_PIN GPIO_PIN_0
#define TRIG_PORT GPIOA
#define ECHO_PIN GPIO_PIN_1
#define ECHO_PORT GPIOA
float Get_Distance(void)
{
uint32_t start_time = 0, end_time = 0;
float distance_cm = 0;
// 发送10us的TRIG高电平脉冲
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET);
delay_us(15);
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET);
// 等待ECHO引脚变高
while(HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) == GPIO_PIN_RESET);
// 记录高电平开始时间
start_time = HAL_GetTick();
// 等待ECHO引脚变低
while(HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) == GPIO_PIN_SET);
// 计算高电平持续时间
end_time = HAL_GetTick();
distance_cm = (end_time - start_time) * 0.034 / 2; // 单位:厘米
return distance_cm;
}
4.3 定时器精确计时实现
使用HAL_GetTick()的精度只有1ms,对于短距离测量不够精确。我们可以使用定时器来提高测量精度:
c复制// 使用TIM2进行高精度计时
void TIM2_Init(void)
{
__HAL_RCC_TIM2_CLK_ENABLE();
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1; // 1MHz计数频率
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
HAL_TIM_Base_Start(&htim2);
}
uint32_t Get_Micros(void)
{
return __HAL_TIM_GET_COUNTER(&htim2);
}
5. 系统优化与误差处理
5.1 常见误差来源分析
超声波测距系统的主要误差来源包括:
- 温度影响:声速随温度变化(v = 331.4 + 0.6*T,T为摄氏温度)
- 多径反射:超声波遇到多个反射面会产生干扰
- 测量角度:当被测表面不垂直于超声波束时,回波可能无法返回
- 环境噪声:其他超声波源可能干扰测量
5.2 软件滤波算法
为了提高测量稳定性,可以采用以下滤波算法:
c复制#define SAMPLE_NUM 5
float Filter_Distance(void)
{
float sum = 0;
float samples[SAMPLE_NUM];
for(int i=0; i<SAMPLE_NUM; i++) {
samples[i] = Get_Distance();
HAL_Delay(50);
}
// 去掉最大值和最小值
float min = samples[0], max = samples[0];
for(int i=1; i<SAMPLE_NUM; i++) {
if(samples[i] < min) min = samples[i];
if(samples[i] > max) max = samples[i];
}
for(int i=0; i<SAMPLE_NUM; i++) {
if(samples[i] != min && samples[i] != max) {
sum += samples[i];
}
}
return sum / (SAMPLE_NUM-2);
}
5.3 温度补偿实现
可以通过DS18B20等温度传感器获取环境温度,进行声速补偿:
c复制float Get_Corrected_Distance(float raw_distance, float temperature)
{
float sound_speed = 331.4 + 0.6 * temperature; // m/s
float calibrated_speed = 343.0; // 标准声速(25℃)
return raw_distance * calibrated_speed / sound_speed;
}
6. 实际应用案例扩展
6.1 与OLED显示集成
可以将测量结果实时显示在OLED屏幕上,增强实用性:
c复制void Show_Distance_On_OLED(float distance)
{
char buf[20];
sprintf(buf, "Dist: %.1fcm", distance);
OLED_ShowString(0, 0, (uint8_t *)buf, 16);
}
6.2 多传感器融合应用
结合其他传感器可以构建更复杂的系统,例如:
- 超声波+红外测距:提高测量可靠性
- 超声波+陀螺仪:实现三维空间测距
- 超声波+电机控制:实现自动避障小车
6.3 无线数据传输实现
通过串口或无线模块将数据发送到上位机:
c复制void Send_Distance_To_PC(float distance)
{
char msg[50];
sprintf(msg, "Distance: %.2f cm\r\n", distance);
HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}
7. 调试技巧与常见问题解决
7.1 硬件调试要点
- 确保电源稳定:HC-SR04对电源噪声敏感,建议在VCC和GND之间加一个100μF电容
- 检查连接:使用万用表确认所有连接正确,特别是GND要共地
- 测试信号:用示波器观察TRIG和ECHO信号波形是否符合预期
7.2 软件调试技巧
- 使用串口打印调试信息:
c复制printf("Start time: %lu, End time: %lu\n", start_time, end_time);
- 添加超时检测,防止程序卡死:
c复制uint32_t timeout = HAL_GetTick();
while(HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) == GPIO_PIN_RESET) {
if(HAL_GetTick() - timeout > 100) {
return 0; // 超时返回0
}
}
7.3 常见问题及解决方案
-
测量结果始终为0:
- 检查TRIG信号是否正常发出
- 确认ECHO引脚连接正确
- 检查模块是否供电正常
-
测量值波动大:
- 增加软件滤波
- 确保测量环境没有强气流或噪声干扰
- 检查电源稳定性
-
测量距离短:
- 检查物体表面是否适合超声波反射(柔软、多孔材料吸收超声波)
- 确认模块朝向正确,与被测物体表面垂直
-
偶尔出现极大值:
- 添加测量范围限制(如最大4米)
- 实现数据合理性检查,丢弃明显异常值
