DS18B20数字温度传感器

DS18B20是由 DALLAS 半导体公司推出的一种的“单总线”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。

单总线结构具有简洁且经济的特点,可使用户轻松地组建传感器网络,从而为测量系统的构建引入全新概念,测量温度范围为-55到+125℃,精度为±0.5℃。现场温度直接以“单总线”的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现 9-12 位的数字值读数方式。它工作在 3~5.5V 的电压范围,采用多种封装形式,从而使系统设计灵活、方便,设定分辨率及用户设定的报警温度存储在 EEPROM 中,掉电后依然保存。以下是其内部结构图:

了解更多单总线的原理,请参考单总线通信。更多传感器详细数据,参考DS18B20数据手册

技术说明

测温范围 -55℃ ~ + 125℃
温度分辨率 0.0625℃ 0.125℃ 0.25℃ 0.5℃
工作电源 3~5V/DC

内部配置

ROM

ROM中的64位序列号是出厂前被光记好的,它可以看作是该DS18B20的地址序列码,每个DS18B20 的64位序列号均不相同。64位ROM的排列是:

[7:0] 产品家族码
[55:8] DS18B20序列号
[63:56] 循环冗余校验码

最低8位能够判断该产品是否属于DS18B20的系列。

最高8位是前面56位的循环冗余校验码(CRC=X8+X5+X4+1)。ROM 作用是使每一个DS18B20都各不相同,这样就可实现一根总线上挂接多个 DS18B20。

所有的单总线器件要求采用严格的信号时序,以保证数据的完整性。DS18B20 共有 6 种信
号类型:复位脉冲、应答脉冲、写 0、写 1、读 0 和读 1。所有这些信号,除了应答脉冲以外,都由主设备发出同步信号。并且发送所有的命令和数据都是字节的低位在前。利用OneWire.h头文件的函数,已配置好时序。想要深入了解时序,参考单总线通讯信号时序部分。

储存器

储存器结构如下所示:

温度测量原理

DS18B20的核心功能在于能够直接测量数字温度的传感器。温度传感器的测量范围由用户配置,可以为9、10、11或12位,相应的增量则为0.25℃、0.125℃、0.0625℃ 。默认为12位,可以通过修改配置寄存器的第6位(R1)和第5位(R0)来设置测量位数。

当主设备发出温度转换的命令(44h)后,传感器开始温度测量和ADC转换,并将数据储存在暂存器的最低两字节中。其数据与实际温度的对应关系如下表。

其中“S”位符号标记,当温度为负时,则符号位全部置1,反之置0。以下是一部分例子。

温度报警

DS18B20执行温度转换后,将温度值与存储在最高与最低温度报警(TH和TL)寄存器中的触发值进行比较(见下图)。

符号位表示值是正还是负:对于正数S=0,对于负数则S=1。TH和TL寄存器是非易失性的(EEPROM),因此当设备断电仍可保存。TH和TL可以通过暂存器的第2字节和第3字节来访问。

器件功能指令

在主设备发出ROM命令后,访问特定地址的DS18B20,接着就可以发出DS18B20支持的功能命令,从而写入或者读取DS18B20暂存器、启动温度转换以及判断设备的供电方式。

DS18B20功能命令集,如下所示:

命令 描述 命令代码 发送命令后,单总线上的响应信息 注释
温度转换命令
转换温度 开始温度转换 44h DS18B20向主设备传输数据(不适用于外部供电的DS18B20) 1
储存器命令
读取暂存器 读取整个暂存器的代码,其中包括CRC校验码 BEh DS18B20向主设备传输多达9个字节的数据 2
写暂存器 向暂存器的第2、3、4字节写入数据(对应高、低温度报警触发寄存器以及配置寄存器) 4Eh 主设备传输3个字节数据到DS18B20 3
复制暂存器 向将EEPROM中的高、低温度报警触发寄存器以及配置寄存器数据复制到暂存器 D8h 1
回读EEPROM 将高、低温度报警触发寄存器以及配置寄存器数据从EEPROM回读至暂存器中 B8h DS18B20传送回读状态至主机
读取电力供应 将DS18B20的电力供应模式发送给主设备 B4h DS18B20向主设备传输供应数据
  1. 在温度转换和复制暂存器数据至EEPROM期间,主设备必须在单总线上允许强上拉,并且在此期间,总线上不能进行其他数据传输。
  2. 通过发出复位脉冲,主设备能够在任何时候中断数据传输。
  3. 在复位脉冲发出前,必须写入全部的三个字节。

DS18B20功能流程图:

代码

#include <Arduino.h>
#include <OneWire.h>


OneWire  ds(10);  // 选择I/O,实例化对象

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();

  // 利用ROM最低8位判断是否属于DS18B20系列,以及具体的种类
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // 老款DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // 开始温度转换, 并保持电源接通单总线

  delay(1000);    


  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // 读取暂存器

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // 温度数据以9位字节存储
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // 将数据转换为实际温度:
  //由于最终结果是16位整形数据,因此被存储为“int16_t”类型 
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 老款DS18B20默认为9位
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // 通过第四字节判断保留的温度数据位数,将多余的位数置0
    if (cfg == 0x00) raw = raw & ~7;  // 9位, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10位, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11位, 375 ms
    // 默认为12位,不需要处理,转换时间750 ms 
  }
  celsius = (float)raw / 16.0;//将数据转化为实际温度,即把数据乘以0.0625
  fahrenheit = celsius * 1.8 + 32.0;//摄氏度转华氏温度
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
}
文档更新时间: 2021-02-14 14:30   作者:Astilbe