1-Wire单总线协议
·
1-Wire单总线协议
1. 核心概念
1.1 1-Wire协议概述
1-Wire(单总线)是由Dallas Semiconductor(现Maxim Integrated)开发的串行通信协议,仅需一根数据线即可实现双向通信并为设备供电。
核心特点:
- 单线通信:数据+电源共用一根线(加地线共2根)
- 寄生供电:可从数据线窃取电能,无需独立电源
- 自动寻址:每个设备有唯一64位ROM ID
- 多设备总线:理论上支持无限多设备
- 低成本:简化硬件设计,降低接线成本
1.2 1-Wire总线拓扑
VDD (可选)
│
┌┴┐ 上拉电阻(4.7kΩ)
└┬┘
│
────┼────────────────────────────────
DQ │
├────●────●────●────●────●────●
│ │ │ │ │ │ │
┌─┴─┐ ┌┴──┐ ┌┴──┐ ┌┴──┐ ┌┴──┐ ┌┴──┐
│MCU│ │DS18B20│ │DS2431│ │DS2413│
│ │ │温度 │ │EEPROM│ │IO开关│
└───┘ └───┘ └───┘ └───┘
GND GND GND GND
- 所有设备并联连接
- 需要4.7kΩ上拉电阻
- 总线长度:<100m(取决于设备数量和线材)
1.3 电气特性
电平定义:
- 逻辑1(空闲):总线被上拉电阻拉高(2.8V-5.25V)
- 逻辑0(活动):设备拉低总线(0V-0.8V)
时序参数(标准速度):
| 参数 | 最小值 | 典型值 | 最大值 | 单位 |
|---|---|---|---|---|
| 复位脉冲 | 480 | - | - | μs |
| 存在脉冲 | 60 | - | 240 | μs |
| 写0时隙 | 60 | - | 120 | μs |
| 写1时隙 | 1 | - | 15 | μs |
| 读取时隙 | 60 | - | 120 | μs |
| 恢复时间 | 1 | - | - | μs |
2. 技术原理
2.1 通信时序
初始化序列(复位与存在检测):
主机: ─┐ ┌──────────────
└─────────────────────────┘
480μs复位脉冲
从机: ───────────────┐ ┌──────────────────
└─────┘
60-240μs存在脉冲
写0时序:
主机: ─┐ ┌────────
└─────────────────────┘
60-120μs低电平
写1时序:
主机: ─┐ ┌─────────────────────────
└───┘
1-15μs低电平
读取时序:
主机: ─┐ ┌─────────────────────────
└───┘
1μs拉低后释放
从机: ─────X─────────────────────────
│
采样点(主机在15μs时读取)
2.2 64位ROM码
ROM结构:
┌─────────┬──────────────────┬───────┐
│ 8位族码 │ 48位序列号 │ 8位CRC│
└─────────┴──────────────────┴───────┘
示例:28 AA BB CC DD EE FF 12
- 族码:0x28 (DS18B20温度传感器)
- 序列号:AA BB CC DD EE FF (唯一)
- CRC:0x12 (校验码)
常见族码:
| 族码 | 设备型号 | 功能 |
|---|---|---|
| 0x10 | DS18S20 | 温度传感器 |
| 0x28 | DS18B20 | 温度传感器 |
| 0x2D | DS2431 | 1Kbit EEPROM |
| 0x3A | DS2413 | 双路IO开关 |
| 0x26 | DS2438 | 电池监测 |
2.3 CRC校验
CRC-8算法(多项式:x^8 + x^5 + x^4 + 1):
def calculate_crc8(data: bytes) -> int:
"""
计算1-Wire CRC-8
Args:
data: 输入数据
Returns:
CRC-8校验码
"""
crc = 0
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x01:
crc = (crc >> 1) ^ 0x8C
else:
crc >>= 1
return crc
2.4 命令集
ROM命令:
| 命令 | 代码 | 功能 |
|---|---|---|
| Read ROM | 0x33 | 读取ROM(仅单设备) |
| Match ROM | 0x55 | 匹配ROM(选择特定设备) |
| Skip ROM | 0xCC | 跳过ROM(广播命令) |
| Search ROM | 0xF0 | 搜索ROM(枚举所有设备) |
| Alarm Search | 0xEC | 报警搜索 |
功能命令(以DS18B20为例):
| 命令 | 代码 | 功能 |
|---|---|---|
| Convert T | 0x44 | 启动温度转换 |
| Write Scratchpad | 0x4E | 写入暂存器 |
| Read Scratchpad | 0xBE | 读取暂存器 |
| Copy Scratchpad | 0x48 | 复制到EEPROM |
| Recall E2 | 0xB8 | 从EEPROM加载 |
3. 代码实现
3.1 Linux 1-Wire驱动(Python)
class OneWireBus:
"""1-Wire总线驱动(GPIO位操作)"""
def __init__(self, pin: int):
"""
初始化1-Wire总线
Args:
pin: GPIO引脚号
"""
self.pin = pin
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
def reset(self) -> bool:
"""
发送复位脉冲并检测存在脉冲
Returns:
True=检测到设备, False=无设备
"""
# 拉低480μs
GPIO.setup(self.pin, GPIO.OUT)
GPIO.output(self.pin, GPIO.LOW)
time.sleep(480e-6)
# 释放总线
GPIO.setup(self.pin, GPIO.IN)
time.sleep(70e-6)
# 检测存在脉冲
presence = not GPIO.input(self.pin)
time.sleep(410e-6)
return presence
def write_bit(self, bit: int):
"""
写入单个位
Args:
bit: 0或1
"""
GPIO.setup(self.pin, GPIO.OUT)
if bit:
# 写1:拉低1-15μs
GPIO.output(self.pin, GPIO.LOW)
time.sleep(6e-6)
GPIO.output(self.pin, GPIO.HIGH)
time.sleep(64e-6)
else:
# 写0:拉低60-120μs
GPIO.output(self.pin, GPIO.LOW)
time.sleep(60e-6)
GPIO.output(self.pin, GPIO.HIGH)
time.sleep(10e-6)
def read_bit(self) -> int:
"""
读取单个位
Returns:
0或1
"""
GPIO.setup(self.pin, GPIO.OUT)
GPIO.output(self.pin, GPIO.LOW)
time.sleep(3e-6)
GPIO.setup(self.pin, GPIO.IN)
time.sleep(10e-6)
bit = GPIO.input(self.pin)
time.sleep(53e-6)
return bit
def write_byte(self, byte: int):
"""写入字节"""
for i in range(8):
self.write_bit((byte >> i) & 0x01)
def read_byte(self) -> int:
"""读取字节"""
byte = 0
for i in range(8):
byte |= (self.read_bit() << i)
return byte
def crc8(self, data: bytes) -> int:
"""计算CRC-8"""
crc = 0
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x01:
crc = (crc >> 1) ^ 0x8C
else:
crc >>= 1
return crc
class DS18B20:
"""DS18B20温度传感器驱动"""
# ROM命令
CMD_READ_ROM = 0x33
CMD_MATCH_ROM = 0x55
CMD_SKIP_ROM = 0xCC
CMD_SEARCH_ROM = 0xF0
# 功能命令
CMD_CONVERT_T = 0x44
CMD_READ_SCRATCHPAD = 0xBE
CMD_WRITE_SCRATCHPAD = 0x4E
def __init__(self, bus: OneWireBus, rom: bytes = None):
"""
初始化DS18B20
Args:
bus: 1-Wire总线对象
rom: ROM码(None表示单设备模式)
"""
self.bus = bus
self.rom = rom
def read_rom(self) -> bytes:
"""
读取ROM码(仅适用于单设备)
Returns:
8字节ROM码
"""
if not self.bus.reset():
raise Exception("设备未响应")
self.bus.write_byte(self.CMD_READ_ROM)
rom = bytes([self.bus.read_byte() for _ in range(8)])
# 验证CRC
if self.bus.crc8(rom[:7]) != rom[7]:
raise Exception("CRC校验失败")
return rom
def select_device(self):
"""选择设备"""
if not self.bus.reset():
raise Exception("设备未响应")
if self.rom:
# 多设备模式:匹配ROM
self.bus.write_byte(self.CMD_MATCH_ROM)
for byte in self.rom:
self.bus.write_byte(byte)
else:
# 单设备模式:跳过ROM
self.bus.write_byte(self.CMD_SKIP_ROM)
def read_temperature(self) -> float:
"""
读取温度
Returns:
温度值(°C)
"""
# 启动温度转换
self.select_device()
self.bus.write_byte(self.CMD_CONVERT_T)
# 等待转换完成(12位精度需要750ms)
time.sleep(0.75)
# 读取暂存器
self.select_device()
self.bus.write_byte(self.CMD_READ_SCRATCHPAD)
scratchpad = bytes([self.bus.read_byte() for _ in range(9)])
# 验证CRC
if self.bus.crc8(scratchpad[:8]) != scratchpad[8]:
raise Exception("CRC校验失败")
# 解析温度(16位有符号整数)
temp_raw = (scratchpad[1] << 8) | scratchpad[0]
if temp_raw & 0x8000:
temp_raw = -((temp_raw ^ 0xFFFF) + 1)
# 转换为摄氏度(0.0625°C分辨率)
temperature = temp_raw * 0.0625
return temperature
def set_resolution(self, resolution: int):
"""
设置分辨率
Args:
resolution: 9-12位
"""
if resolution < 9 or resolution > 12:
raise ValueError("分辨率必须在9-12位之间")
# 读取当前配置
self.select_device()
self.bus.write_byte(self.CMD_READ_SCRATCHPAD)
scratchpad = [self.bus.read_byte() for _ in range(9)]
# 修改配置寄存器
config = ((resolution - 9) << 5) | 0x1F
scratchpad[4] = config
# 写入配置
self.select_device()
self.bus.write_byte(self.CMD_WRITE_SCRATCHPAD)
self.bus.write_byte(scratchpad[2]) # TH
self.bus.write_byte(scratchpad[3]) # TL
self.bus.write_byte(scratchpad[4]) # Config
# 主程序示例
if __name__ == '__main__':
# 初始化总线(GPIO4)
bus = OneWireBus(pin=4)
# 检测设备
if not bus.reset():
print("未检测到1-Wire设备")
sys.exit(1)
# 初始化DS18B20
sensor = DS18B20(bus)
try:
# 读取ROM
rom = sensor.read_rom()
print(f"ROM码: {rom.hex()}")
# 设置12位分辨率
sensor.set_resolution(12)
# 连续读取温度
for i in range(10):
temp = sensor.read_temperature()
print(f"温度: {temp:.4f} °C")
time.sleep(1)
finally:
GPIO.cleanup()
3.2 Arduino 1-Wire实现(C++)
#include <Arduino.h>
class OneWire {
private:
uint8_t pin;
void pinMode_input() {
pinMode(pin, INPUT);
}
void pinMode_output() {
pinMode(pin, OUTPUT);
}
void digitalWrite_low() {
digitalWrite(pin, LOW);
}
void digitalWrite_high() {
digitalWrite(pin, HIGH);
}
int digitalRead_pin() {
return digitalRead(pin);
}
public:
OneWire(uint8_t pin) : pin(pin) {
pinMode_input();
digitalWrite_high();
}
/**
* 复位并检测存在脉冲
*/
bool reset() {
pinMode_output();
digitalWrite_low();
delayMicroseconds(480);
pinMode_input();
delayMicroseconds(70);
bool presence = !digitalRead_pin();
delayMicroseconds(410);
return presence;
}
/**
* 写入位
*/
void writeBit(uint8_t bit) {
pinMode_output();
if (bit) {
digitalWrite_low();
delayMicroseconds(10);
digitalWrite_high();
delayMicroseconds(55);
} else {
digitalWrite_low();
delayMicroseconds(65);
digitalWrite_high();
delayMicroseconds(5);
}
}
/**
* 读取位
*/
uint8_t readBit() {
pinMode_output();
digitalWrite_low();
delayMicroseconds(3);
pinMode_input();
delayMicroseconds(10);
uint8_t bit = digitalRead_pin();
delayMicroseconds(53);
return bit;
}
/**
* 写入字节
*/
void write(uint8_t data) {
for (uint8_t i = 0; i < 8; i++) {
writeBit((data >> i) & 0x01);
}
}
/**
* 读取字节
*/
uint8_t read() {
uint8_t data = 0;
for (uint8_t i = 0; i < 8; i++) {
data |= (readBit() << i);
}
return data;
}
/**
* 计算CRC-8
*/
static uint8_t crc8(const uint8_t* data, uint8_t len) {
uint8_t crc = 0;
for (uint8_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x01) {
crc = (crc >> 1) ^ 0x8C;
} else {
crc >>= 1;
}
}
}
return crc;
}
};
class DS18B20 {
private:
OneWire* wire;
public:
DS18B20(OneWire* w) : wire(w) {}
/**
* 读取温度
*/
float readTemperature() {
uint8_t data[9];
// 启动转换
if (!wire->reset()) {
return -999;
}
wire->write(0xCC); // Skip ROM
wire->write(0x44); // Convert T
// 等待转换完成
delay(750);
// 读取暂存器
if (!wire->reset()) {
return -999;
}
wire->write(0xCC); // Skip ROM
wire->write(0xBE); // Read Scratchpad
for (uint8_t i = 0; i < 9; i++) {
data[i] = wire->read();
}
// 验证CRC
if (OneWire::crc8(data, 8) != data[8]) {
return -999;
}
// 解析温度
int16_t raw = (data[1] << 8) | data[0];
float celsius = (float)raw * 0.0625;
return celsius;
}
};
// Arduino主程序
OneWire oneWire(2); // GPIO2
DS18B20 sensor(&oneWire);
void setup() {
Serial.begin(115200);
Serial.println("DS18B20温度传感器测试");
}
void loop() {
float temp = sensor.readTemperature();
if (temp != -999) {
Serial.print("温度: ");
Serial.print(temp, 4);
Serial.println(" °C");
} else {
Serial.println("读取失败");
}
delay(1000);
}
4. 行业案例
案例1:数据中心温度监控(Google数据中心)
项目背景:
Google数据中心部署10,000+个DS18B20温度传感器,实时监控服务器机架温度。
技术方案:
- 传感器:DS18B20(精度±0.5°C)
- 拓扑:每条总线连接30个传感器
- 采集周期:10秒
- 数据上报:通过树莓派网关上传到监控平台
代码实现(多传感器搜索):
def search_devices(bus: OneWireBus) -> list:
"""
搜索所有1-Wire设备
Returns:
ROM码列表
"""
rom_list = []
last_discrepancy = 0
last_device = False
while not last_device:
if not bus.reset():
break
bus.write_byte(0xF0) # Search ROM
rom = [0] * 8
discrepancy = 0
for bit_index in range(64):
# 读取位和补码位
bit = bus.read_bit()
comp_bit = bus.read_bit()
if bit and comp_bit:
# 无设备响应
break
if not bit and not comp_bit:
# 冲突,需要选择分支
if bit_index == last_discrepancy:
direction = 1
elif bit_index > last_discrepancy:
direction = 0
else:
direction = (rom[bit_index // 8] >> (bit_index % 8)) & 0x01
if direction == 0:
discrepancy = bit_index
else:
# 无冲突
direction = bit
# 写入选择位
byte_index = bit_index // 8
bit_position = bit_index % 8
if direction:
rom[byte_index] |= (1 << bit_position)
bus.write_bit(direction)
# 验证CRC
if bus.crc8(bytes(rom[:7])) == rom[7]:
rom_list.append(bytes(rom))
last_discrepancy = discrepancy
if last_discrepancy == 0:
last_device = True
return rom_list
# 使用示例
bus = OneWireBus(pin=4)
devices = search_devices(bus)
print(f"发现 {len(devices)} 个设备:")
for rom in devices:
print(f" ROM: {rom.hex()}")
# 读取温度
sensor = DS18B20(bus, rom)
temp = sensor.read_temperature()
print(f" 温度: {temp:.2f} °C")
实施效果:
- 温度监控覆盖率:100%
- 数据采集延迟:<1秒
- 预警准确率:95%
- 节能效果:优化空调运行,降低15%能耗
案例2:智能家居温度控制(Nest恒温器)
项目背景:
Nest恒温器使用1-Wire传感器网络实现多房间温度监控。
技术方案:
- 主控:Nest Hub(ARM Cortex-A7)
- 传感器:每个房间1个DS18B20
- 无线通信:Thread协议(IEEE 802.15.4)
- 控制策略:基于多点温度的智能调节
实施效果:
- 温度精度:±0.5°C
- 响应时间:<30秒
- 节能效果:比传统恒温器节能20-30%
更多推荐


所有评论(0)