1. 认识DS18B20温度传感器
第一次接触DS18B20是在大学电子设计课上,当时老师要求用51单片机做个温控风扇。这个只有三个引脚的小芯片让我印象深刻——它居然能直接输出数字温度信号!相比需要复杂ADC电路的热敏电阻,DS18B20简直是新手福音。
DS18B20的核心优势在于其单总线通信协议。传统温度传感器需要模拟信号线+电源线+地线,而它只需要一根数据线就能完成供电和通信(寄生供电模式下甚至只需两根线)。实测中发现,在3.0V-5.5V宽电压范围内都能稳定工作,测量范围-55℃到+125℃,精度可达±0.5℃(12位分辨率时)。
这个传感器的内部结构很有意思:
- 64位ROM:相当于身份证号,前8位是家族码(28h代表DS18B20),中间48位是唯一序列号,最后8位是CRC校验码。正是这个设计让多传感器并联成为可能
- 温度灵敏元件:直接输出9-12位的二进制温度值
- 非易失性存储器:可以保存温度报警阈值TH/TL,断电不丢失
实际项目中踩过的坑:寄生供电模式下,当进行温度转换时电流需求骤增(最高1.5mA),如果总线电容不足会导致电压跌落。解决方法是在数据线加4.7kΩ上拉电阻的同时,并联一个100nF电容储能。
2. 单总线协议深度解析
刚开始调DS18B20时,最头疼的就是那个严苛的时序要求。有次调试整整一天都没反应,最后发现是延时函数少了个nop指令。这里把关键时序拆解给大家:
2.1 初始化时序(复位脉冲)
// 51单片机示例代码 bit DS18B20_Init() { DQ = 0; // 拉低总线 delay_us(480); // 保持480-960us DQ = 1; // 释放总线 delay_us(60); // 等待15-60us return DQ; // 0表示存在,1表示不存在 }这个复位脉冲就像"敲门",传感器收到后会回个"应答脉冲"。实测中发现:
- 拉低时间少于480us可能导致无应答
- 应答信号出现在释放总线后15-60us窗口期
- 总线恢复时间至少480us后才能进行下一步操作
2.2 读写时序对比
通过示波器抓取的波形显示(如下图),写0需要保持60us低电平,而写1在拉低2us后就要释放总线:
写时序技巧:
void DS18B20_WriteByte(uchar dat) { for(int i=0; i<8; i++) { DQ = 0; // 拉低开始写周期 _nop_(); _nop_(); // 保持至少1us DQ = dat & 0x01; // 发送最低位 delay_us(60); // 保持写周期 DQ = 1; // 释放总线 dat >>= 1; // 准备下一位 } }读时序要点:
- 主机拉低总线至少1us后释放
- 必须在15us内采样数据线状态
- 整个读时隙不少于60us
- 实测发现温度转换期间读取会返回0xFF
3. 多路测温系统设计
去年给温室大棚做环境监控时,需要在20米范围内布置8个测温点。DS18B20的多点组网能力派上了大用场,具体实现方案如下:
3.1 硬件连接优化
+-----------------+ | 51单片机 | | P3.7 - DQ | +--------+--------+ | +---------------+---------------+ 4.7kΩ | | 上拉电阻 | | +---+ | | | | | === 100nF === 100nF === 100nF | | | +--+--+ +--+--+ +--+--+ | DS1 | | DS2 | ... | DS8 | +-----+ +-----+ +-----+布线经验:
- 总线长度超过10米时改用屏蔽双绞线
- 每增加5个传感器,上拉电阻减小1kΩ
- 分支线长度控制在1米内,避免信号反射
3.2 传感器寻址策略
通过ROM匹配实现精准寻址,流程如下:
- 发送搜索ROM命令(0xF0)
- 读取所有传感器的位数据
- 构建二进制搜索树
- 逐个记录64位ROM码
// 示例ROM搜索代码 void DS18B20_SearchROM(uint8_t *ROM_LIST) { uint8_t last_diff = 0; uint8_t search_dir = 0; Reset_DS18B20(); Write_DS18B20(0xF0); // 搜索ROM命令 for(int bit_idx=0; bit_idx<64; bit_idx++) { int byte_pos = bit_idx/8; int bit_mask = 1<<(bit_idx%8); int bit1 = Read_DS18B20(); int bit2 = Read_DS18B20(); if(bit1 && bit2) { // 无设备响应 break; } else if(bit1 != bit2) { // 所有设备该位相同 ROM_LIST[byte_pos] |= (bit1 ? bit_mask : 0); } else { // 存在分歧位 if(bit_idx == last_diff) { search_dir = 1; } else if(bit_idx > last_diff) { search_dir = 0; last_diff = bit_idx; } ROM_LIST[byte_pos] |= (search_dir ? bit_mask : 0); } Write_DS18B20(search_dir); } }4. 完整工程实现
结合LCD显示和串口上传功能,分享一个经过实际验证的代码框架:
4.1 驱动层封装
// ds18b20.h #ifndef __DS18B20_H__ #define __DS18B20_H__ #include <reg52.h> sbit DQ = P3^7; // 单总线接口 float DS18B20_GetTemp(void); void DS18B20_SetResolution(uint8_t res); uint8_t DS18B20_ScanDevices(uint8_t *rom_codes); #endif4.2 应用层实现
// main.c #include "ds18b20.h" #include "lcd1602.h" #include "uart.h" uint8_t rom_codes[8][8]; // 存储最多8个传感器ROM码 void main() { UART_Init(9600); LCD_Init(); uint8_t dev_count = DS18B20_ScanDevices(rom_codes); printf("Found %d devices\r\n", dev_count); while(1) { for(int i=0; i<dev_count; i++) { DS18B20_MatchROM(rom_codes[i]); float temp = DS18B20_GetTemp(); LCD_SetCursor(0, i); LCD_Printf("T%d:%4.1fC", i+1, temp); printf("Sensor%d: %.1fC\r\n", i, temp); } delay_ms(1000); } }4.3 抗干扰设计
在工业现场应用中,还需要增加以下保护措施:
- 总线加TVS二极管防静电
- 采用磁珠隔离数字噪声
- 每30米增加总线中继器
- 软件上增加CRC校验和数据重传机制
温度转换期间,实测发现电源会出现约1ms的50mV纹波。通过以下电源滤波电路可有效抑制:
+---+ +---+ 5V -----| L |--+--| C |---> VDD +---+ | +---+ === 100μF | GND5. 性能优化技巧
经过多个项目验证,总结出这些实用经验:
转换速度优化:
- 9位分辨率时转换时间94ms,12位时750ms
- 对于实时性要求高的场景,可以:
DS18B20_WriteByte(0x44); // 启动转换 while(!DQ); // 等待转换完成(总线变高)
功耗控制:
- 休眠模式下电流仅750nA
- 采用间歇工作模式:每5分钟唤醒测量一次
温度报警应用:
// 设置报警阈值 void DS18B20_SetAlarm(int8_t TH, int8_t TL) { DS18B20_WriteByte(0x4E); // 写暂存器 DS18B20_WriteByte(TH); DS18B20_WriteByte(TL); DS18B20_WriteByte(0x7F); // 配置寄存器 DS18B20_WriteByte(0x48); // 复制到EEPROM }异常处理:
- 总线冲突时复位所有设备
- 增加温度变化率检测(>2℃/秒视为异常)
- 对连续3次读取失败