一、系统概述
基于STC89C52RC单片机(11.0592MHz晶振),通过DS18B20数字温度传感器实现环境温度实时采集,结合LCD1602液晶显示模块输出温度值(支持正负温度显示,精度±0.5℃),适用于温室监控、家电温度检测等场景。系统采用单总线通信协议,仅需1个IO口连接传感器,硬件简单,软件模块化设计。
二、硬件设计
2.1 核心组件
| 模块 | 型号/参数 | 功能说明 |
|---|---|---|
| 主控 | STC89C52RC(8位,11.0592MHz) | 温度采集、数据处理、LCD驱动 |
| 温度传感器 | DS18B20(单总线,数字输出) | 检测环境温度(-55~+125℃,精度±0.5℃) |
| 显示模块 | LCD1602(16×2字符型液晶) | 显示温度值(格式:Temp: XX.X℃) |
| 电源 | 5V DC电源(或USB供电) | 为单片机和传感器供电 |
2.2 硬件连接
| 模块 | 引脚(STC89C52RC) | 说明 |
|---|---|---|
| DS18B20 | P3.7(DQ) | 单总线数据引脚(需4.7kΩ上拉电阻) |
| LCD1602 | RS→P2.0,RW→P2.1,E→P2.2 | 控制引脚 |
| D0~D7→P0口 | 数据总线(8位并行) | |
| 电源 | VCC→5V,GND→GND | 共地连接 |
三、软件设计(Keil C51)
3.1 开发环境
-
IDE:Keil μVision5(C51编译器)
-
晶振:11.0592MHz(确保延时精度与串口兼容性)
-
通信协议:DS18B20单总线协议(1-Wire)
3.2 核心代码实现
3.2.1 头文件与引脚定义
c
#include <reg52.h>
#include <intrins.h>
// ==================== 引脚定义 ====================
#define DQ P3_7 // DS18B20数据引脚(单总线)
#define LCD_RS P2_0 // LCD1602 RS
#define LCD_RW P2_1 // LCD1602 RW
#define LCD_E P2_2 // LCD1602 E
#define LCD_DB P0 // LCD1602数据总线(D0~D7)
// ==================== 全局变量 ====================
unsigned char temp_int = 0; // 温度整数部分
unsigned char temp_frac = 0; // 温度小数部分(0.1℃精度)
bit temp_sign = 0; // 温度符号(0=正,1=负)
3.2.2 延时函数(微秒/毫秒级,11.0592MHz晶振)
c
// 微秒延时(误差±1us,@11.0592MHz,1机器周期≈0.907us)
void DelayUs(unsigned int us) {
while (us--) {
_nop_(); _nop_(); _nop_(); _nop_(); // 4个_nop_≈3.6us,需调整循环次数
_nop_(); _nop_(); _nop_(); _nop_();
}
}
// 毫秒延时(@11.0592MHz,1ms≈1100机器周期)
void DelayMs(unsigned int ms) {
unsigned int i, j;
for (i=0; i<ms; i++)
for (j=0; j<110; j++); // 110次循环≈1ms
}
3.2.3 DS18B20单总线驱动
c
// DS18B20初始化(检测设备是否存在)
bit DS18B20_Init() {
bit ack;
DQ = 1; DelayUs(2); // 总线拉高
DQ = 0; DelayUs(500); // 复位脉冲(480~960us)
DQ = 1; DelayUs(60); // 释放总线,等待应答
ack = DQ; // 读取应答信号(0=存在,1=不存在)
DelayUs(420); // 等待初始化完成
return !ack; // 返回1表示设备存在
}
// 向DS18B20写一个字节
void DS18B20_WriteByte(unsigned char dat) {
unsigned char i;
for (i=0; i<8; i++) {
DQ = 0; _nop_(); // 拉低总线(写时隙开始)
DQ = dat & 0x01; // 输出最低位
DelayUs(60); // 保持60us(写0时持续60us,写1时拉高后释放)
DQ = 1; _nop_(); // 释放总线
dat >>= 1; // 准备下一位
}
}
// 从DS18B20读一个字节
unsigned char DS18B20_ReadByte() {
unsigned char i, dat = 0;
for (i=0; i<8; i++) {
DQ = 0; _nop_(); // 拉低总线(读时隙开始)
dat >>= 1;
DQ = 1; _nop_(); // 释放总线
if (DQ) dat |= 0x80; // 读取数据位(高电平为1)
DelayUs(60); // 保持60us
}
return dat;
}
3.2.4 温度采集与转换
c
// 读取温度值(返回0成功,1失败)
bit DS18B20_ReadTemp() {
unsigned char temp_l, temp_h;
if (!DS18B20_Init()) return 1; // 初始化失败
DS18B20_WriteByte(0xCC); // 跳过ROM命令(单设备)
DS18B20_WriteByte(0x44); // 启动温度转换(12位精度,需750ms)
DelayMs(750); // 等待转换完成
if (!DS18B20_Init()) return 1; // 重新初始化
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0xBE); // 读取温度寄存器(共2字节)
temp_l = DS18B20_ReadByte(); // 低8位(小数部分+低4位整数)
temp_h = DS18B20_ReadByte(); // 高8位(高4位整数+符号位)
// 解析温度值(12位精度:高5位符号位,中间7位整数,低4位小数)
if (temp_h & 0x80) { // 负数(符号位为1)
temp_sign = 1;
temp_h = ~temp_h; // 取反
temp_l = ~temp_l + 1; // 补码转换
if (temp_l == 0) temp_h++; // 进位
} else {
temp_sign = 0;
}
// 计算整数和小数部分(0.0625℃/LSB,保留1位小数)
temp_int = (temp_h << 4) | (temp_l >> 4); // 整数部分(高4位+低4位)
temp_frac = (temp_l & 0x0F) * 625 / 1000; // 小数部分(0.0625 * 1000=62.5,取0.1℃精度)
return 0;
}
3.2.5 LCD1602显示驱动
c
// LCD写命令
void LCD_WriteCmd(unsigned char cmd) {
LCD_RS = 0; LCD_RW = 0;
LCD_DB = cmd; DelayUs(5);
LCD_E = 1; DelayUs(5); LCD_E = 0;
}
// LCD写数据
void LCD_WriteData(unsigned char dat) {
LCD_RS = 1; LCD_RW = 0;
LCD_DB = dat; DelayUs(5);
LCD_E = 1; DelayUs(5); LCD_E = 0;
}
// LCD初始化
void LCD_Init() {
LCD_WriteCmd(0x38); // 8位数据,2行显示,5×7点阵
LCD_WriteCmd(0x0C); // 开显示,关光标
LCD_WriteCmd(0x06); // 光标右移,不滚动
LCD_WriteCmd(0x01); // 清屏
DelayMs(2);
}
// 显示温度值(格式:Temp: XX.X℃ 或 -XX.X℃)
void LCD_DisplayTemp() {
LCD_WriteCmd(0x80); // 第一行首地址
LCD_WriteData('T'); LCD_WriteData('e'); LCD_WriteData('m'); LCD_WriteData('p');
LCD_WriteData(':'); LCD_WriteData(' ');
if (temp_sign) { // 负数
LCD_WriteData('-');
if (temp_int < 10) LCD_WriteData('0'); // 补零(如-5.5℃显示为-05.5℃)
LCD_WriteData(temp_int/10 + '0');
LCD_WriteData(temp_int%10 + '0');
} else { // 正数
if (temp_int < 10) LCD_WriteData(' '); // 对齐(如 5.5℃显示为 5.5℃)
else {
LCD_WriteData(temp_int/10 + '0');
LCD_WriteData(temp_int%10 + '0');
}
}
// 显示小数部分
LCD_WriteData('.');
LCD_WriteData(temp_frac + '0'); // 0.1℃精度(0~9对应0.0~0.9℃)
LCD_WriteData(0xDF); // ℃符号(LCD字库中的特殊字符,需自定义或查表)
LCD_WriteData('C');
}
3.2.6 主函数
c
void main() {
LCD_Init(); // 初始化LCD
DelayMs(100); // 等待LCD稳定
while (1) {
if (DS18B20_ReadTemp() == 0) { // 读取温度成功
LCD_DisplayTemp(); // 显示温度
} else { // 读取失败(显示错误)
LCD_WriteCmd(0x80);
LCD_WriteData('E'); LCD_WriteData('r'); LCD_WriteData('r');
}
DelayMs(1000); // 1秒刷新一次
}
}
参考代码 51单片机实时温度源码 www.youwenfan.com/contentcst/182665.html
四、关键问题与解决方案
4.1 单总线时序错误
-
问题:DS18B20对时序要求严格,延时误差导致通信失败。
-
解决:
-
用示波器校准
DelayUs函数(11.0592MHz下,1us≈1.085机器周期,需调整循环次数); -
确保
DS18B20_Init中复位脉冲>480us,应答检测>60us。
4.2 温度值跳变
-
问题:环境干扰或电源波动导致读数异常。
-
解决:
-
软件添加滑动平均滤波(连续3次采样取平均);
-
电源端并联100μF电解电容+0.1μF瓷片电容滤波。
4.3 LCD显示乱码
-
问题:LCD初始化时序错误或数据总线接触不良。
-
解决:
-
检查
LCD_Init中命令顺序(0x38→0x0C→0x06→0x01); -
确保P0口加上拉电阻(10kΩ排阻),避免高阻态。
五、测试与验证
-
硬件连接:按2.2节连接DS18B20(P3.7)和LCD1602(P0+P2.0~P2.2),确保上拉电阻正确焊接。
-
功能测试:用手触摸DS18B20,观察LCD显示温度是否上升(如从25.0℃升至30.5℃)。
-
精度验证:对比标准温度计,误差<±0.5℃(室温下)。
六、总结
基于51单片机和DS18B20实现了实时温度监测,核心是单总线通信时序和温度数据解析。通过LCD1602直观显示,可扩展为温度报警(超阈值蜂鸣器响)、串口上传(PC端记录)等功能,满足低成本温度监测需求。