54、DS18B20单线数字温度采集

DS18B20单线数字温度采集

一、DS18B20核心特性与硬件基础

1. 关键参数(必记!)

参数 规格细节
测量量程 -55℃ ~ +125℃(工业级场景全覆盖)
测量精度 -10℃~+85℃范围内±0.5℃,全量程误差≤±2℃
分辨率 9~12位可调(默认12位):9位=0.5℃、10位=0.25℃、11位=0.125℃、12位=0.0625℃
工作电压 3V~5.5V(兼容51单片机3.3V/5V供电,无需额外稳压)
通信接口 GPIO单线总线(仅需1根I/O引脚+GND,硬件成本极低)
核心优势 无需外部元件、抗干扰强、支持多传感器组网、掉电保留配置

2. 引脚定义与接线方式

DS18B20采用TO-92封装,共3个引脚,51单片机接线如下:

DS18B20引脚 功能 51单片机连接方式
VDD 电源引脚 接3.3V/5V(外部电源模式),或悬空(寄生电源模式)
DQ 数据I/O 接任意GPIO引脚(本文代码用P3.7)+ 4.7KΩ上拉电阻
GND 接单片机GND(必须共地,否则信号干扰)

关键提醒:单线总线必须串联4.7KΩ上拉电阻,确保总线空闲时为高电平,通信稳定。


二、DS18B20核心时序原理(通信关键!)

DS18B20的通信依赖严格的时序协议,所有操作(复位、写数据、读数据)都需遵循单线总线的时序规则,这是采集成功的核心。

1. 复位时序(初始化)

所有通信必须从复位开始,流程如下:

  1. 主机(51单片机)拉低总线 ≥480μs(复位脉冲);
  2. 主机释放总线,拉高后切换为输入模式;
  3. DS18B20检测到上升沿后,延迟15~60μs,拉低总线 60~240μs(存在脉冲),告知主机"已就绪";
  4. DS18B20释放总线,恢复高电平,进入空闲状态。

代码对应:ds18b20_reset()函数中,通过Delay10us(70)实现700μs复位拉低,Delay10us(6)等待存在脉冲。

2. 写时序(主机→DS18B20)

主机通过不同的低电平时长传递"0"或"1",每次仅发送1位,8位为1字节(LSB优先):

  • 写0 :拉低总线 ≥60μs → 释放总线(拉高);DS18B20在60μs内采样,低电平视为"0";
  • 写1 :拉低总线 1~15μs → 释放总线(拉高);DS18B20采样到高电平视为"1";
  • 两次写操作间隔 ≥1μs(恢复时间)。

代码对应:write_ds18b20()函数中,dat&1判断当前位,短延时对应写1,长延时对应写0。

3. 读时序(DS18B20→主机)

主机先拉低总线触发读操作,再释放总线由DS18B20控制总线电平,流程如下:

  1. 主机拉低总线 ≥1μs → 立即释放总线(拉高);
  2. 主机在拉低后15μs内采样总线电平(高=1,低=0);
  3. 单次读操作时长 ≥60μs,两次读间隔 ≥1μs。

代码对应:read_ds18b20()函数中,拉低后快速释放,通过DQ_CHECK检测电平,存入dat对应位。


三、核心命令解析(DS18B20操作灵魂)

DS18B20通过主机发送的8位命令执行对应操作,本文代码用到3个核心命令:

命令字节 命令名称 功能说明
0xCC Skip ROM(跳过ROM) 无需读取DS18B20的唯一64位ROM编码,直接进入功能操作(单传感器场景首选,节省时间)
0x44 Convert T(温度转换) 启动一次温度测量,转换时间与分辨率相关(12位约750ms),转换期间总线需保持高电平
0xBE Read Scratchpad(读暂存器) 读取DS18B20内部暂存器数据(共9字节,前2字节为温度数据)

扩展:多传感器组网时,需用0x55(Match ROM)命令+64位ROM编码,定位目标传感器。


四、51单片机代码完整解析

代码基于P3.7引脚实现,分"复位→测温→读数→数据解析"四大步骤,可直接编译下载使用。

1. 头文件与宏定义(ds18b20.h)

c 复制代码
#ifndef __DS18B20_H__
#define __DS18B20_H__

#include <reg51.h>

// 函数声明
int ds18b20_reset(void);          // 复位DS18B20
void write_ds18b20(unsigned char dat); // 写1字节到DS18B20
unsigned char read_ds18b20(void);    // 从DS18B20读1字节
float get_temp(void);               // 获取温度值(返回浮点型)

#endif

2. 核心功能实现(ds18b20.c)

c 复制代码
#include <reg51.h>
#include <intrins.h>
#include "ds18b20.h"
#include "delay.h"

// 宏定义:P3.7作为DQ引脚
#define  DQ_DOWN  (P3 &= ~(1 << 7))  // 拉低DQ
#define  DQ_HIGH  (P3 |= (1 << 7))  // 拉高DQ
#define  DQ_CHECK ((P3 & (1 << 7)) != 0)  // 检测DQ电平

/**
 * @brief  DS18B20复位初始化
 * @retval 1-复位成功,0-复位失败
 */
int ds18b20_reset(void)
{
    int t = 0;

    // 1. 发送复位脉冲(拉低≥480μs)
    DQ_DOWN;
    Delay10us(70);  // 70×10μs=700μs,满足≥480μs要求
    DQ_HIGH;         // 释放总线
    Delay10us(6);    // 等待60μs,准备接收存在脉冲

    // 2. 检测存在脉冲(DS18B20拉低总线)
    while (DQ_CHECK && t < 30)  // 超时300μs未检测到低电平→失败
    {
        Delay10us(1);
        t++;
    }
    if (t >= 30) return 0;  // 复位失败

    // 3. 等待存在脉冲结束(DS18B20拉高总线)
    t = 0;
    while (!DQ_CHECK && t < 30)  // 超时300μs未拉高→失败
    {
        Delay10us(1);
        t++;
    }
    if (t >= 30) return 0;  // 复位失败

    return 1;  // 复位成功
}

/**
 * @brief  向DS18B20写1字节数据(LSB优先)
 * @param  dat:要发送的字节
 */
void write_ds18b20(unsigned char dat)
{
    int i = 0;

    for (i = 0; i < 8; i++)  // 循环8次,每次写1位
    {
        if (dat & 1)  // 写1:拉低1~15μs
        {
            DQ_DOWN;
            _nop_();  // 短延时(约1μs)
            _nop_();
            DQ_HIGH;  // 释放总线
            Delay10us(5);  // 等待45μs,确保DS18B20采样
        }
        else  // 写0:拉低≥60μs
        {
            DQ_DOWN;
            Delay10us(6);  // 60μs
            DQ_HIGH;      // 释放总线
        }
        dat >>= 1;  // 右移1位,准备写下一位(LSB优先)
    }
}

/**
 * @brief  从DS18B20读1字节数据(LSB优先)
 * @retval 读取到的字节
 */
unsigned char read_ds18b20(void)
{
    unsigned char dat = 0;
    int i = 0;

    for (i = 0; i < 8; i++)  // 循环8次,每次读1位
    {
        DQ_DOWN;  // 拉低≥1μs,触发读操作
        _nop_();
        _nop_();
        DQ_HIGH;   // 释放总线,由DS18B20控制电平
        _nop_();
        _nop_();
        _nop_();    // 延时约3μs,准备采样

        if (DQ_CHECK)  // 采样电平:高=1,低=0
        {
            dat |= (1 << i);  // 存入对应位(LSB优先)
        }
        Delay10us(6);  // 单次读操作≥60μs
    }

    return dat;
}

/**
 * @brief  获取温度值
 * @retval 浮点型温度(精度0.0625℃)
 */
float get_temp(void)
{
    unsigned char tl = 0;  // 温度低字节(LSB)
    unsigned char th = 0;  // 温度高字节(MSB,含符号位)
    short t = 0;           // 组合后的16位温度数据

    // 1. 复位→跳过ROM→启动温度转换
    if (ds18b20_reset() == 0) return -99.9;  // 复位失败返回错误值
    write_ds18b20(0xCC);  // 跳过ROM(单传感器)
    write_ds18b20(0x44);  // 启动温度转换
    Delay1ms(1000);       // 等待转换完成(12位分辨率需750ms以上)

    // 2. 复位→跳过ROM→读暂存器
    ds18b20_reset();
    write_ds18b20(0xCC);  // 跳过ROM
    write_ds18b20(0xBE);  // 读暂存器

    // 3. 读取温度数据(前2字节为温度值,LSB先读)
    tl = read_ds18b20();  // 低字节
    th = read_ds18b20();  // 高字节

    // 4. 组合温度数据(16位带符号补码)
    t = th << 8;  // 高字节左移8位
    t |= tl;      // 拼接低字节

    // 5. 温度换算:12位分辨率→1LSB=0.0625℃
    return t * 0.0625;
}

3. 延时函数支持(delay.c/h)

DS18B20时序对延时精度要求高,需实现10μs、1ms级延时(11.0592MHz晶振):

c 复制代码
// delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay10us(unsigned int n);
void Delay1ms(unsigned int n);
#endif

// delay.c
#include <reg51.h>
void Delay10us(unsigned int n)
{
    unsigned int i, j;
    for (i = n; i > 0; i--)
        for (j = 2; j > 0; j--);  // 11.0592MHz下≈10μs
}

void Delay1ms(unsigned int n)
{
    unsigned int i, j;
    for (i = n; i > 0; i--)
        for (j = 110; j > 0; j--);  // 11.0592MHz下≈1ms
}

4. 主函数调用示例(main.c)

c 复制代码
#include <reg51.h>
#include "ds18b20.h"
#include "uart.h"  // 假设已实现串口发送函数

void main(void)
{
    float temp;
    uart_init();  // 初始化串口(用于打印温度)

    while (1)
    {
        temp = get_temp();  // 获取温度
        if (temp != -99.9)  // 采集成功
        {
            // 串口打印温度(需实现浮点型转字符串函数,此处省略)
            uart_sendstr("当前温度:");
            // 示例:打印整数部分+小数部分
        }
        Delay1ms(2000);  // 每2秒采集一次
    }
}

五、实战关键注意事项

  1. 上拉电阻不可少:4.7KΩ上拉电阻是单线总线稳定通信的核心,缺少会导致复位失败、数据传输错误;
  2. 延时精度要达标:时序中的时间参数(如复位480μs、写0 60μs)需严格匹配,延时误差过大会导致采集失败;
  3. 温度转换需等待:启动转换(0x44命令)后,必须等待足够时间(12位分辨率≥750ms),否则读取到的是旧数据;
  4. 寄生电源模式注意:若DS18B20采用寄生电源(VDD悬空),转换期间总线需保持高电平,不能进行其他操作;
  5. 多传感器组网 :需使用0x55(Match ROM)命令+传感器唯一64位ROM编码,避免数据冲突。

六、温度数据解析原理

DS18B20的温度数据以16位带符号补码形式存储,格式如下:

位15(MSB) 位14-11 位10-4 位3-0(LSB)
符号位(0=正,1=负) 整数部分 小数部分 小数部分(12位分辨率)
  • 正数:直接按"整数部分×1 + 小数部分×0.0625"换算;
  • 负数:按补码规则转换(取反+1)后再换算,符号为负;
  • 示例:温度25.5℃→二进制00000000 00011001.1000→十六进制0x00198→换算为25 + 8×0.0625=25.5℃。

总结

DS18B20的核心优势在于"单线通信+极简硬件",学习重点集中在时序协议命令解析:复位是通信前提,写/读时序是数据传输基础,温度转换和读暂存器命令是核心操作。掌握本文代码和原理后,可轻松扩展多传感器组网、温度报警(利用TH/TL寄存器)、串口上传温度等功能,适用于环境监测、设备温控等嵌入式场景。

相关推荐
一念杂记2 小时前
实测有效!手把手教你免费领一台服务器,程序员/博主必备~
服务器
Gofarlic_OMS2 小时前
MATLAB许可证闲置自动检测与智能提醒
java·大数据·运维·开发语言·人工智能·算法·matlab
yaoxin5211232 小时前
293. Java Stream API - 从 HTTP 源创建 Stream
java·开发语言·http
哟哟耶耶2 小时前
java-MySql下载与配置环境变量
java·开发语言·mysql
Vallelonga2 小时前
ELF 文件和 Linux 内核镜像文件
linux·经验分享
e***98572 小时前
C语言轮子大赛:从零打造经典轮子
c语言·开发语言
初願致夕霞2 小时前
实现具备C++11现代特性的STL——vector篇(附带简单的航空订票系统实例)
开发语言·c++·rpc
zzzsde2 小时前
【Linux】基础开发工具(4):自动化构建--make/makefile
linux·运维·服务器
云泽8082 小时前
C++ 模板进阶全解析:非类型模板参数、模板特化与分离编译详解
开发语言·c++