在嵌入式开发中,DS18B20 是单总线协议的典型代表。由于单总线对时序要求极严(微秒级),传统 GPIO 翻转易受中断干扰。本文重点介绍如何通过 UART(串口)硬件 完美模拟 1-Wire 时序。

一、什么是单总线(1-Wire)?

单总线(1-Wire)是由 Dallas(现 Maxim)开发的异步半双工通信协议。

  • 极简连线: 最少连线仅需 一根数据线(DQ)+ GND。
  • 物理特性: 采用 开漏输出(Open Drain),通过外部上拉电阻(通常 4.7kΩ)保证空闲时总线为高电平。
  • 主从结构: 主机发起,从机响应。
  • 唯一标识: 每个从机出厂自带全球唯一的 64 位 ROM ID,支持一根线上挂载多个传感器。
  • 时序敏感: 通信时序要求严格,主机必须精确控制拉低和释放总线的时间。

二、通信基本流程

一次完整通信包含以下步骤:

1. 初始化(Reset + Presence)

  • 主机拉低 ≥ 480µs(Reset)
  • 释放总线
  • 从机等待 15~60µs 后拉低 60~240µs(Presence)

表示设备在线并准备通信

2. 发送 ROM 命令

用于设备寻址:

命令 功能
Read ROM (33h) 读取唯一ID(单设备)
Match ROM (55h) 选择指定设备
Skip ROM (CCh) 跳过寻址(单设备)
Search ROM (F0h) 搜索所有设备
Alarm Search (ECh) 搜索报警设备

3. 发送功能命令

针对设备内部功能操作:

命令 功能
Convert T (44h) 启动温度转换
Read Scratchpad (BEh) 读取数据
Write Scratchpad (4Eh) 写配置
Copy Scratchpad (48h) 写入 EEPROM
Recall E2 (B8h) 读取 EEPROM
Read Power Supply (B4h) 查询供电方式

4. 数据读写

写时序:

  • 写 0:拉低时间长(60 ~ 120µs)
  • 写 1:拉低时间短(1~15µs)

读时序:

  • 主机拉低后释放
  • 从机决定电平:
    • 低 → 0
    • 高 → 1

三、UART模拟单总线通信原理

利用 UART 的起始位(Start Bit)始终为低电平,以及数据位可控的特性,通过切换波特率来模拟 1-Wire 的各种脉冲。

硬件连接: UART 的 TX 和 RX 必须短接(Half-Duplex 模式或物理短接),并连接一个上拉电阻 到 3.3V。

1. 复位与存在检测(波特率:9600)

  • 原理: 9600 下 1 个 Bit 约为 104µs。
  • 发送 0xF0:
    • 时序:1 个起始位(0) + 4 个数据位(0) + 4 个数据位(1) + 1 个停止位(1)。
    • 低电平时长:(满足 Reset 要求的 >480µs)。
    • 高电平释放期:后 5 个 bit 为高,DS18B20 会在此期间拉低总线产生 Presence(存在信号)。
  • 判断逻辑: 若 Receive != 0xF0,说明有从机拉低了总线,传感器在线。

2. 位读写(波特率:115200)

  • 原理: 115200 下 1 个 Bit 约为 8.68µs。
操作 UART 发送字节 信号成分 低电平总时长 1-Wire 标准要求
写 1 / 读请求 0xFF 仅起始位为 0 8.68µs 1 ~ 15µs
写 0 0x00 起始位 + 8位数据全 0 78.12µs 60 ~ 120µs

四、代码示例

1. 单个设备代码示例:

// 初始化 UART 波特率的辅助函数
static void DS18B20_UART_ReInit(uint32_t baudrate) {
    huart2.Init.BaudRate = baudrate;
    HAL_UART_Init(&huart2);
}

// 发送并接收一个位 (115200bps)
static uint8_t DS18B20_WriteRead_Bit(uint8_t bit_val) {
    uint8_t tx_data = bit_val ? 0xFF : 0x00; // 发送 0xFF 代表释放总线读 1,0x00 代表拉低读 0
    uint8_t rx_data = 0;

    HAL_UART_Transmit(&huart2, &tx_data, 1, 10);
    HAL_UART_Receive(&huart2, &rx_data, 1, 10);

    return (rx_data == 0xFF) ? 1 : 0; // 如果收回也是 0xFF,说明总线被拉高,读到 1
}

// 发送一个字节 (8位)
static void DS18B20_WriteByte(uint8_t data) {
    for (int i = 0; i < 8; i++) {
        DS18B20_WriteRead_Bit(data & 0x01);
        data >>= 1;
    }
}

// 读取一个字节 (8位)
static uint8_t DS18B20_ReadByte(void) {
    uint8_t data = 0;
    for (int i = 0; i < 8; i++) {
        if (DS18B20_WriteRead_Bit(1)) { // 发送 1 释放总线,读取回复
            data |= (1 << i);
        }
    }
    return data;
}

// 3. 读取完整温度
float DS18B20_GetTemp(void) {
    uint8_t data[2];
    uint8_t presence_echo = 0;
    uint8_t reset_cmd = 0xF0;

    // --- 1. 启动转换 ---
    DS18B20_UART_ReInit(9600);
    HAL_UART_Transmit(&huart2, &reset_cmd, 1, 10);
    HAL_UART_Receive(&huart2, &presence_echo, 1, 10);
    if (presence_echo == 0xF0) return -101.0; // 没检测到传感器

    DS18B20_UART_ReInit(115200);
    DS18B20_WriteByte(0xCC); // Skip ROM
    DS18B20_WriteByte(0x44); // Convert T

    HAL_Delay(750); // 等待转换

    // --- 2. 读取结果 (必须重新 Reset) ---
    DS18B20_UART_ReInit(9600);
    HAL_UART_Transmit(&huart2, &reset_cmd, 1, 10);
    HAL_UART_Receive(&huart2, &presence_echo, 1, 10);
    if (presence_echo == 0xF0) return -102.0;

    DS18B20_UART_ReInit(115200);
    DS18B20_WriteByte(0xCC); // Skip ROM
    DS18B20_WriteByte(0xBE); // Read Scratchpad

    data[0] = DS18B20_ReadByte(); // LSB
    data[1] = DS18B20_ReadByte(); // MSB

    int16_t temp_raw = (data[1] << 8) | data[0];
    return temp_raw * 0.0625f;
}

2. 多设备支持

如果总线上有多个设备,必须使用 Match ROM 或 Search ROM 来区分不同设备。

  • 匹配ID读取:
    DS18B20_UART_ReInit(115200);
    DS18B20_WriteByte(0x55); // Match ROM 指令
    DS18B20_WriteROM(ROM_IDs[rom_id]);   // 发送指定的 8 字节 ID
    DS18B20_WriteByte(0xBE); // Read Scratchpad

在正式布线先,可以逐个接入传感器以确认 ID,或使用 Search ROM 命令扫描总线上的所有设备。

  • 读取单个设备的 ROM ID 示例:
void DS18B20_Read_Single_ROM(void) {
    // 1. 复位 (9600 bps)
    DS18B20_UART_ReInit(9600);
    uint8_t reset_cmd = 0xF0;
    uint8_t echo;
    HAL_UART_Transmit(&huart2, &reset_cmd, 1, 10);
    HAL_UART_Receive(&huart2, &echo, 1, 10);

    uint8_t current_id[8];

    if (echo != 0xF0) { // 检测到设备
        // 2. 发送 Read ROM 命令 (0x33h) @ 115200 bps
        DS18B20_UART_ReInit(115200);
        DS18B20_WriteByte(0x33);

        // 3. 连续读取 8 个字节
        for (int i = 0; i < 8; i++) {
            current_id[i] = DS18B20_ReadByte();
        }

        // 4. 打印出来 (假设你已经配置好了 printf)
        printf("ROM ID: ");
        for (int i = 0; i < 8; i++) {
            printf("%02X ", current_id[i]);
        }
        printf("\r\n");
    } else {
        printf("No Sensor Found!\r\n");
    }
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐