目录

DS18B20

参数

  • 单线总线结构, 允许一根总线上挂接多个 DS18B20 并分别通信
  • 在普通温度下, 可以直接从数据口取电, 这时候只需要两根连线.
  • 供电电压 [3.0V, 5.5V]
  • 温度检测范围 [-55°C, +125°C]摄氏度, [-67°F, +257°F]华氏度
  • 精确率: 在 [-10°C, +85°C] 为 ±0.5°C

Pin脚

一般见到的都是3pin的To-92封装, 和普通三极管一样, 使平面朝向自己, Pin脚朝下, 从左往右依次为: GND, DQ, VDD

内部存储结构

DS18B20内部有9字节的暂存器和3个字节的EEPROM存储, EEPROM可以擦写5万次以上. 结构如下

测温

DS18B20的核心功能就是数字化的温度读数, 可以设置为9, 10, 11, 12位分辨率, 缺省分辨率是12位. 各分辨率对应的读数, 温度分辨率分别是0.5, 0.25, 0.125, 0.0625摄氏度.

在执行温度转换命令Convert T0x44后, 温度会被转换并存储在一个2字节的内存单元, 然后通过读取命令Read Scratchpad0xBE读出.

转换时间

在温度转换命令Convert T0x44发起到采集完成需要的时间可能会长达750 ms. 实际使用中, 不同批次 DS18B20 的转换时间差异也很大, 有的在200-300 ms, 有的接近 800 ms. 貌似越是最近制造的时间越短(可能是工艺改进了?).

如果没有从VDD供电, DS18B20 的 DQ 必须在转换过程中保持高电平以提供能量, 因此在这种场景下, 采集的过程中不允许进行其他活动.

读数结构

这两个字节各个bit分别代表的数字含义如下, 高字节的高5位仅用于表示温度的正负, 正温度是0, 负温度是1, 后面11个bit表示的数字, 负值使用的是补码, 读数用 (0xFF - 读数)

  • 正温度时, 将16位整数乘以对应的温度分辨率
  • 负温度时, 将16位整数取反加1后, 乘以对应的温度分辨率
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
S S S S S \(2^6\) \(2^5\) \(2^4\) \(2^3\) \(2^2\) \(2^1\) \(2^0\) \(2^-1\) \(2^-2\) \(2^-3\) \(2^-4\)
MSB LSB MSB LSB

读数快查表

上电后的缺省值为0x0550, 对应85°C, 如果一直读出都是这个值, 需要检查接线

TEMPERATURE DIGITAL OUTPUT (Binary) DIGITAL OUTPUT (Hex)
+125°C 0000 0111 1101 0000 07D0h
+85°C 0000 0101 0101 0000 0550h*
+25.0625°C 0000 0001 1001 0001 0191h
+10.125°C 0000 0000 1010 0010 00A2h
+0.5°C 0000 0000 0000 1000 0008h
0°C 0000 0000 0000 0000 0000h
-0.5°C 1111 1111 1111 1000 FFF8h
-10.125°C 1111 1111 0101 1110 FF5Eh
-25.0625°C 1111 1110 0110 1111 FF6Fh
-55°C 1111 1100 1001 0000 FC90h

ROM读数

每个 DS18B20 包含一个唯一的只读的64bit编码, 其结构为

  1. 最初 8 bits 为固定的 0x28, 1-Wire family code
  2. 接下来的 48 bits 是唯一序列号
  3. 最后的 8 bits 是前面 56 bits 的 CRC 校验码.

这个 64-bit ROM 和 ROM 方法允许在单线(1-Wire)总线上运行多个 DS18B20, 使用单线总线需要使用下面的方法之一发起:

  1. Read ROM,
  2. Match ROM,
  3. Search ROM,
  4. Skip ROM, or
  5. Alarm Search.

After a ROM function sequence has been successfully executed, the functions specific to the DS18B20 are accessible and the

bus master may then provide one of the six memory and control function commands.

CRC 计算

DS18B20 在读取8字节ROM和9字节暂存器时, 最后一个字节都是前面所有字节的CRC校验值. CRC值的比较与是否继续操作完全由总线控制端决定, DS18B20 内部仅计算CRC, 并不会对CRC不匹配的情况进行处理, 需要总线控制端主动判断.

计算CRC的等效多项式函数为(这是datasheet中的式子, 并非幂运算, 要结合后面的流程图理解)

\(CRC = X^8 + X^5 + X^4 + 1\)

1-Wire总线的CRC计算由移位寄存器和异或门组成的多项式发生器来执行: 移位寄存器位初始化为0, 然后从第一个字节的最低位开始, 一次移入一位, 根据计算结果决定是否与第4, 第5位作异或, 然后CRC也往右移, 最后移位寄存器的值就是CRC.

使用C语言表示的8位CRC计算函数为

uint8_t DS18B20_Crc(uint8_t *addr, uint8_t len)
{
uint8_t crc = 0, inbyte, i, mix; while (len--)
{
// inbyte 存储当前参与计算的新字节
inbyte = *addr++; for (i = 8; i; i--)
{
// 将新字节与CRC从低位到高位, 依次做异或运算, 每次运算完CRC右移一位
// 如果运算结果值为1, 则将CRC与 1000 1100 作异或
// 第3,4位代表流程图中的异或运算, 第7位其实就是运算结果移入的1
mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix)
{
crc ^= 0x8C;
}
inbyte >>= 1;
}
}
return crc;
}

ROM Search 搜索算法

当单线总线上挂接了多个DS18B20时, 总线控制端需要通过 ROM Search 命令来判断总线上存在的设备以及获取他们的8字节唯一ROM.

ROM搜索算法是重复进行一个简单的三步操作: 读取一位, 读取这位的补码, 写入这一位的目标值.

总线控制端在8字节ROM的每一位上执行这个三步操作后, 就能知道一个 DS18B20 的 8字节 ROM 值, 如果总线上有多个 DS18B20, 则需要重复多次. 下面的例子假设总线上有4个设备, 对应的ROM值分别为

  • ROM1 00110101...
  • ROM2 10101010...
  • ROM3 11110101...
  • ROM4 00010001...

搜索过程如下

  1. 单线总线控制端(以下简称总控)执行 RESET, 所有的 DS18B20设备(以下简称设备)响应这个RESET
  2. 总控执行 Search ROM 命令
  3. 总控读取1个bit. 这时每个设备都会将自己的ROM的第一个bit放到总线上, ROM1 和 ROM4 会对总线写0(拉低总线), 而 ROM2 和 ROM3 则会对总线写1, 允许总线保持高电平. 这时候总控读取的是0(低电平). 然后总控继续读下一个bit, 每个设备会将第一个bit的补码放到总线上, 这时候 ROM1 和 ROM4 写1, 而 ROM2 和 ROM3 写0, 因此总控依然读到一个0, 这时候总控会知道存在多个设备, 并且它们的ROM在这一位上的值不同. 从每次的两步读取中观察到的值分别有以下的含义
  • 00 一定有多个设备, 且在这一位上值不同
  • 01 所有设备(不一定有多个), ROM在这一位上的值是0
  • 10 所有设备(不一定有多个), ROM在这一位上的值是1
  • 11 总线上没有设备
  1. 总控写入一个bit, 比如写入0, 表示在后面的搜索中屏蔽 ROM2 和 ROM3, 仅留下 ROM1 和 ROM4
  2. 总控再执行两次读操作, 读到的值为0,1, 这表示总线上所有设备在这一位上的值都是0
  3. 总控写入一个bit, 因为值是确定的, 这次写入的是0
  4. 总控再执行两次读操作, 读到的值为0,0, 这表示总线上还有多个设备, 在这一位上的值不同
  5. 总控写入一个bit, 这次写入0, 这将屏蔽 ROM1, 仅留下 ROM4
  6. 总控重复进行三步操作, 读出 ROM4 剩余的位, 完成第一次搜索
  7. 总控再次重复之前的搜索直到第7位
  8. 总控写入一个bit, 这次写入1, 将屏蔽 ROM4, 仅保留 ROM1
  9. 总控通过重复三步操作, 读出 ROM1 剩余的位
  10. 总控再次重复之前的搜索直到第3位
  11. 总控写入一个bit, 这次写入1, 将屏蔽 ROM1 和 ROM4 仅保留 ROM2 和 ROM3
  12. 重复之前的逻辑, 当所有00读数都被处理, 说明设备的ROM已经全部被读取.

注意:

总控通过单线总线读取所有设备, 每个设备需要的时间为

960 µs + (8 + 3 x 64) 61 µs = 13.16 ms

因此这样的结构的识别速度为每秒钟75个设备.

使用STC8H驱动DS18B20

接线

GND  -> GND
P35 -> DQ
3.3V -> VDD

代码

代码可以从GitHub或者Gitee下载

定义 IO

只需要一个Pin, 在STC8H中, 注意要将其设置为上拉, 否则读出来的全是0

#define DS18B20_DQ           P35
#define DS18B20_DQ_PULLUP() GPIO_SetPullUp(GPIO_Port_3, GPIO_Pin_5, HAL_State_ON)
#define DS18B20_DQ_INPUT() GPIO_P3_SetMode(GPIO_Pin_5, GPIO_Mode_Input_HIP)
#define DS18B20_DQ_OUTPUT() GPIO_P3_SetMode(GPIO_Pin_5, GPIO_Mode_InOut_OD)
#define DS18B20_DQ_LOW() DS18B20_DQ=RESET
#define DS18B20_DQ_HIGH() DS18B20_DQ=SET

IO 读写

读一个bit和一个byte

__BIT DS18B20_ReadBit(void)
{
__BIT b = RESET; /* Line low */
DS18B20_DQ = RESET;
DS18B20_DQ_OUTPUT();
SYS_DelayUs(2); /* Release line */
DS18B20_DQ_INPUT();
SYS_DelayUs(10); /* Read line value */
if (DS18B20_DQ) {
/* Bit is HIGH */
b = SET;
} /* Wait 50us to complete 60us period */
SYS_DelayUs(50); /* Return bit value */
return b;
} uint8_t DS18B20_ReadByte(void)
{
uint8_t i = 8, byte = 0;
while (i--)
{
byte >>= 1;
byte |= (DS18B20_ReadBit() << 7);
}
return byte;
}

写一个bit和一个byte

void DS18B20_WriteBit(__BIT b)
{
if (b)
{
/* Set line low */
DS18B20_DQ = RESET;
DS18B20_DQ_OUTPUT();
SYS_DelayUs(10); /* Bit high */
DS18B20_DQ_INPUT(); /* Wait for 55 us and release the line */
SYS_DelayUs(55);
DS18B20_DQ_INPUT();
}
else
{
/* Set line low */
DS18B20_DQ = RESET;
DS18B20_DQ_OUTPUT();
SYS_DelayUs(65); /* Bit high */
DS18B20_DQ_INPUT(); /* Wait for 5 us and release the line */
SYS_DelayUs(5);
DS18B20_DQ_INPUT();
}
} void DS18B20_WriteByte(uint8_t byte)
{
uint8_t i = 8;
/* Write 8 bits */
while (i--)
{
/* LSB bit is first */
DS18B20_WriteBit(byte & 0x01);
byte >>= 1;
}
}

单个 DS18B20 的场景

初始化, 注意设置上拉, 以及输入和输出模式的切换

void DS18B20_Init(void)
{
DS18B20_DQ_PULLUP();
DS18B20_DQ_OUTPUT();
DS18B20_DQ = SET;
SYS_DelayUs(1000);
DS18B20_DQ = RESET;
SYS_DelayUs(1000);
DS18B20_DQ = SET;
SYS_DelayUs(2000);
}

读取温度, 这样读出的值并非温度值, 需要根据上面的温度转换, 乘以对应的温度单元值(默认为0.0625摄氏度)

// 发起转换
DS18B20_StartAll();
// 读取总线, 当转换完成时会变为高电平
while (!DS18B20_AllDone())
{
UART1_TxChar('.');
SYS_Delay(1);
} // 重置总线
DS18B20_Reset();
// 跳过ROM选择
DS18B20_WriteByte(ONEWIRE_CMD_SKIPROM);
// 写入读取暂存器指令
DS18B20_WriteByte(ONEWIRE_CMD_RSCRATCHPAD); // 读出9个字节的数据
for (i = 0; i < 9; i++)
{
/* Read byte by byte */
data[i] = DS18B20_ReadByte();
}
// 温度值位于第1和第2个字节
temperature = data[1];
temperature = temperature << 8 | data[0];

读取ROM

// 重置总线
DS18B20_Reset();
// 写入读取ROM指令, 注意这个命令不能用于连接多个设备的总线, 否则结果读数是无意义的
DS18B20_WriteByte(ONEWIRE_CMD_READROM);
// 读出数据
for (i = 0; i < 8; i++)
{
*buf++ = DS18B20_ReadByte();
}

多个 DS18B20 的场景

指定设备地址, 读取温度

// 重置总线
DS18B20_Reset();
// 根据地址选择设备
DS18B20_Select(addr);
// 对选中的设备, 发起转换
DS18B20_WriteByte(DS18B20_CMD_CONVERTTEMP); // 等待转换结束 // 重置总线
DS18B20_Reset();
// 根据地址选择设备
DS18B20_Select(addr);
// 写入读取暂存器指令
DS18B20_WriteByte(ONEWIRE_CMD_RSCRATCHPAD);
// 读取数据
for (i = 0; i < 9; i++)
{
*buf++ = DS18B20_ReadByte();
}

参考

STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计的更多相关文章

  1. STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  2. STC8H开发(十四): I2C驱动RX8025T高精度实时时钟芯片

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  3. STC8H开发(十五): GPIO驱动Ci24R1无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  4. STC8H开发(十六): GPIO驱动XL2400无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  5. STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  6. STC8H开发(六): SPI驱动ADXL345三轴加速度检测模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  7. STC8H开发(五): SPI驱动nRF24L01无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  8. Linux GPIO键盘驱动开发记录_OMAPL138

    Linux GPIO键盘驱动开发记录_OMAPL138 Linux基本配置完毕了,这几天开始着手Linux驱动的开发,从一个最简单的键盘驱动开始,逐步的了解开发驱动的过程有哪些.看了一下Linux3. ...

  9. STC8H开发(七): I2C驱动MPU6050三轴加速度+三轴角速度检测模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

随机推荐

  1. Ubuntu安装docker-compose(摘自官网,自用)

    安装 Docker Compose 预计阅读时间:8分钟 加速 Docker 桌面中的新功能 Docker Desktop 可帮助您在 Mac 和 Windows 上轻松构建.共享和运行容器,就像在 ...

  2. Windows中Nginx配置nginx.conf不生效解决方法(路径映射)

    Windows中Nginx配置nginx.conf不生效解决方法 今天在做Nginx项目的时候,要处理一个路径映射问题, location /evaluate/ { proxy_pass http:/ ...

  3. LC-283

    题目链接:https://leetcode-cn.com/problems/move-zeroes/ 首先想到了快排(简易思想),0为中间点, 把不等于0(注意题目没说不能有负数)的放到中间点的左边, ...

  4. Python 图_系列之纵横对比 Bellman-Ford 和 Dijkstra 最短路径算法

    1. 前言 因无向.无加权图的任意顶点之间的最短路径由顶点之间的边数决定,可以直接使用原始定义的广度优先搜索算法查找. 但是,无论是有向.还是无向,只要是加权图,最短路径长度的定义是:起点到终点之间所 ...

  5. 帝国cms 7.5版列表页分页样式修改笔记

    最近在用帝国改版我的个人博客站点,这个也是我第一次尝试用帝国来做博客,之前用过wordpress,每用一个新的程序,都会有些新的收获,也会学到一些新的东西. 在改用帝国之前,我也在网上大概了解了一下, ...

  6. DRF JWT认证(一)

    为什么要使用JWT认证?构成和原理又是什么?怎么还有Base64的事?我都写了

  7. JavaScript学习总结6-apply

    JS中的apply方法可以控制this指向 任何JS支持的类型都可以转化为JSON JS对象是键值对型的,JSON是字符串型的 原型对象:__proto__ JS万物皆对象,ES6开始提供了对后端开发 ...

  8. 比较爽的导航查询 功能 - SqlSugar ORM

    1.导航查询特点 作用:主要处理主对象里面有子对象这种层级关系查询 1.1 无外键开箱就用 其它ORM导航查询 需要 各种配置或者外键,而SqlSugar则开箱就用,无外键,只需配置特性和主键就能使用 ...

  9. JQuery学习基础

    ## 今日内容     1. JQuery 基础:         1. 概念         2. 快速入门         3. JQuery对象和JS对象区别与转换         4. 选择器 ...

  10. node.js -- 身份认证

    请问昨天结束的早是对堆积在了今天吗,今天还来加个班更博,看在这个毅力的份上能否给亿点点推荐. 有个好消息有个坏消息,先说坏消息吧,就是在这么学下去我急需急支糖浆,来回顾回顾前面的知识,这几天学的太急了 ...