1-Wire 一线式总线:从原理到实战,玩转 DS18B20 温度采集

发布于:2025-06-04 ⋅ 阅读:(102) ⋅ 点赞:(0)

引言

在嵌入式系统中,通信总线是连接 CPU 与外设的桥梁。从 I2C、SPI 到 UART,每种总线都有其独特的应用场景。而本文要介绍的1-Wire 一线式总线,以其极简的硬件设计和独特的通信协议,在温度采集、身份识别等领域大放异彩。本文将从原理入手,结合 STM32 与 DS18B20 温度传感器的实战案例,带您深入理解一线式总线的奥秘。

一、一线式总线的核心原理

1. 定义与特性

一线式串行总线,顾名思义,只需一根数据线即可实现 CPU 与外设的通信(需上拉电阻,默认高电平)。与 I2C、SPI 等多线总线不同,它的特点是:

  • 单数据线:数据传输与时钟同步都依赖这根线。

  • 串行通信:一位一位地传输数据,而非并行。

  • 总线结构:支持多个从设备挂接在同一数据线上(需通过 ROM 寻址)。

2. 时钟机制:无时钟线如何同步?

一线式总线没有独立的时钟信号线,它通过严格的时序控制实现数据同步:

  • 主设备(MCU) 通过精确控制数据线的高低电平持续时间,定义时钟周期。

  • 从设备(如 DS18B20)根据主设备的时序要求,在特定时间窗口内采样或发送数据。

这种设计虽然增加了软件实现的复杂度,但极大简化了硬件连接。

二、DS18B20 温度传感器实战:硬件设计

1. 硬件连接

以 STM32F103 与 DS18B20 为例,硬件连接如下:

  • 数据线(DQ):连接 STM32 的 PG11 引脚(需 4.7KΩ 上拉电阻)。

  • 电源(VDD):接 3.3V 或 5V(支持寄生电源模式,本文使用外部供电)。

  • 地(GND):共地。

2. GPIO 初始化代码

// 定义DS18B20连接引脚
#define DS18B20_PORT    GPIOG
#define DS18B20_PIN     GPIO_Pin_11
#define DS18B20_IO_OUT  DS18B20_PORT->ODR
#define DS18B20_IO_IN   DS18B20_PORT->IDR
​
void DS18B20_Init(void) {
    // 1. 打开GPIOG时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
    
    // 2. 配置PG11为推挽输出,50MHz
    GPIO_InitTypeDef GPIO_Config;
    GPIO_Config.GPIO_Pin   = DS18B20_PIN;
    GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Config.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(DS18B20_PORT, &GPIO_Config);
}

三、一线式总线通信协议详解

1. 通信三部曲

由厂家提供的芯片手册可知要想获取温度值,访问 DS18B20 需遵循固定顺序:

  1. 初始化复位:类似 I2C 的 START 信号,检测设备是否在线。

  2. 发送 ROM 命令:识别总线上的特定设备(如读取唯一 ID 或跳过 ID 直接访问)。

  3. 发送功能命令:执行具体操作(如温度转换、读取寄存器)。

2. 底层时序实现

(1)初始化复位信号

// 初始化复位
void DS18B20_Reset(void) {
    u8 tempTime = 0;
    
    // 1. 拉低总线 >=480us
    DS18B20_OUT();
    DS18B20_IO_OUT = 0;
    delay_us(500);
    
    // 2. 释放总线(拉高)15~60us
    DS18B20_IO_OUT = 1;
    delay_us(30);
    
    // 3. 等待DS18B20应答(拉低60~240us)
    DS18B20_IN();
    while (DS18B20_IO_IN && tempTime < 240) {
        tempTime++;
        delay_us(1);
    }
    
    if (tempTime >= 240) {
        printf("DS18B20 Reset Failed\n");
    } else {
        printf("DS18B20 Reset Success\n");
        tempTime = 0;
    }
    
    // 4. 总线恢复高电平
    delay_us(200);
}
(2)数据读写操作

// 发送单字节
void DS18B20_Write_Byte(u8 data) {
    u8 i;
    DS18B20_OUT();
    
    for (i = 0; i < 8; i++) {
        if (data & 0x01) {  // 写1
            // 拉低 >1us
            DS18B20_IO_OUT = 0;
            delay_us(2);
            // 拉高 >45us
            DS18B20_IO_OUT = 1;
            delay_us(60);
        } else {  // 写0
            // 拉低60~120us
            DS18B20_IO_OUT = 0;
            delay_us(60);
            // 拉高 >1us
            DS18B20_IO_OUT = 1;
            delay_us(2);
        }
        data >>= 1;
    }
}
​
// 读取单字节
u8 DS18B20_Read_Byte(void) {
    u8 i, data = 0;
    
    for (i = 0; i < 8; i++) {
        // 1. 拉低 >1us
        DS18B20_OUT();
        DS18B20_IO_OUT = 0;
        delay_us(2);
        
        // 2. 释放总线,准备读取
        DS18B20_IN();
        delay_us(8);  // 等待DS18B20输出数据
        
        // 3. 读取数据
        data |= DS18B20_IO_IN << i;
        delay_us(50);  // 完成读时隙
    }
    return data;
}

四、DS18B20 温度采集实战

1. ROM 命令与功能命令

DS18B20 支持多种 ROM 命令,常见的有:

  • SKIP ROM(0xCC):跳过 ROM 匹配,适用于单设备场景。

  • MATCH ROM(0x55):匹配特定设备的 64 位 ROM ID,适用于多设备场景。

功能命令则包括:

  • CONVERT T(0x44):启动温度转换。

  • READ SCRATCHPAD(0xBE):读取温度寄存器数据。

2. 温度采集代码实现

(1)单设备场景(SKIP ROM)
// 获取温度值(单设备)
float DS18B20_GetTemperature(void) {
    u8 temp_lsb, temp_msb;
    u16 temp;
    float value;
    
    // 1. 初始化 -> SKIP ROM -> 启动温度转换
    DS18B20_Reset();
    DS18B20_Write_Byte(0xCC);  // SKIP ROM
    DS18B20_Write_Byte(0x44);  // CONVERT T
    delay_ms(800);  // 等待转换完成(最大750ms)
    
    // 2. 初始化 -> SKIP ROM -> 读取温度寄存器
    DS18B20_Reset();
    DS18B20_Write_Byte(0xCC);  // SKIP ROM
    DS18B20_Write_Byte(0xBE);  // READ SCRATCHPAD
    
    // 3. 读取温度数据(低8位和高8位)
    temp_lsb = DS18B20_Read_Byte();
    temp_msb = DS18B20_Read_Byte();
    temp = (temp_msb << 8) | temp_lsb;
    
    // 4. 温度值转换(分辨率默认为12位)
    if ((temp & 0xF800) == 0xF800) {  // 负数
        temp = (~temp) + 1;
        value = temp * (-0.0625);
    } else {  // 正数
        value = temp * 0.0625;
    }
    
    return value;
}
(2)多设备场景(MATCH ROM)
u8 rom[8];  // 存储DS18B20的64位ROM ID
​
// 读取ROM值
void DS18B20_ReadRom(void) {
    u8 i;
    
    DS18B20_Reset();
    DS18B20_Write_Byte(0x33);  // READ ROM
    
    for (i = 0; i < 8; i++) {
        rom[i] = DS18B20_Read_Byte();
        printf("%02X ", rom[i]);
    }
    printf("\n");
}
​
// 匹配指定ROM的设备
void DS18B20_MatchRom(void) {
    u8 i;
    
    DS18B20_Reset();
    DS18B20_Write_Byte(0x55);  // MATCH ROM
    
    for (i = 0; i < 8; i++) {
        DS18B20_Write_Byte(rom[i]);
    }
}
​
// 获取指定设备的温度(多设备场景)
float DS18B20_GetTemperature(void) {
    // 与单设备类似,但将SKIP ROM替换为MATCH ROM
    // ...
}

五、常见问题与优化建议

1. 通信稳定性问题

  • 上拉电阻:务必使用 4.7KΩ 上拉电阻,确保总线在空闲时为高电平。

  • 延时精度:一线式总线对时序要求极高,建议使用精确的微秒级延时函数。

  • 线路长度:数据线过长会导致信号衰减,建议控制在 2 米以内(或增加驱动电路)。

2. 多设备寻址技巧

  • ROM 搜索算法:通过SEARCH ROM(0xF0)命令遍历总线上所有设备的 ROM ID。

  • 寄生电源模式:DS18B20 可通过数据线获取电源,但可能影响稳定性,建议优先使用外部供电。

3. 代码优化方向

  • 超时处理:在关键通信环节添加超时检测,避免程序卡死。

  • CRC 校验:DS18B20 的 ROM 和数据寄存器包含 CRC 校验值,可提高数据可靠性。

六、总结

一线式总线以其极简的硬件设计和独特的通信机制,为嵌入式系统提供了一种低成本、易扩展的解决方案。通过本文的实战案例,我们掌握了 DS18B20 温度传感器的基本原理和编程方法,理解了一线式总线的通信协议与实现技巧。

在实际应用中,一线式总线不仅适用于温度采集,还可用于湿度、压力等多种传感器的连接。掌握这一技术,将为您的嵌入式开发工具箱增添一件有力武器!


网站公告

今日签到

点亮在社区的每一天
去签到