STM32F103C8T6结合HC-SR04超声波模块实现高精度测距与串口数据可视化
1. 项目背景与硬件选型
大家好,我是单片机阿伟,今天咱们来聊聊怎么用STM32F103C8T6这款性价比超高的单片机驱动HC-SR04超声波模块实现高精度测距,并且把数据通过串口打印出来可视化。这个项目特别适合嵌入式初学者,既能学习传感器数据采集,又能掌握串口通信,一举两得。
先说说为什么选STM32F103C8T6和HC-SR04。STM32F103C8T6是ST的经典款,72MHz主频、64KB Flash、20KB RAM,资源足够丰富,价格还便宜,江湖人称"蓝色小药丸",调试起来特别方便。HC-SR04超声波模块就更不用说了,淘宝上几块钱一个,测距范围2cm-450cm,精度能达到3mm,关键是使用简单,就四个引脚:VCC、GND、Trig(触发)、Echo(回波)。
实际项目中,我发现在室内环境下,HC-SR04的稳定性相当不错,但要注意避免强噪声环境和柔软吸音材料表面。有一次我测试窗帘距离,数据就跳得厉害,后来换成墙面就稳定了。所以选型时要考虑实际应用场景。
2. 工作原理与硬件连接
HC-SR04的工作原理其实很简单,就像我们在山谷里喊话听回声一样。模块工作时,我们先给Trig引脚一个至少10μs的高电平脉冲,它就会发射一组40kHz的超声波。声波遇到障碍物反射回来,模块检测到回波后,会在Echo引脚输出一个高电平,这个高电平的持续时间就是声波往返的时间。
距离计算公式很简单:距离 = (高电平时间 × 声速) / 2。声速在常温下约340m/s,换算成厘米微秒就是除以58。所以实际计算时直接用高电平时间(微秒)除以58就能得到厘米为单位的距离。
接线方面特别要注意:
- VCC接5V,GND接GND
- Trig接PA0(任意GPIO都可以)
- Echo接PA1(最好用支持定时器输入的引脚)
我在实际接线时踩过一个坑:一开始用杜邦线连接,数据老是跳变,后来发现是线太长引入了干扰。换成短线后就稳定多了。所以建议接线尽量短,如果必须用长线,最好加上屏蔽。
3. 开发环境搭建
首先得准备好开发环境。我习惯用Keil MDK,版本5.25以上都比较稳定。新建工程时记得选对器件型号:STM32F103C8T6。然后要配置好时钟,这个芯片最高能跑到72MHz,咱们就按最大性能来。
需要包含的库文件有:
- stm32f10x_gpio.c
- stm32f10x_tim.c
- stm32f10x_usart.c
- stm32f10x_rcc.c
在配置系统时钟时,我推荐直接用官方提供的SystemInit()函数,它会自动配置好72MHz的主频。有时候初学者会忘记调用这个函数,结果程序跑在默认的8MHz下,什么定时器、串口时序全乱了。
串口配置我用的是USART1,PA9作TX,PA10作RX,波特率115200。这个速率足够传输距离数据了,如果想实时可视化,还可以考虑用串口绘图工具。
4. 定时器精准计时实现
定时器是本次项目的核心,我们要用TIM4来精确测量Echo高电平的持续时间。为什么选TIM4?因为它是个通用定时器,用起来简单,不占用系统关键资源。
配置定时器时,我设置预分频器psc=71,自动重装载值arr=999,这样每个计数周期是1μs(72MHz/(71+1)=1MHz),最大能测量到1ms*65535=65.535ms,对应约11米的测距范围,完全够用。
中断服务程序里主要做一件事:记录溢出次数。每次定时器溢出,msHcCount就加1。这个变量要用volatile修饰,避免编译器优化出错。
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
msHcCount++;
}
}
实测中发现,如果不开中断,单纯用轮询方式读取计数器值,精度会差一些。因为主循环可能被其他中断打断,导致读数不准。所以还是建议用中断+计数器的组合方式。
5. 超声波模块驱动编写
驱动代码主要分三部分:初始化、触发测量、读取时间。初始化时要配置好GPIO,Trig引脚推挽输出,Echo引脚浮空输入。这里要注意,Echo绝对不能设为上拉或下拉,否则会影响高电平的检测。
触发信号的时序很关键,必须至少10μs的高电平。我实际测试发现,20μs比较稳妥,太短了可能触发不了,太长了又浪费电。所以代码里用delay_us(20)来确保触发成功。
测量距离的函数我做了5次采样然后取平均,这样能有效抑制随机误差。有一次我测试水温对测距的影响,发现温度变化时声速会变,但5次平均后数据还是很稳定。
float UltrasonicGetLength(void)
{
u32 t = 0;
int i = 0;
float lengthTemp = 0;
float sum = 0;
while(i != 5) // 取5次测量平均值
{
TRIG_Send = 1;
delay_us(20);
TRIG_Send = 0;
while(ECHO_Reci == 0); // 等待回波高电平
OpenTimerForHc();
while(ECHO_Reci == 1); // 等待回波低电平
CloseTimerForHc();
t = GetEchoTimer();
lengthTemp = ((float)t / 58.0);
sum += lengthTemp;
i++;
}
return sum / 5.0;
}
6. 数据滤波与稳定性优化
原始数据往往带有噪声,直接使用会跳变得很厉害。除了5次平均滤波,我还试过其他滤波方法。中值滤波效果不错,就是实现起来稍复杂。移动平均滤波简单但响应会变慢。
对于要求更高的场合,可以结合温度传感器动态修正声速。因为声速随温度变化很大,每升高1℃,声速约增加0.6m/s。可以用DS18B20测环境温度,然后用公式:声速=331.5+0.6*温度(℃)。
硬件方面也可以优化:在VCC和GND之间加个100nF的退耦电容,能有效抑制电源噪声。Echo信号线上可以加个100Ω的串阻,防止振铃现象。
我在一个机器人项目中就用了温度补偿,测距精度从±1cm提高到了±2mm。不过对于一般应用,软件滤波已经足够了。
7. 串口数据输出与可视化
串口配置要注意波特率匹配,我常用115200bps,8位数据位,无校验,1位停止位。发送数据时可以用printf重定向,这样调试起来特别方便。
// 在usart.c中添加以下代码
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
USART_SendData(USART1, (uint8_t) ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
return ch;
}
数据格式也很重要。我推荐用JSON格式输出,这样上位机解析起来方便。比如:{"distance":25.3,"unit":"cm"}。如果想实时可视化,可以用Serial Chart、CoolTerm这些工具,它们能实时绘制数据曲线。
有一次我帮学弟调试,发现串口数据老是断断续续。后来发现是delay放错了地方,导致发送间隔不稳定。所以建议用定时器定时发送,比如每100ms发送一次数据,这样波形图看起来就很流畅。
8. 实战调试与问题排查
调试时最容易遇到几个问题:一是读到的距离永远是0,二是数据跳变太大,三是测量范围不够。
如果距离总是0,先检查Trig触发信号有没有产生,用示波器看是不是真的有20μs高电平。然后检查Echo引脚,看是否有高电平输出。有时候模块坏了,怎么测都是0。
数据跳变大的话,首先要排除环境干扰。超声波对温度和气流都很敏感,最好在室内静止空气中测试。软件上可以增加滤波次数,或者改用更复杂的滤波算法。
测量范围不足可能是硬件限制,HC-SR04标称4.5m,但实际上超过3m后精度就下降了。如果真要测远距离,可以考虑换其他型号,比如US-100。
我总结了个调试清单:
- 电源电压是否稳定(5V±0.5V)
- 接线是否正确牢固
- 触发信号时序对不对
- 环境是否有强噪声或振动
- 被测物体是否适合超声波测距
9. 进阶优化与扩展应用
基础功能实现后,还可以做很多优化。比如用输入捕获功能来替代轮询检测Echo引脚,这样能大大提高效率,减少CPU占用。
// 输入捕获配置示例
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
还可以加上超时检测,避免因为没收到回波而卡死程序。我一般设50ms超时,超过这个时间就认为测量失败返回错误值。
扩展应用方面,可以用多个超声波模块做360度测距,或者加上蓝牙模块把数据无线传输到手机。我在智能小车上就用了4个HC-SR04,实现了全向避障功能。
如果想进一步降低功耗,可以间歇工作模式。比如每秒钟只测量一次,其他时间让单片机进入睡眠模式。这样整个系统的工作电流能从30mA降到5mA以下,特别适合电池供电的应用。
最后提醒一点,超声波测距有个盲区,大约是2cm左右,太近的物体是测不准的。所以设计时要留出足够余地,或者用其他传感器如红外来补盲。
更多推荐
所有评论(0)