新手必看:用AT89C51和DS18B20做个温度计,LCD1602显示,代码逐行讲解
从零构建AT89C51温度监测系统:DS18B20与LCD1602实战指南
在嵌入式开发的入门阶段,没有什么比亲手搭建一个完整的硬件系统更能巩固基础知识的了。今天我们要实现的这个温度监测项目,看似简单却涵盖了单片机开发的三大核心要素:传感器数据采集(DS18B20)、主控芯片处理(AT89C51)以及人机交互显示(LCD1602)。不同于单纯复制代码,我们将从电路原理、代码逻辑到调试技巧全方位拆解,让你真正理解每个环节的设计思路。
1. 硬件系统架构设计
1.1 核心元件选型解析
AT89C51作为经典的8位单片机,其40引脚DIP封装特别适合初学者实验。它的4个8位I/O口(P0-P3)为我们提供了充足的接口资源:
- P0口:需外接上拉电阻,在本项目中用于LCD数据总线
- P2口:部分引脚用于LCD控制线
- P3.3:连接DS18B20的单总线接口
DS18B20数字温度传感器的三大优势使其成为首选:
- 单总线接口(仅需1个GPIO)
- 精度达±0.5℃(-10℃至+85℃范围)
- 内置12位ADC,分辨率可达0.0625℃
LCD1602字符型液晶模块的引脚定义需要特别注意:
1-VSS(地) 2-VDD(5V) 3-V0(对比度) 4-RS(寄存器选择) 5-R/W(读写) 6-EN(使能) 7-14 DB0-DB7(数据线) 15-A(背光+) 16-K(背光-)1.2 电路连接示意图
关键连接关系如下表所示:
| 模块 | AT89C51引脚 | 连接说明 |
|---|---|---|
| LCD1602 RS | P2.0 | 命令/数据选择 |
| LCD1602 RW | P2.1 | 读/写控制 |
| LCD1602 EN | P2.2 | 使能信号 |
| LCD1602 DB | P0.0-P0.7 | 8位数据总线 |
| DS18B20 DQ | P3.3 | 单总线数据线 |
实际搭建时,DS18B20建议采用4.7KΩ上拉电阻,VCC与GND之间需加0.1μF去耦电容。
2. 开发环境配置要点
2.1 Keil μVision配置流程
- 新建工程时选择"AT89C51"作为Device
- 设置Target选项:
- 晶体频率设为11.0592MHz(与硬件一致)
- 勾选"Create HEX File"选项
- 添加源文件时的注意事项:
- 包含reg52.h头文件
- 启用C51编译器的优化等级-O2
常见编译错误解决方案:
error C202: 'P3': undefined identifier → 检查是否遗漏了#include <reg52.h> warning C206: 'DelayXus': missing function-prototype → 在文件顶部添加函数声明 void DelayXus(uint x);2.2 Proteus仿真技巧
在绘制原理图时特别注意:
- 为AT89C51添加时钟电路(11.0592MHz晶振+30pF电容)
- DS18B20的仿真模型需要设置初始温度值
- LCD1602的对比度调节端接10K电位器
仿真调试时若遇到LCD无显示:
- 检查EN使能信号是否有脉冲
- 确认RW引脚已接地(写模式)
- 测量V0引脚电压应在0-5V可调
3. 核心代码深度解析
3.1 DS18B20驱动实现
单总线通信时序是难点所在,我们分解为三个关键函数:
初始化序列(检测器件存在):
uchar Init_DS18B20() { uchar status; DQ = 1; // 释放总线 Delay(8); // 等待15μs以上 DQ = 0; // 主机拉低480μs Delay(90); // 实际延时约540μs DQ = 1; // 释放总线 Delay(8); // 等待器件响应 status = DQ; // 读取应答信号 Delay(100); // 等待恢复 return status; // 0=存在,1=不存在 }字节读取函数的时序要点:
uchar ReadOneByte() { uchar i, dat = 0; for(i=0; i<8; i++) { DQ = 0; // 主机拉低开始读时隙 _nop_(); // 保持1μs以上 dat >>= 1; // 先右移准备接收新位 DQ = 1; // 释放总线 _nop_(); // 等待15μs采样窗口 if(DQ) dat |= 0x80; // 读取数据位 Delay(30); // 完成60μs时隙 } return dat; }温度转换与读取流程:
- 发送跳过ROM命令(0xCC)
- 启动温度转换(0x44)
- 等待转换完成(典型750ms@12位分辨率)
- 再次初始化总线
- 发送读取暂存器命令(0xBE)
- 连续读取2字节温度数据
3.2 LCD1602显示驱动
LCD初始化序列需要严格按照时序:
void LCD_Initialise() { Write_LCD_Command(0x38); // 8位总线,2行显示 Write_LCD_Command(0x0C); // 开显示,关光标 Write_LCD_Command(0x06); // 地址自动递增 Write_LCD_Command(0x01); // 清屏 DelayXus(2000); // 清屏需要较长延时 }温度值格式化显示的算法解析:
void Display_Temperature() { // 处理负温度(补码转换) if((Temp_Value[1]&0xf8)==0xf8) { Temp_Value[1] = ~Temp_Value[1]; Temp_Value[0] = ~Temp_Value[0]+1; if(Temp_Value[0]==0x00) Temp_Value[1]++; ng = 1; // 负号标记 } // 提取小数部分(低4位) Display_Digit[0] = df_Table[Temp_Value[0]&0x0f]; // 组合整数部分(高12位) CurrentT = ((Temp_Value[0]&0xf0)>>4) | ((Temp_Value[1]&0x07)<<4); // 十进制转换 Display_Digit[3] = CurrentT/100; // 百位 Display_Digit[2] = CurrentT%100/10; // 十位 Display_Digit[1] = CurrentT%10; // 个位 // 构建显示字符串 Current_Temp_Display_Buffer[11] = Display_Digit[0] + '0'; Current_Temp_Display_Buffer[10] = '.'; Current_Temp_Display_Buffer[9] = Display_Digit[1] + '0'; // ...省略其他位处理... // 显示温度符号 Set_LCD_POS(0x4e); Write_LCD_Data('C'); }4. 系统优化与调试实战
4.1 常见问题排查指南
当系统不能正常工作时,建议按照以下顺序排查:
电源检查
- 测量各芯片VCC与GND间电压是否为5V±10%
- 检查去耦电容是否靠近芯片电源引脚
信号测量
- 用示波器观察DS18B20的DQ线波形
- 检查LCD的EN使能信号是否有500ns以上脉冲
软件调试技巧
- 在Keil中使用软件仿真逐步执行
- 添加测试代码点亮LED指示各阶段状态
4.2 精度提升方案
原始设计的温度分辨率是0.1℃,可通过以下方式优化:
硬件改进:
- 为DS18B20增加金属外壳增强热耦合
- 在电源端增加LC滤波电路(10μH+0.1μF)
软件算法优化:
// 滑动平均滤波示例 #define FILTER_LEN 5 uint16_t temp_history[FILTER_LEN]; uint8_t filter_index = 0; uint16_t Filter_Temperature(uint16_t new_val) { temp_history[filter_index++] = new_val; if(filter_index >= FILTER_LEN) filter_index = 0; uint32_t sum = 0; for(uint8_t i=0; i<FILTER_LEN; i++) { sum += temp_history[i]; } return sum / FILTER_LEN; }4.3 功能扩展思路
基于当前系统可轻松实现的功能升级:
多路温度监测:
- 使用多个DS18B20时,需通过ROM识别不同器件
- 修改代码支持轮流读取各传感器
温度报警功能:
void Check_Alarm(float temp) { if(temp > HIGH_LIMIT) Buzzer_On(); else if(temp < LOW_LIMIT) LED_Alert(); }上位机通信:
- 通过串口将温度数据发送到PC
- 使用Python编写简单的数据记录程序
在面包板上完成原型搭建后,建议使用万用板制作永久性电路。焊接时特别注意DS18B20的引脚顺序,错误的接线可能导致传感器发热甚至损坏。实际测试中发现,当电源电压低于4.5V时,DS18B20的转换精度会显著下降,因此稳定的电源设计至关重要。
