51单片机驱动DS18B20温度传感器笔记
1. DS18B20简介
- 单总线(1-Wire)接口: 仅需一根数据线即可完成通信(外加电源和地线),支持多个器件挂接在同一条总线上。
- 数字输出: 直接输出数字温度值,省去复杂的信号调理电路。
- 精度: 典型精度为\\pm0.5\^{\\circ}C(-10\^{\\circ}C至+85\^{\\circ}C范围内)。
- 分辨率: 可编程为9、10、11或12位,对应温度分辨率分别为0.5\^{\\circ}C、0.25\^{\\circ}C、0.125\^{\\circ}C和0.0625\^{\\circ}C。默认为12位。
- 温度范围: -55\^{\\circ}C至+125\^{\\circ}C。
- 供电: 支持寄生供电(仅需GND和DQ两根线)或外部供电(需VDD、GND、DQ三根线)。寄生供电时,需在DQ线上接一个约4.7KΩ的上拉电阻。
2. 硬件连接 (以外部供电为例)
- 51单片机:
Px.y(某个IO口) 连接至 DS18B20 的DQ(数据线)。VCC连接至 DS18B20 的VDD。GND连接至 DS18B20 的GND。
- DS18B20:
VDD接 +5V电源。GND接地。DQ接单片机IO口,并通过一个 4.7KΩ 的上拉电阻连接到VDD。
3. 单总线通信协议要点
通信由单片机主机发起和控制,包括初始化、ROM命令、功能命令、数据传输。所有通信均以**时隙(Time Slot)**为单位。
-
初始化序列 (复位脉冲 + 存在脉冲)
- 主机拉低DQ线至少480μs(复位脉冲)。
- 主机释放DQ线(上拉电阻拉高)。
- DS18B20等待15-60μs后,拉低DQ线60-240μs(存在脉冲),表示响应。
- 主机检测到存在脉冲后,知道总线上有器件。
-
写时隙
- 写'0'时隙: 主机拉低DQ线,并保持低电平至少60μs(通常在60-120μs)。
- 写'1'时隙: 主机拉低DQ线,然后在15μs内释放DQ线(上拉拉高),并保持高电平直到时隙结束(总时隙时间至少60μs)。
- 每个写时隙只能写入1位数据,先写最低位(LSB)。
-
读时隙
- 主机拉低DQ线至少1μs。
- 主机释放DQ线(上拉拉高)。
- 主机在拉低后的15μs内采样DQ线电平。
- 低电平:表示DS18B20发送了'0'。
- 高电平:表示DS18B20发送了'1'。
- 整个读时隙应大于60μs。
- 每个读时隙只能读取1位数据,先读最低位(LSB)。
-
ROM命令 (识别总线上器件)
0x33- Read ROM (读ROM,仅单器件时用)。0x55- Match ROM (匹配ROM,用于选择特定器件)。0xCC- Skip ROM (跳过ROM,总线只有一个器件或向所有器件广播命令时用)。
-
功能命令 (操作DS18B20)
0x44- Convert T (启动温度转换)。0xBE- Read Scratchpad (读暂存器,读取温度值和配置)。0x4E- Write Scratchpad (写暂存器,写TH、TL和配置寄存器)。0x48- Copy Scratchpad (复制暂存器内容到EEPROM)。0xB8- Recall EEPROM (将EEPROM内容召回至暂存器)。0xB4- Read Power Supply (读供电模式)。
4. 读取温度流程 (以跳过ROM为例)
- 初始化: 发送复位脉冲,检测存在脉冲。
- 跳过ROM: 发送命令
0xCC。 - 启动转换: 发送命令
0x44。- 转换时间取决于分辨率:12位时约需750ms。在此期间,主机可以查询总线状态或等待。
- 初始化: 再次发送复位脉冲,检测存在脉冲。
- 跳过ROM: 发送命令
0xCC。 - 读暂存器: 发送命令
0xBE。 - 读取数据: 连续读取9字节(或只读前2字节温度值)。
- 第0字节:温度值低字节(LS Byte)。
- 第1字节:温度值高字节(MS Byte)。
- 计算温度:
- 将高低字节合并成一个16位整数
TempRaw。 - 温度值以 1/16\^{\\circ}C 为单位(12位分辨率时)。
- 实际温度 T: $$ T = \frac{\text{TempRaw}}{16} $$
- 注意:
TempRaw的最高位(bit15)是符号位。0表示正温度,1表示负温度(以补码形式存储)。- 负温度处理:
TempRaw = TempRaw - 0xFFFF - 1或取反加一后再计算。
- 负温度处理:
- 将高低字节合并成一个16位整数
c
#include <REGX51.H>
#include <INTRINS.H> // 用于_nop_()延时
sbit DQ = P1^0; // 假设DS18B20数据线接在P1.0
// 延时函数 (需根据实际晶振频率调整)
void Delay_us(unsigned int us) {
while (us--);
}
// 初始化 (复位 + 检测存在)
bit DS18B20_Init() {
bit ack;
DQ = 1; // 释放总线
Delay_us(5);
DQ = 0; // 主机拉低总线 (复位脉冲)
Delay_us(480); // 保持480us以上低电平
DQ = 1; // 释放总线,等待上拉拉高
Delay_us(60); // 等待15-60us后采样
ack = DQ; // 读取存在脉冲 (ack=0表示存在)
Delay_us(420); // 等待剩余时隙时间 (总时隙>480us)
return !ack; // 返回1表示初始化成功
}
// 写一个字节 (低位在前)
void DS18B20_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
DQ = 0; // 拉低总线开始写时隙
_nop_(); // 短暂延时 (约1us)
DQ = dat & 0x01; // 输出当前位 (最低位)
Delay_us(60); // 保持60us以上 (写'0'时保持低,写'1'时会被释放)
DQ = 1; // 释放总线
dat >>= 1; // 准备下一位
Delay_us(5); // 时隙间恢复时间
}
}
// 读一个字节 (低位在前)
unsigned char DS18B20_ReadByte() {
unsigned char i, dat = 0;
for (i = 0; i < 8; i++) {
DQ = 0; // 拉低总线开始读时隙
_nop_(); // 短暂延时 (约1us)
DQ = 1; // 释放总线,准备采样
_nop_(); // 短暂延时 (约5-10us)
if (DQ) dat |= (0x01 << i); // 在15us内采样,若为高则置位相应位
Delay_us(50); // 等待该时隙剩余时间 (总时隙>60us)
}
return dat;
}
// 获取温度值 (简化流程)
float DS18B20_GetTemp() {
unsigned char TL, TH;
int TempRaw;
float T;
if (!DS18B20_Init()) return 0; // 初始化失败
DS18B20_WriteByte(0xCC); // Skip ROM
DS18B20_WriteByte(0x44); // Convert T
// 此处应等待转换完成 (可延时750ms 或 查询总线状态)
Delay_ms(750); // 简单延时等待转换 (需实现ms级延时函数)
if (!DS18B20_Init()) return 0; // 再次初始化
DS18B20_WriteByte(0xCC); // Skip ROM
DS18B20_WriteByte(0xBE); // Read Scratchpad
TL = DS18B20_ReadByte(); // 读取温度低字节
TH = DS18B20_ReadByte(); // 读取温度高字节
// 可以继续读取其他字节,但这里只关心温度
TempRaw = (TH << 8) | TL; // 合成16位温度原始值
T = (float) TempRaw * 0.0625; // 乘以分辨率 (1/16 = 0.0625)
return T;
}
注意事项:
- 延时精度: 单总线协议对时序要求严格。延时函数 (
Delay_us,Delay_ms) 必须根据单片机实际晶振频率精确调整。_nop_()用于产生极短延时(1个机器周期)。 - 上拉电阻: 必须连接约4.7KΩ的上拉电阻到DQ线。
- 总线负载: 总线上的器件数量会影响信号质量。
- 负温度处理: 代码示例未处理负温度(当TH的最高位为1时表示负温度),实际应用中需完善。
- 等待转换完成: 除了延时等待,更优的方法是主机在启动转换后,不断发送读时隙,直到DS18B20返回'0'(表示转换完成)或'1'(表示仍在转换)。
- 寄生供电: 若使用寄生供电,在温度转换期间(特别是高分辨率时),主机需通过上拉电阻给总线提供足够的电流(强上拉),转换完成后恢复弱上拉。