1. 项目概述
最近在调试一个基于51单片机的超声波测距系统时,发现了一个有趣的现象:同样的距离测量,在早晨和中午的测量结果竟然有2-3cm的偏差。经过排查才发现,原来是温度变化影响了超声波的传播速度。这个发现促使我开发了这个带温度补偿的双路超声波测距系统。
这个系统使用STC89C52单片机作为主控,配合HC-SR04超声波模块实现距离测量,DS18B20数字温度传感器进行环境温度采集,最终通过LCD1602液晶屏显示测量结果。特别之处在于,系统会根据实时温度对超声波测距结果进行动态补偿,显著提高了测量精度。
2. 硬件设计详解
2.1 核心器件选型
单片机选择:
我选用STC89C52RC这款经典51单片机,主要基于以下考虑:
- 8KB Flash存储空间足够存放整个程序
- 512B RAM满足变量存储需求
- 32个I/O口完全满足外设连接需求
- 价格低廉(约5元/片)且易于购买
超声波模块:
HC-SR04模块因其性价比高(约3元/个)、测量范围广(2cm-4m)、精度较高(约3mm)而成为首选。需要注意的是,市场上有些劣质模块的测量稳定性较差,建议选择带有蓝色PCB的版本。
温度传感器:
DS18B20的优势在于:
- 数字输出,抗干扰能力强
- ±0.5℃的测量精度
- 单总线接口节省I/O资源
- 防水封装版本可直接用于户外环境
显示模块:
LCD1602液晶屏虽然显示内容有限,但具有以下优点:
- 接口简单,驱动方便
- 可视性好,功耗低
- 价格便宜(约8元/片)
2.2 电路连接方案
完整的电路连接如下表示:
| 模块 | 引脚连接 | 备注 |
|---|---|---|
| HC-SR04(1) | Trig -> P1.0 | 第一路超声波触发信号 |
| Echo -> P1.1 | 第一路超声波回波信号 | |
| HC-SR04(2) | Trig -> P1.2 | 第二路超声波触发信号 |
| Echo -> P1.3 | 第二路超声波回波信号 | |
| DS18B20 | DQ -> P2.2 | 温度传感器数据线 |
| LCD1602 | RS -> P2.0 | 寄存器选择 |
| RW -> P2.1 | 读写控制 | |
| E -> P2.3 | 使能信号 | |
| D0-D7 -> P0.0-P0.7 | 数据总线 |
注意:DS18B20的数据线需要接4.7KΩ上拉电阻,这个细节容易被忽略但至关重要。
3. 关键算法实现
3.1 超声波测距原理
超声波测距的基本公式为:
距离 = (声速 × 传播时间) / 2
其中声速与温度的关系为:
V = 331.4 + 0.607 × T (T为摄氏温度)
在代码中,我们这样实现:
c复制// 温度补偿的声速计算
float soundSpeed = 331.4 + 0.607 * temperature;
// 距离计算(单位:cm)
distance = (time * 0.000001 * soundSpeed) / 2 * 100;
这里time是超声波往返时间(微秒),乘以0.000001转换为秒,再乘以声速得到往返距离,最后除以2得到实际距离,乘以100转换为厘米。
3.2 温度测量实现
DS18B20的驱动相对复杂,需要严格按照时序操作:
- 初始化:主机发出复位脉冲(>480μs低电平)
- ROM命令:跳过ROM检测(0xCC)
- 功能命令:启动温度转换(0x44)
- 等待转换完成(典型750ms)
- 再次初始化
- 发送读取命令(0xBE)
- 读取温度数据(2字节)
关键代码片段:
c复制void getTemperature() {
unsigned char low, high;
if (init18b20()) {
write18b20(0xCC); // 跳过ROM
write18b20(0x44); // 启动转换
delay_ms(750); // 等待转换完成
if (init18b20()) {
write18b20(0xCC);
write18b20(0xBE); // 读取命令
low = read18b20(); // 低字节
high = read18b20(); // 高字节
// 计算温度值
temperature = (high << 8 | low) * 0.0625;
}
}
}
3.3 双路测距的实现策略
为了实现两路超声波同时工作而不互相干扰,我采用了分时复用策略:
- 先触发第一路超声波
- 等待其回波检测完成
- 触发第二路超声波
- 等待其回波检测完成
- 更新显示
这种顺序执行的方式虽然会略微增加测量周期,但保证了测量的可靠性。在实际测试中,完整测量周期约100ms,对于大多数应用已经足够。
4. 软件设计要点
4.1 主程序流程
c复制void main() {
lcd_init(); // 初始化LCD
timer0_init(); // 初始化定时器0用于计时
while(1) {
getTemperature(); // 获取温度
measureDistance1(); // 测量第一路距离
measureDistance2(); // 测量第二路距离
calculateWithCompensation(); // 温度补偿计算
display(); // 显示结果
delay_ms(100); // 适当延时
}
}
4.2 定时器配置
使用定时器0来精确测量超声波传播时间:
c复制void timer0_init() {
TMOD |= 0x01; // 定时器0,模式1(16位)
TH0 = 0; // 初始值高字节
TL0 = 0; // 初始值低字节
ET0 = 1; // 允许定时器0中断
EA = 1; // 开总中断
}
void timer0_isr() interrupt 1 {
// 超时处理
time_out = 1;
}
4.3 显示优化技巧
为了提升LCD显示效果,我采用了以下优化措施:
- 只更新变化的数据,避免全屏刷新造成的闪烁
- 对数值进行平滑滤波处理
- 添加单位显示和格式对齐
显示函数改进版:
c复制void display() {
static float last_temp = -100;
static uint last_dist1 = 0xFFFF;
// 只有温度变化超过0.1度才更新
if(fabs(temperature - last_temp) > 0.1) {
lcd_command(0x80);
lcd_print("Temp:");
lcd_print_float(temperature,1);
lcd_print("C");
last_temp = temperature;
}
// 距离变化超过1cm才更新
if(abs(distance1 - last_dist1) > 1) {
lcd_command(0xC0);
lcd_print("Dist1:");
lcd_print_int(distance1);
lcd_print("cm");
last_dist1 = distance1;
}
}
5. 调试经验分享
5.1 常见问题排查
-
超声波无返回信号
- 检查Trig信号是否正常(10μs以上高电平)
- 确认Echo引脚连接正确
- 测试模块供电电压(5V±10%)
-
DS18B20无法初始化
- 检查上拉电阻(4.7KΩ必须接)
- 确认时序严格符合规范
- 尝试降低单总线通信速度
-
LCD显示乱码
- 检查初始化序列是否正确
- 确认对比度调节电位器设置合适
- 检查总线是否有短路/虚焊
5.2 精度提升技巧
- 对多次测量结果取中值滤波
- 在温度稳定环境中校准距离参数
- 使用示波器检查超声波信号质量
- 避免测量面过于光滑(易产生多次反射)
5.3 Proteus仿真注意事项
- 超声波模块仿真时需设置合理的回波延迟
- DS18B20在仿真中可能需要调整温度参数
- LCD1602的仿真模型有时响应较慢,需增加延时
- 建议先测试各模块单独工作正常后再整合
6. 实际应用扩展
这个基础系统可以根据需要进一步扩展:
- 添加蓝牙模块实现无线数据传输
- 增加按键设置报警阈值
- 改用OLED显示屏提升显示效果
- 添加SD卡存储测量数据
- 开发上位机软件进行数据分析
我在实际项目中曾将其应用于智能小车避障系统,通过两路超声波实现了前方180°范围的障碍物检测,配合温度补偿,在不同环境温度下都能保持稳定的检测性能。
