51单片机实时温度监测系统(DS18B20 + LCD1602)

一、系统概述

基于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 头文件与引脚定义

#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晶振)

// 微秒延时(误差±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单总线驱动

// 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 温度采集与转换

// 读取温度值(返回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显示驱动

// 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 主函数

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/contentcnt/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Ω排阻),避免高阻态。

五、测试与验证

  1. 硬件连接:按2.2节连接DS18B20(P3.7)和LCD1602(P0+P2.0~P2.2),确保上拉电阻正确焊接。

  2. 功能测试:用手触摸DS18B20,观察LCD显示温度是否上升(如从25.0℃升至30.5℃)。

  3. 精度验证:对比标准温度计,误差<±0.5℃(室温下)。

六、总结

基于51单片机和DS18B20实现了实时温度监测,核心是单总线通信时序和温度数据解析。通过LCD1602直观显示,可扩展为温度报警(超阈值蜂鸣器响)、串口上传(PC端记录)等功能,满足低成本温度监测需求。

posted @ 2026-04-06 17:00  yes_go  阅读(53)  评论(0)    收藏  举报