1. 项目概述与设计思路
这个基于AT89C51单片机的数字温度计项目,是我在指导学生完成课程设计时开发的一个经典案例。整个系统以AT89C51为核心控制器,搭配DS18B20数字温度传感器和四位共阴数码管,实现了-28℃~28℃范围内的温度测量与显示,精度达到0.1℃。当温度超出设定范围时,系统会通过蜂鸣器发出警报。
选择这个方案主要基于几个关键考量:
- 成本控制:AT89C51作为经典51单片机,价格低廉且资源足够满足需求;DS18B20采用单总线通信,节省I/O口线
- 精度要求:DS18B20具有±0.5℃的精度,12位分辨率下可达0.0625℃,完全满足日常测温需求
- 系统复杂度:数码管显示驱动简单,不需要复杂的驱动电路,整体BOM成本控制在30元以内
实际开发中发现,DS18B20的时序要求非常严格,初始化时序中的480μs复位脉冲和15-60μs的存在脉冲必须精确控制,这是整个项目第一个需要攻克的难点。
2. 硬件设计详解
2.1 核心器件选型
AT89C51单片机:
- 4KB Flash存储器,足够存储整个程序
- 128B RAM,满足临时数据存储需求
- 32个I/O口,本项目实际使用P0口(数码管段选)、P2口(位选)、P1.0(DS18B20数据线)、P3.7(蜂鸣器控制)
DS18B20温度传感器:
- 工作电压:3.0V~5.5V(本项目采用5V供电)
- 测温范围:-55℃~+125℃
- 9~12位可调分辨率(本项目使用默认12位)
- 单总线接口,节省I/O资源
2.2 关键电路设计
2.2.1 传感器接口电路
circuit复制DS18B20
┌───────┐
│ │ VDD → 5V
│ │ DQ → P1.0 (接4.7K上拉电阻)
│ │ GND → GND
└───────┘
设计要点:
- 必须添加4.7K上拉电阻,否则无法正常通信
- 传感器距离单片机最好不超过20米(实际使用建议<5米)
- 寄生供电模式下,VDD需接地,此时对时序要求更严格
2.2.2 数码管显示电路
采用四位共阴数码管动态扫描方式:
- 段选:P0口通过74HC245驱动(增加驱动能力)
- 位选:P2.0-P2.3通过PNP三极管控制(如8550)
c复制// 数码管驱动示例代码
void Display_Temperature(float temp) {
uint8_t digits[4];
// 温度数据处理
if(temp < 0) {
digits[0] = 0xBF; // 显示负号
temp = -temp;
} else {
digits[0] = SegTable[(int)temp/10]; // 十位
}
digits[1] = SegTable[(int)temp%10]; // 个位
digits[2] = SegTable[(int)(temp*10)%10] | 0x80; // 小数位(带小数点)
// 动态扫描显示
for(int i=0; i<3; i++) {
P2 = ~(1 << i); // 位选
P0 = digits[i]; // 段选
delay_ms(2); // 保持2ms
}
}
2.2.3 报警电路设计
蜂鸣器驱动采用NPN三极管(如8050):
- 基极通过1K电阻接P3.7
- 蜂鸣器接在集电极和VCC之间
- 当温度超限时,P3.7输出方波驱动蜂鸣器
3. 软件实现关键点
3.1 DS18B20驱动开发
DS18B20的严格时序要求是软件难点,必须精确控制微秒级延时:
c复制// 精确延时函数(12MHz晶振)
void Delay_us(uint16_t us) {
while(us--) {
_nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_(); _nop_(); _nop_();
}
}
// 复位脉冲(480μs以上)
uint8_t DS18B20_Reset() {
uint8_t presence = 0;
DQ = 0;
Delay_us(480); // 保持480μs低电平
DQ = 1;
Delay_us(60); // 等待60μs
presence = DQ; // 读取存在脉冲
Delay_us(420); // 等待剩余时间
return presence; // 0=存在,1=不存在
}
3.2 温度读取流程
- 发送转换命令(0x44)
- 等待转换完成(750ms@12位分辨率)
- 发送读取命令(0xBE)
- 读取两个字节的温度数据
- 转换为实际温度值
c复制float Read_Temperature() {
uint8_t tempL, tempH;
uint16_t temp;
DS18B20_Reset();
Write_Byte(0xCC); // 跳过ROM
Write_Byte(0x44); // 启动转换
Delay_ms(750); // 等待转换完成
DS18B20_Reset();
Write_Byte(0xCC);
Write_Byte(0xBE); // 读取暂存器
tempL = Read_Byte();
tempH = Read_Byte();
temp = (tempH << 8) | tempL;
return temp * 0.0625; // 12位分辨率
}
3.3 主程序逻辑
c复制void main() {
float temperature;
Init_System(); // 初始化IO、定时器等
while(1) {
temperature = Read_Temperature();
Display_Temperature(temperature);
// 报警判断
if(temperature > 28.0 || temperature < -28.0) {
Buzzer_Alarm(1); // 启动报警
} else {
Buzzer_Alarm(0); // 关闭报警
}
Delay_ms(500); // 500ms刷新一次
}
}
4. 调试经验与问题解决
4.1 常见问题排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 数码管不亮 | 1. 位选/段选接反 2. 驱动能力不足 |
1. 检查电路连接 2. 增加驱动芯片 |
| 显示乱码 | 1. 段码表错误 2. 动态扫描间隔不当 |
1. 核对段码表 2. 调整扫描频率 |
| DS18B20无响应 | 1. 上拉电阻缺失 2. 时序不准确 |
1. 添加4.7K上拉 2. 用示波器调试时序 |
| 温度值跳变 | 1. 电源干扰 2. 传感器接触不良 |
1. 增加滤波电容 2. 检查连接 |
4.2 实测波形分析
使用示波器抓取DS18B20通信波形时,要特别注意:
- 复位阶段:单片机拉低DQ线480μs以上,然后释放,DS18B20应在15-60μs内拉低回应
- 写0:拉低至少60μs,整个时隙>60μs
- 写1:拉低1-15μs,然后释放
实际调试中发现,当环境温度低于0℃时,读取的温度值需要特殊处理。DS18B20返回的是16位补码形式,需先判断符号位(bit15),若为1则表示负温,需要取反加1后再转换。
5. 系统优化建议
-
低功耗优化:
- 使用DS18B20的寄生供电模式
- 单片机进入空闲模式,定时唤醒测温
- 数码管采用更低的扫描频率
-
功能扩展:
c复制// 添加温度校准函数 void Temperature_Calibration(float offset) { g_tempOffset = offset; // 存储校准值 } // 修改读取函数 float Get_Temperature() { return Read_Temperature() + g_tempOffset; } -
显示增强:
- 添加LED指示灯:绿色=正常,红色=超限
- 采用LCD显示,可同时显示当前温度和设定阈值
-
通信接口:
- 添加UART接口,可将温度数据上传至PC
- 通过蓝牙模块实现无线监控
这个项目虽然基础,但涵盖了单片机开发的完整流程:从器件选型、电路设计、编程实现到调试优化。特别是在时序控制方面,DS18B20的严格时序要求对初学者是很好的锻炼。在实际应用中,这种基础温度检测系统可以扩展为恒温控制、环境监测等更复杂的系统。