野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 81|回复: 0

单总线协议的核心概念

[复制链接]
发表于 2025-12-19 10:37:39 | 显示全部楼层 |阅读模式
一、单总线协议的核心概念
单总线协议的操作分为三个层次:
初始化:建立主机与 DS18B20 的连接(类似“握手”);
ROM 命令:识别总线上的传感器(通过唯一 64 ROM 地址);
功能命令:执行具体操作(如温度转换、读数据)。
二、具体通信时序
单总线的所有操作都基于时间片(Time Slot),关键是延时的准确性(需用示波器校准或用精确的软件延时)。
1. 初始化时序(Initialization Sequence
作用:主机检测总线上是否存在 DS18B20,并同步通信节奏。
步骤(主机主动发起):
复位脉冲(Reset Pulse):主机将数据线(DQ)拉低 480~960μs(典型 500μs),然后释放总线(由上拉电阻拉回高电平)。
应答脉冲(Presence Pulse):DS18B20 检测到复位脉冲后,会在 15~60μs​ 内拉低总线 60~240μs(典型 120μs),表示“我在”。
主机需在释放总线后等待至少 480μs,确保应答完成。
时序图:
主机:低电平(480-960μs)→ 高电平(释放)
DS18B20:等待 15-60μs → 低电平(60-240μs)→ 高电平(恢复)
2. ROM 命令(ROM Commands
总线上可能有多个 DS18B20(最多 8 个,因 1-Wire 总线驱动能力限制),ROM 命令用于选择目标传感器。常用 ROM 命令:
命令
十六进制
作用
适用场景
Skip ROM
0xCC
跳过 ROM 检测,直接操作总线上的所有传感器
单传感器系统
Search ROM
0xF0
搜索总线上所有传感器的 ROM 地址
多传感器组网
Match ROM
0x55
匹配指定 ROM 地址的传感器
多传感器中选通某一个
Read ROM
0x33
读取总线上单个传感器的 ROM 地址
已知单传感器时验证地址
注意:单传感器系统中,优先使用 0xCCSkip ROM),简化流程。
3. 功能命令(Function Commands
在完成初始化和 ROM 命令后,主机发送功能命令控制传感器工作。常用功能命令:
命令
十六进制
作用
Convert T
0x44
启动温度转换(异步操作,需等待转换完成)
Read Scratchpad
0xBE
读取传感器内部暂存器(共 9 字节,前 2 字节是温度值)
Write Scratchpad
0x4E
写入暂存器(TH/TL 报警阈值、分辨率配置)
Copy Scratchpad
0x48
TH/TL 复制到 EEPROM(掉电保存)
4. 写时隙(Write Time Slot
作用:主机向 DS18B20 写一个字节(8 位)。
规则:
0:主机拉低 DQ 至少 60μs(≤120μs),然后释放(上拉电阻拉回高)。
1:主机拉低 DQ 1~15μs(典型 5μs),然后释放,让总线自然回到高。
每两位写时隙之间需间隔1μs
时序图(以写 1 为例):
主机:低电平(1-15μs)→ 高电平(释放)→ 等待 ≥1μs(下一个时隙)
5. 读时隙(Read Time Slot
作用:主机从 DS18B20 读一个字节(8 位)。
规则:
主机拉低 DQ 1~15μs(典型 5μs),然后立即释放(转为输入模式)
在释放后的 15μs 内,读取 DQ 的状态:高电平为 1,低电平为 0
每两位读时隙之间需间隔1μs
时序图:
主机:低电平(1-15μs)→ 释放(转为输入)→ 15μs 内读状态 → 等待 ≥1μs(下一个时隙)
三、编程示例(C 语言,51 单片机为例)
以下是单传感器系统的完整流程:初始化→跳过 ROM→启动温度转换→等待转换完成→读暂存器→解析温度。
1. 硬件连接
VCC3.3V/5V(推荐外部供电,寄生供电需额外处理);
GND:接地;
DQ:接 MCU P1.0 引脚(或其他 I/O),必须串联 4.7kΩ 上拉电阻到 VCC
2. 基础延时函数(关键!)
单总线对延时敏感,需用机器周期精确控制(51 单片机晶振 12MHz 时,1 机器周期=1μs):
#include <reg52.h>#include <intrins.h> // _nop_()
#define uchar unsigned char#define uint unsigned int
// 延时 1μs12MHz 晶振)void delay_us(uint t) {
    while(t--) _nop_(); // _nop_() 1μs 延时(12MHz
}
// 延时 1ms12MHz 晶振)void delay_ms(uint t) {
    uint i, j;
    for(i = t; i > 0; i--)
        for(j = 110; j > 0; j--);
}
3. 单总线核心函数
1)初始化函数(返回 1 表示成功检测到传感器)
bit Init_DS18B20(void) {
    bit ack; // 应答标志:0=无传感器,1=有传感器
    DQ = 0;       // 拉低 DQP1.0
    delay_us(500); // 复位脉冲:480-960μs(选 500μs
    DQ = 1;       // 释放总线(上拉电阻拉高)
    delay_us(60);  // 等待 15-60μs(选 60μs
    ack = DQ;     // 读取应答脉冲(DS18B20 拉低时为 0
    delay_us(420); // 等待剩余时间(总时间 ≥480μs
    return ~ack;  // 反转:ack=0 表示有应答,返回 1
}
2)写一个字节函数
void Write_OneByte(uchar dat) {
    uchar i;
    for(i = 0; i < 8; i++) {
        DQ = 0;          // 拉低总线,开始写时隙
        delay_us(2);     // 保持 1-15μs(选 2μs
        DQ = dat & 0x01; // 写最低位(1 则释放,0 则保持低)
        delay_us(60);    // 保持 ≥60μs(写 0 时足够,写 1 时自动释放)
        DQ = 1;          // 释放总线(准备下一位)
        dat >>= 1;       // 右移,处理下一位
    }
}
3)读一个字节函数
uchar Read_OneByte(void) {
    uchar i, dat = 0;
    for(i = 0; i < 8; i++) {
        DQ = 0;          // 拉低总线,开始读时隙
        delay_us(2);     // 保持 1-15μs(选 2μs
        DQ = 1;          // 释放总线,转为输入
        delay_us(8);     // 等待 15μs 内的状态稳定(选 8μs
        dat >>= 1;       // 右移,腾出高位
        if(DQ) dat |= 0x80; // 读当前位(高则置 1
        delay_us(50);    // 等待时隙结束(≥1μs
    }
    return dat;
}
4. 主流程:读取温度值
float Read_Temperature(void) {
    uchar LSB, MSB; // 温度低字节、高字节(16 位带符号数)
    int temp;       // 16 位整数(用于符号处理)
    float t;        // 最终温度值(℃)
    // 1. 初始化 + 跳过 ROM(单传感器)
    Init_DS18B20();
    Write_OneByte(0xCC); // Skip ROM 命令
    Write_OneByte(0x44); // 启动温度转换(Convert T
    // 2. 等待转换完成(12 位分辨率需 750ms
    delay_ms(750); // 简单延时,或用“读总线状态”判断(更高效)
    // 3. 再次初始化 + 读暂存器
    Init_DS18B20();
    Write_OneByte(0xCC); // Skip ROM
    Write_OneByte(0xBE); // 读暂存器(Read Scratchpad
    LSB = Read_OneByte(); // 读低字节(温度小数部分+整数部分低4位)
    MSB = Read_OneByte(); // 读高字节(符号位+整数部分高4位)
    // 4. 解析 16 位温度数据(补码格式)
    temp = (MSB << 8) | LSB; // 合并为 16 位整数
    if(temp & 0x8000) {      // 负温度(最高位为 1
        temp = ~temp + 1;     // 取反加 1,得到绝对值的补码
        t = -(temp * 0.0625); // 12 位分辨率:0.0625/LSB
    } else {                  // 正温度
        t = temp * 0.0625;
    }
    return t;
}
5. 主函数调用
void main(void) {
    float temp;
    while(1) {
        temp = Read_Temperature();
        // 此处可将 temp 发送到串口/显示(如 LCD
        delay_ms(1000); // 每秒读一次
    }
}
四、关键细节说明
温度数据解析:
DS18B20 输出的 16 位是带符号二进制补码,格式如下(12 位分辨率):
5 位:符号位(S);
中间 7 位:整数部分(T_integer);
4 位:小数部分(T_fraction1/16℃ 步进)。
例如:0000 0110 0101 00000x0650 1616 1616×0.0625=101℃?不对,等一下,正确的计算是:
16 位数据是 MSB<<8 | LSB,比如 LSB=0x5080),MSB=0x066),合并后是 0x0650=16161616×0.0625=101℃?这显然超过量程,哦,错了——12 位分辨率时,有效位是 12 位,即 16 位数据中,低 4 位是小数,高 12 位是整数+符号。正确的计算应该是:
对于 12 位分辨率,temp = (MSB << 8 | LSB),然后 t = temp * 0.0625(因为 2^12=40961/4096=0.00024414?不对,等一下,DS18B20 的分辨率是
9 位:0.5℃ → 1/512=0.001953125?不,等一下,官方文档说:
分辨率 | 温度增量 | 转换时间
9 | 0.5| 93.75ms
10 | 0.25| 187.5ms
11 | 0.125| 375ms
12 | 0.0625| 750ms
所以,12 位时,每 LSB 对应 0.0625℃,不管符号,直接用 temp × 0.0625即可(符号由最高位决定)。
转换完成的判断:
上述示例用了 delay_ms(750)等待转换,更高效的方法是读总线的“忙”状态:
启动转换后,DQ 会被 DS18B20 拉低(表示忙),转换完成后释放为高。主机可通过读 DQ 状态判断是否完成(无需固定延时)。
多传感器组网:
若总线上有多个 DS18B20,需先通过 Search ROM0xF0)​ 读取所有传感器的 64 位地址,再用 Match ROM0x55+ 地址​ 选通目标传感器。
五、常见问题排查
无应答(初始化返回 0):检查接线(上拉电阻是否接 4.7kΩ?DQ 是否接对?)、电源电压(是否 3-5V?)、传感器是否损坏。
读值错误:检查时序延时(用示波器测 DQ 波形)、是否有干扰(用屏蔽线)、是否在转换完成前读数据。
温度偏差大:确认分辨率设置(默认 12 位)、是否用了寄生供电(高温时建议外部供电)、传感器是否靠近热源。
通过以上步骤,你可以快速实现 DS18B20 的温度读取。如果需要更高效的代码(如用中断处理时序),可进一步优化延时函数(如用定时器),但核心逻辑不变。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2025-12-31 07:55 , Processed in 0.088202 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表