1. 项目概述与设计思路
超声波测距系统在智能小车避障、倒车雷达、工业检测等领域应用广泛。基于STC89C52单片机和HC-SR04模块的方案,成本低、易上手,非常适合嵌入式入门和快速开发。我自己做过好几个类似项目,实测下来稳定性不错,测距范围2cm到4米,精度能控制在3mm以内。
系统核心工作原理很简单:单片机触发HC-SR04发射超声波,计算从发射到接收回波的时间差,根据声速换算成距离。但实际调试中会遇到不少坑,比如温度对声速的影响、回波信号处理、测量盲区等问题。这个项目不仅包含硬件电路设计,还涉及温度补偿算法和实物调试技巧,我会把踩过的坑和解决方案都分享出来。
系统功能包括:
- 实时测量并显示距离(4位数码管)
- 按键设置报警阈值(支持掉电保存)
- 超出阈值时蜂鸣器报警
- 测量误差自动校准
2. 硬件设计详解
2.1 核心控制器:STC89C52
我选择STC89C52是因为它性价比高,资源足够用:8KB Flash、512B RAM、32个IO口、3个定时器。相比STM32,51单片机对新手更友好,寄存器配置简单,不用折腾复杂的库函数。
最小系统设计要点:
- 晶振电路:12MHz晶振搭配30pF电容(容值太小不易起振,太大会增加功耗)
- 复位电路:10k电阻+10uF电容,复位时间约0.1秒
- 电源滤波:在VCC和GND之间加个0.1uF去耦电容,能有效减少单片机重启问题
实际焊接时,晶振要尽量靠近单片机引脚,否则容易导致时钟不稳定。我曾经因为晶振走线太长,导致数码管显示闪烁,折腾了半天才找到问题。
2.2 HC-SR04模块原理与使用
HC-SR04模块有4个引脚:
- VCC:5V供电(低于4.5V会影响测量距离)
- Trig:触发信号输入(至少10us高电平)
- Echo:回波信号输出(高电平持续时间对应距离)
- GND:接地
工作时序很关键:
计算距离的公式:距离 = (高电平时间 * 声速) / 2
声速在常温下约340m/s,但会随温度变化。每升高1℃,声速增加0.6m/s。如果要提高精度,需要加入温度传感器进行补偿。我实测过,在20℃环境下,测量误差小于1%;但在0℃和40℃时,误差会达到3%以上。
2.3 显示与报警电路
数码管驱动方案:
我用的是4位共阴数码管,采用动态扫描方式。P0口输出段码(要加上拉电阻),P2.0-P2.3控制位选。动态扫描时要注意:
- 扫描频率不能太低(否则闪烁),也不能太高(导致亮度不足)
- 一般设置在100-200Hz之间,每位数码管点亮2-5ms
实际调试时发现,如果直接驱动数码管,单片机IO口电流可能不足。我加了74HC245缓冲器后,亮度明显提升。
报警电路设计:
用9012 PNP三极管驱动蜂鸣器,基极通过1k电阻接到单片机IO口。当IO输出低电平时,三极管导通,蜂鸣器发声。记得在蜂鸣器两端反向并联一个二极管,防止感应电动势损坏三极管。
3. 软件设计与代码实现
3.1 主程序框架
程序采用前后台架构,主循环中处理按键扫描、显示刷新,中断处理超声波测距。
void main() {
time_init(); // 定时器初始化
init_eepom(); // 读取保存的阈值
while(1) {
key(); // 按键扫描
key_with(); // 按键处理
display(); // 数码管显示
if(flag_300ms) {
flag_300ms = 0;
send_wave(); // 每300ms触发一次测距
}
}
}
定时器0用于测量Echo高电平时间,定时器1产生1ms中断用于数码管扫描和按键检测。我建议把超声波触发间隔设在200-500ms,太频繁会影响显示刷新,太慢则响应延迟。
3.2 超声波测距核心代码
void send_wave() {
c_send = 1; // 触发信号
delay_10us(); // 延时10us
c_send = 0;
TH0 = 0; // 定时器清零
TL0 = 0;
while(!c_recive); // 等待回波开始
TR0 = 1; // 启动定时
while(c_recive) { // 等待回波结束
flag_time0 = TH0 * 256 + TL0;
if(flag_time0 > 65000) { // 超时处理
TR0 = 0;
distance = 888; // 显示888表示超范围
break;
}
}
TR0 = 0;
// 计算距离(单位:cm)
distance = (TH0 * 256 + TL0) * 0.017; // 0.017=340/2/10000
if(distance > 350) distance = 888; // 超过3.5m显示888
}
这里有个重要细节:0.017这个系数的由来。声速340m/s = 34000cm/s,换算成cm/μs是0.034。因为时间是往返时间,所以要除以2,得到0.017。实际测量时,这个值需要根据温度微调。
3.3 温度补偿算法
为了提高精度,我加入了DS18B20温度传感器进行声速补偿:
float get_speed_by_temperature(float temp) {
// 声速 = 331.5 + 0.607 * 温度
return 331.5 + 0.607 * temp;
}
void calculate_distance() {
float temperature = read_temperature();
float speed = get_speed_by_temperature(temperature);
distance = (time * speed / 2) * 100; // 换算成cm
}
实测显示,加入温度补偿后,在0-40℃范围内,误差从原来的3%降低到0.5%以内。
3.4 按键处理与EEPROM存储
设置按键采用短按切换模式、长按进入设置的方案:
- K1:切换显示模式(当前距离/报警阈值)
- K2:阈值加1(长按连加)
- K3:阈值减1(长按连减)
阈值保存在STC89C52的EEPROM中(其实是Data Flash模拟的),掉电不丢失。写入前要先擦除整个扇区:
void write_eepom() {
SectorErase(0x2000); // 擦除扇区
byte_write(0x2000, set_d % 256); // 存储低字节
byte_write(0x2001, set_d / 256); // 存储高字节
}
4. 实物制作与调试技巧
4.1 PCB布局建议
我第一次画板时没注意这些,结果测量结果跳动很大。后来重新布局,稳定性明显改善。
4.2 常见问题解决
问题1:测量结果总是888(超范围)
- 检查Trig信号是否正常(用示波器看10us脉冲)
- 确保Echo信号线连接正确
- 对象是否在测量范围内(2cm-4m)
问题2:测量值跳动大
- 在VCC和GND之间加100uF电解电容
- 软件上采用多次测量取平均值的算法
- 避免测量表面柔软或倾斜的物体
问题3:数码管显示闪烁
- 调整动态扫描频率(我一般用150Hz)
- 检查位选驱动能力,必要时加三极管驱动
问题4:蜂鸣器不响或声音小
- 检查三极管引脚是否接错(9012是PNP型)
- 蜂鸣器是否支持5V驱动(有的需要12V)
4.3 校准方法
即使有温度补偿,仍然需要现场校准:
我通常在不同距离(20cm、50cm、100cm)各测一次,取平均补偿系数。
5. 优化与扩展方向
这个基础版本完成后,还可以进一步优化:
软件滤波算法:
采用滑动平均滤波+中值滤波,能有效消除突发干扰:
#define FILTER_LEN 5
uint16_t filter_buf[FILTER_LEN];
uint16_t distance_filter(uint16_t new_val) {
// 滑动窗口
for(int i = 0; i < FILTER_LEN-1; i++) {
filter_buf[i] = filter_buf[i+1];
}
filter_buf[FILTER_LEN-1] = new_val;
// 中值滤波
bubble_sort(filter_buf, FILTER_LEN);
return filter_buf[FILTER_LEN/2];
}
低功耗设计:
如果用于电池供电,可以加入休眠模式:
- 正常模式下每秒测量一次
- 无变化时进入休眠,每10秒唤醒一次
- 按键或距离变化时立即唤醒
实测休眠模式下,整机电流从20mA降到0.5mA以下。
无线传输功能:
可以添加蓝牙模块(HC-05)或WiFi模块(ESP8266),将测量数据发送到手机或服务器。我在一个智能车库项目中就这样做,当车辆停靠位置不到位时,手机APP会收到提醒。
最终完成的系统测量稳定,显示清晰,报警及时。特别是加入了温度补偿和软件滤波后,在不同环境下都能保持较高的测量精度。对于初学者来说,从这个项目可以学到单片机编程、传感器应用、PCB设计、调试技巧等实用技能,是个很好的综合实践项目。
网硕互联帮助中心





评论前必须登录!
注册