1. 项目概述:基于DS18B20的多终端温度监控系统
这个温度监控系统是我在完成一个工业环境监测项目时开发的实用方案,核心功能是通过DS18B20数字温度传感器采集环境温度,同时在1602液晶屏和PC端上位机实时显示。系统采用STM32F103C8T6作为主控芯片,通过单总线协议与传感器通信,并利用串口将数据发送到自行开发的VB6上位机软件进行波形展示。
在实际应用中,这类系统常见于实验室恒温控制、食品冷链监控等场景。我选择DS18B20的原因在于它的数字输出特性(免去了模拟传感器需要的ADC电路)、±0.5℃的精度以及独特的单总线接口——只需要一根数据线加地线即可完成通信,非常适合布线受限的场合。不过正如后来踩坑发现的,这种简洁的硬件设计背后是严苛的时序要求。
2. 硬件设计与关键元件选型
2.1 传感器电路设计
DS18B20的典型应用电路看似简单,但细节决定成败。我的最终稳定方案包含以下关键点:
- 上拉电阻选择:数据线必须接4.7kΩ上拉电阻(尝试过10kΩ会导致通信不稳定)
- 电源去耦:在传感器VDD引脚就近放置0.1μF陶瓷电容
- 布线隔离:传感器数据线要远离MCU的晶振和高速信号线
重要教训:初期将传感器直接插在面包板上导致温度读数跳动,后来改用20cm长的屏蔽双绞线连接后,干扰问题完全消失。这是因为DS18B20的工作电流在温度转换时会突变,长导线等效电感会导致电源波动。
2.2 显示模块接口优化
1602液晶模块采用4位数据总线连接方式(D4-D7),相比8位模式节省了4个IO口。关键改进点:
- 对比度调节:通过10kΩ电位器分压提供VEE电压
- 背光限流:串联120Ω电阻限制电流在15mA左右
- 布线简化:将RS、RW、E控制线归为一组走线
实测发现,当LCD刷新率超过2Hz时,8位模式会出现明显的字符残影,而4位模式在5Hz刷新下仍能保持清晰显示。
3. 软件架构与核心代码实现
3.1 单总线通信协议实现
DS18B20的1-Wire协议对时序要求极为严格,以下是经过验证的稳定实现:
c复制// 单总线复位脉冲(480-960μs低电平+15-60μs高电平)
uint8_t DS18B20_Reset(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置为开漏输出
GPIO_InitStruct.Pin = DS18B20_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
// 产生复位脉冲
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET);
delay_us(480); // 精确延时
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET);
// 切换为输入模式检测应答
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
delay_us(60);
uint8_t ret = (HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN) == GPIO_PIN_RESET);
delay_us(420);
return ret;
}
关键经验:
- 必须使用开漏输出模式,避免总线竞争
- 延时函数要避开中断干扰(关闭全局中断或使用硬件定时器)
- 每次通信前必须成功执行复位序列
3.2 温度数据读取与处理
DS18B20的温度值以16位补码形式存储,转换流程如下:
- 发送转换命令(0x44)
- 等待转换完成(典型750ms@12位分辨率)
- 发送读取命令(0xBE)
- 读取9字节暂存器(前2字节为温度值)
c复制float DS18B20_GetTemp(void) {
int16_t temp_raw;
uint8_t temp_lsb, temp_msb;
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0x44); // 启动转换
delay_ms(750); // 等待转换
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); // 读取暂存器
temp_lsb = DS18B20_ReadByte();
temp_msb = DS18B20_ReadByte();
temp_raw = (temp_msb << 8) | temp_lsb;
return temp_raw * 0.0625; // 12位分辨率时LSB=0.0625℃
}
数据处理技巧:
- 采用三取中值滤波:连续读取三次,取中间值作为最终结果
- 温度突变检测:当相邻两次读数差值超过2℃时触发重新校验
4. 多终端显示实现
4.1 LCD1602显示优化
1602液晶的显示优化包括自定义字符和刷新策略:
c复制// 自定义摄氏度符号
const uint8_t degreeChar[8] = {
0b00110,
0b01001,
0b01001,
0b00110,
0b00000,
0b00000,
0b00000,
0b00000
};
void LCD_ShowTemp(float temp) {
char buf[16];
LCD_CreateChar(0, degreeChar); // 写入CGRAM
sprintf(buf, "Temp:%6.2f\xDFC", temp); // \xDF为自定义字符代码
LCD_SetCursor(0, 0);
LCD_Print(buf);
// 第二行显示更新时间和状态
static uint32_t last_update = 0;
uint32_t interval = HAL_GetTick() - last_update;
sprintf(buf, "Update:%3lums", interval);
LCD_SetCursor(0, 1);
LCD_Print(buf);
last_update = HAL_GetTick();
}
显示优化点:
- 采用半字节(4bit)传输模式,减少IO操作时间
- 只在温度变化超过0.1℃时更新显示
- 第二行显示系统状态信息(刷新间隔、错误计数等)
4.2 串口通信协议设计
稳定的串口通信需要解决数据完整性和实时性问题,我的方案是:
- 通信参数:115200bps, 8数据位, 无校验, 1停止位
- 数据帧格式:
- 帧头:0xAA(1字节)
- 温度数据:IEEE754浮点数(4字节)
- 校验和:异或校验(1字节)
- 帧尾:0x55(1字节)
c复制void USART_SendTemperature(float temp) {
uint8_t frame[7], checksum = 0;
uint8_t *p = (uint8_t*)&temp;
frame[0] = 0xAA; // 帧头
for(int i=0; i<4; i++) {
frame[i+1] = p[i];
checksum ^= p[i];
}
frame[5] = checksum;
frame[6] = 0x55; // 帧尾
HAL_UART_Transmit(&huart1, frame, sizeof(frame), 10);
}
抗干扰措施:
- 每帧发送前清空发送缓冲区
- 接收端超时机制(500ms无数据视为帧丢失)
- 重要数据采用三重复发机制
5. VB6上位机开发实战
5.1 串口数据处理
VB6通过MSComm控件接收数据,核心处理逻辑:
vb复制Private Sub MSComm1_OnComm()
Static buffer() As Byte
Static ptr As Integer
If MSComm1.CommEvent = comEvReceive Then
Dim newData() As Byte
newData = MSComm1.Input
' 处理粘包
ReDim Preserve buffer(ptr + UBound(newData))
For i = 0 To UBound(newData)
buffer(ptr + i) = newData(i)
Next
ptr = ptr + UBound(newData) + 1
' 查找完整帧
Do
Dim frameStart As Integer
frameStart = FindFrameStart(buffer)
If frameStart >= 0 And ptr - frameStart >= 7 Then
If buffer(frameStart + 6) = &H55 Then ' 检查帧尾
Dim checksum As Byte
checksum = 0
For i = 1 To 4
checksum = checksum Xor buffer(frameStart + i)
Next
If checksum = buffer(frameStart + 5) Then
' 提取温度值
Dim tempBytes(0 To 3) As Byte
CopyMemory tempBytes(0), buffer(frameStart + 1), 4
currentTemp = ByteArrayToSingle(tempBytes)
' 移除已处理数据
Dim remaining As Integer
remaining = ptr - (frameStart + 7)
If remaining > 0 Then
CopyMemory buffer(0), buffer(frameStart + 7), remaining
End If
ptr = remaining
End If
End If
Else
Exit Do
End If
Loop
End If
End Sub
5.2 实时波形显示优化
初期直接绘制导致界面卡顿,改进方案采用双缓冲技术:
- 创建内存DC作为后台缓冲区
- 所有绘制操作在内存DC完成
- 每100ms将内存DC内容一次性复制到显示DC
vb复制Private Sub DrawWaveform(temp As Single)
Static lastX As Integer
Static lastY As Integer
Static waveDC As Long
Static waveBmp As Long
' 初始化双缓冲
If waveDC = 0 Then
Dim rect As RECT
GetClientRect picWave.hWnd, rect
waveDC = CreateCompatibleDC(picWave.hDC)
waveBmp = CreateCompatibleBitmap(picWave.hDC, rect.Right, rect.Bottom)
SelectObject waveDC, waveBmp
End If
' 计算坐标
Dim newX As Integer
Dim newY As Integer
newX = lastX + 2
If newX >= picWave.ScaleWidth Then
newX = 0
BitBlt waveDC, 0, 0, picWave.ScaleWidth, picWave.ScaleHeight, _
waveDC, 0, 0, WHITENESS
End If
newY = picWave.ScaleHeight - (temp - tempMin) * yScale
' 绘制波形
MoveToEx waveDC, lastX, lastY, ByVal 0&
LineTo waveDC, newX, newY
lastX = newX: lastY = newY
' 更新显示
BitBlt picWave.hDC, 0, 0, picWave.ScaleWidth, picWave.ScaleHeight, _
waveDC, 0, 0, SRCCOPY
picWave.Refresh
End Sub
性能优化点:
- 采用ScaleWidth/Height代替固定尺寸,适应窗口缩放
- 纵坐标自动缩放,始终显示完整温度范围
- 每100点数据保存一次到日志文件
6. 系统调试与问题排查
6.1 常见故障现象及解决方法
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| LCD显示乱码 | 初始化时序不正确 | 检查EN使能脉冲宽度(>450ns) |
| 温度读数固定85℃ | 传感器未收到转换命令 | 验证1-Wire复位序列 |
| 上位机曲线断断续续 | 串口缓冲区溢出 | 降低发送频率或提高波特率 |
| DS18B20无响应 | 上拉电阻值过大 | 更换为4.7kΩ电阻 |
| VB界面卡顿 | 直接绘制操作 | 改用双缓冲技术 |
6.2 Proteus仿真注意事项
-
DS18B20模型参数设置:
- 在元件属性中设置初始温度(默认为85℃)
- 修改温度变化斜率(默认较慢)
-
串口调试技巧:
- 使用虚拟串口工具连接仿真和上位机
- 在Proteus中添加电压探针监控1-Wire信号
-
时序验证:
- 利用逻辑分析仪查看1-Wire波形
- 检查复位脉冲宽度(480-960μs)
- 验证采样点位置(在应答脉冲后15-60μs)
7. 项目扩展与改进方向
在实际部署这个系统后,我总结了几个有价值的改进方向:
-
低功耗优化:
- 使用DS18B20的寄生供电模式
- 采用间歇工作模式(每分钟唤醒一次)
- 选择LDO稳压器降低静态电流
-
无线传输方案:
- 替换串口为HC-12无线模块
- 增加简单的TDMA协议避免冲突
- 添加RSSI指示功能判断信号强度
-
多传感器组网:
- 利用1-Wire的ROM搜索功能
- 实现动态传感器识别
- 构建树状拓扑网络
-
上位机功能增强:
- 增加温度报警阈值设置
- 实现数据导出为Excel格式
- 添加远程监控功能(通过TCP/IP)
这个项目最让我满意的不是最终实现的功能,而是在解决各种奇怪问题时积累的实战经验。比如发现DS18B20的转换时间会随温度升高而增加(0℃时750ms,100℃时长达1.2s),这种细节在数据手册的角落里才有说明。还有VB6的双缓冲实现,经过三次重构才达到理想的流畅度。这些经验让我深刻体会到,嵌入式开发就是要在理论和实践的不断碰撞中成长。
