目录

DS18B20 搜索算法

以下说明当总线上存在多个 DS18B20 芯片时, 识别各个 DS18B20 的编号并进行通信的算法.

其实这是 1-Wire 总线的搜索算法, 当 1-Wire 总线上挂接了多个设备时, 总线控制端需要通过 ROM Search 命令来判断总线上存在的设备以及获取他们的8字节唯一ROM.

1-WIRE SEARCH ALGORITHM 算法规则和实现机制

ROM搜索算法的核心规则, 是在搜索中重复进行一个简单的三步操作

步骤1: 读一次: 得到一位的值

总控读取1个bit. 这时每个设备都会将ROM当前这一位的bit值放到总线上, 如果这位是0, 就会对总线写0(拉低总线), 如果这位是1, 则会对总线写1, 允许总线保持高电平. 如果两者都存在, 总控读取的是0(低电平).

步骤2: 再读一次: 得到这位的补码

总控继续读一个bit, 这时候每个设备会将ROM当前这一位的bit的补码放到总线上, 如果这位是0就会写1, 如果这位是1则会写0, 如果两者都存在, 总控会读到一个0, 这样总控就会知道存在多个设备, 并且它们的ROM在这一位上的值不同.

步骤3: 写一次: 指定这一位的目标值

总控写入一个bit, 比如写入0, 表示在后面的搜索中选择这一位为0的设备, 屏蔽掉这一位为1的设备

循环

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

总控通过单线总线读取所有设备, 每个设备需要的时间为960 µs + (8 + 3 x 64) 61 µs = 13.16 ms, 识别速度为每秒钟75个设备.

代码逻辑

使用代码实现时, 整体的逻辑是按一个固定的方向(先0后1)深度优先遍历一个二叉树.

数据结构

  • 预设一个8字节数组 Buff 用于记录路径(即ROM的读数)
  • 预设一个8字节数组 Stack, 用于记录每一位的值是否确定, 如果确定就是1, 未确定就是0.
  • 预设一个整数变量 Split_Point 用于记录每一轮搜索中得到的最深分叉点的位置, 下一次到这一位就用1进行分叉.

遍历逻辑

在每一轮遍历中

  1. 从低位开始, 每一位进行两次读, 得到这一位的值和补码
  2. 对前面的结果进行判断
    1. 如果为11, 说明没有设备, 直接退出
    2. 如果为01, 说明这一位都是0, 写入 Buff, 同时将 Stack 这一位设成 1, 表示这一位已确认
    3. 如果为10, 说明这一位都是1, 写入 Buff, 同时将 Stack 这一位设成 1, 表示这一位已确认
    4. 如果为00, 说明这一位产生了分叉, 需要继续判断
  3. 对分叉的判断, 与 Split_Point 记录的值进行比较
    1. 如果当前位置比已知的分叉点更浅, 说明还没到该分叉的位置, 继续设置成 Buff 中上一次使用的值, Stack不变
    2. 如果当前位置等于分叉点, 说明已经到了上次定好的分叉位置, 上次已经用0分叉过了, 这次就用1进行分叉, 这一位就确认了, 将 Stack 这一位设成 1, 表示已确认
    3. 如果当前位置比已知的分叉点位置还要深, 说明发现了新的分叉点(例如用1分叉后, 进入了新的子树, 发现下面还有分叉), 更新 Split_Point 记录分叉点位置, 将 Stack 这一位设成 0 (未确认), 用默认的0继续往下走
  4. 在这轮遍历结束后, Buff 就得到了一个新的地址
  5. 检查 Split_Point 是否需要往上挪: 在 Stack 上找到 Split_Point 标识的位置, 如果值为1, 则将 Split_Point 设置到最浅的一个0的位置. (例如这次正好在分叉点使用1分叉, 当前点确认了, 而之后又全是确认的情况, 需要将分叉点往上移)
  6. 结束条件: 和深度遍历一样, 每一轮遍历后分叉点可能会上下变化, 当分叉点的位置为0时, 说明遍历结束

代码实现

搜索逻辑的C语言代码实现

/**
* buff, stack 和 split_point 都是全局变量, 由外部传入
*
*/
uint8_t DS18B20_Search(uint8_t *buff, uint8_t *stack, uint8_t split_point)
{
uint8_t len = 64, pos = 0;
/* 分叉点的初始值应该用0xFF, 如果输入参数为0, 将其设为0xFF */
split_point = (split_point == 0x00)? 0xFF : split_point;
/* Reset line */
DS18B20_Reset();
/* Start searching */
DS18B20_WriteByte(ONEWIRE_CMD_SEARCHROM); // len 初始值为64, 对 8 字节 ROM 做一个遍历
while (len--)
{
// 两次读, 读取这一位bit值和补码
__BIT pb = DS18B20_ReadBit();
__BIT cb = DS18B20_ReadBit();
if (pb && cb) // 都是1, 表示没有设备
{
return 0;
}
else if (pb) // pb=1, cb=0, 说明这一位为1
{
// 在buff上记录这一位
*(buff + pos / 8) |= 0x01 << (pos % 8);
DS18B20_WriteBit(SET);
// 在stack上将这一位记录为1, 表示已确认
*(stack + pos / 8) |= 0x01 << (pos % 8);
}
else if (cb) // pb=0, cb=1, 说明这一位为0
{
// 在buff上记录这一位
*(buff + pos / 8) &= ~(0x01 << (pos % 8));
DS18B20_WriteBit(RESET);
// 在stack上将这一位记录为1, 表示已确认
*(stack + pos / 8) |= 0x01 << (pos % 8);
}
else // 出现分叉点
{
if (split_point == 0xFF || pos > split_point)
{
// 比上次记录的点更深, 出现了新的分叉点
*(buff + pos / 8) &= ~(0x01 << (pos % 8));
DS18B20_WriteBit(RESET);
// 在stack上将这一位记录为0, 表示未确认
*(stack + pos / 8) &= ~(0x01 << (pos % 8));
// 记录新的分叉点位置
split_point = pos;
}
else if (pos == split_point)
{
// 到达了上次记录的分叉点位置, 这次使用1继续往下走
*(buff + pos / 8) |= 0x01 << (pos % 8);
DS18B20_WriteBit(SET);
// 在stack上将这一位记录为1, 表示已确认
*(stack + pos / 8) |= 0x01 << (pos % 8);
}
else
{
// 这个分叉点处于中间位置, 还没到处理时间, 继续使用上次记录的值
DS18B20_WriteBit(*(buff + pos / 8) >> (pos % 8) & 0x01);
}
}
pos++;
}
// 重新定位分叉点, 将其指向到stack上最后一个未确认的位置
while (split_point > 0 && *(stack + split_point / 8) >> (split_point % 8) & 0x01 == 0x01) split_point--;
return split_point;
}

调用方法

sp = 0;
do
{
// ROM search and store ROM bytes to addr
sp = DS18B20_Detect(addr, Search_Stack, sp);
// Print the new split point and address
UART1_TxHex(sp);
UART1_TxChar(' ');
PrintArray(addr, 0, 8);
UART1_TxString("\r\n");
} while (sp);

运行实测

对一个挂载了19个 DS18B20 的 1-Wire 总线进行实际测试, 用1uF电容和1N4148模拟寄生供电电路, 与上位机只连了两根线.

实际的测试输出如下, 第一列输出的是Split_Point的值, 表示当前的分叉深度, 后半部分是这个DS18B20采样的温度值和CRC

0F 2854FD96F0013C1A........B20155057FA5A5669A CRC:9A ␍␊
0D 28D44496F0013C4C........BD0155057FA5A56660 CRC:60 ␍␊
0B 28744196F0013CC2........B50155057FA5A5664A CRC:4A ␍␊
09 280CCB96F0013C8D........B20155057FA5A5669A CRC:9A ␍␊
0B 28D2A396F0013C75........B50155057FA5A5664A CRC:4A ␍␊
0D 288AFB48F6973CFD.......BE0155057FA581665F CRC:5F ␍␊
0C 28AA8196F0013C37........B40155057FA5A56609 CRC:09 ␍␊
0A 283A9096F0013C37........B80155057FA5A56636 CRC:36 ␍␊
08 283E5996F0013C3A........B80155057FA5A56636 CRC:36 ␍␊
0B 2811E896F0013C2A........B70155057FA5816636 CRC:36 ␍␊
0C 28C90196F0013C66........B40155057FA5A56609 CRC:09 ␍␊
0D 28597196F0013CBA........B80155057FA5A56636 CRC:36 ␍␊
0A 28794648F65D3C26........B60155057FA5A5668F CRC:8F ␍␊
0B 2865BB96F0013CB5........BD0155057FA5A56660 CRC:60 ␍␊
0C 28ADCB96F0013CE6........BA0155057FA581664A CRC:4A ␍␊
09 281D1648F64B3CEA.......BD0155057FA5A56660 CRC:60 ␍␊
0B 2843E896F0013C6A........BB0155057FA5A566F3 CRC:F3 ␍␊
0A 289B0896F0013CD5........B70155057FA5816636 CRC:36 ␍␊
00 28EF5C96F0013C1B........BE0155057FA5A566A5 CRC:A5 ␍␊

参考

DS18B20数字温度计 (三) 1-WIRE总线 ROM搜索算法和实际测试的更多相关文章

  1. DS18B20数字温度计 (一) 电气特性, 供电和接线方式

    目录 DS18B20数字温度计 (一) 电气特性, 供电和接线方式 DS18B20数字温度计 (二) 测温, ROM和CRC校验 DS18B20数字温度计 (三) 1-WIRE总线ROM搜索算法 DS ...

  2. STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计

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

  3. 【python】题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

    # encoding:utf-8 # p001_1234threeNums.py def threeNums(): '''题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多 ...

  4. php实现数字格式化,数字每三位加逗号的功能函数

    原地址:http://www.jb51.net/article/73781.htm php实现数字格式化,数字每三位加逗号的功能函数,具体代码如下: ? 1 2 3 4 5 6 7 8 9 10 11 ...

  5. JavaScript进阶(四)js字符串转换成数字的三种方法

    js字符串转换成数字的三种方法 在js读取文本框或者其它表单数据的时候获得的值是字符串类型的,例如两个文本框a和b,如果获得a的value值为11,b的value值为9 ,那么a.value要小于b. ...

  6. Android ROM开发(三)——精简官方ROM并且内置ROOT权限,开启Romer之路

    Android ROM开发(三)--精简官方ROM并且内置ROOT权限,开启Romer之路 相信ROM的相关信息大家通过前几篇的学习都是有所了解了,这里就不在一一提示了,这里我们下载一个官方包,我们还 ...

  7. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

  8. python练习实例1--------给定数字组成三位数

    题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? 来看第一种解法 num = [1, 2, 3, 4] """ 根据题中'互不相同' ...

  9. js字符串转换为数字的三种方法。(转换函数)(强制类型转换)(利用js变量弱类型转换)

    js字符串转换为数字的三种方法.(转换函数)(强制类型转换)(利用js变量弱类型转换) 一.总结 js字符串转换为数字的三种方法(parseInt("1234blue"))(Num ...

随机推荐

  1. Linux磁盘之inode

    什么是 inode ? 文件储存在硬盘上,硬盘的最小存储单位叫作"扇区"(Sector).每一个扇区储存512字节(至关于0.5KB).操作系统读取硬盘的时候,不会一个个扇区地读取 ...

  2. mysql4与mysql5的区别_MySQL 4.1/5.0/5.1/5.5/5.6各版本的主要区别

    MySQL 4.1/5.0/5.1/5.5/5.6各版本的主要区别 一.5.0 增加了Stored procedures.Views.Cursors.Triggers.XA transactions的 ...

  3. DFS与N皇后问题

    DFS与N皇后问题 DFS 什么是DFS DFS是指深度优先遍历也叫深度优先搜索. 它是一种用来遍历或搜索树和图数据结构的算法 注:关于树的一些知识可以去看<树的概念及基本术语>这篇文章 ...

  4. ASP.NET Core的几种服务器类型[共6篇]

    作为ASP.NET CORE请求处理管道的"龙头"的服务器负责监听和接收请求并最终完成对请求的响应.它将原始的请求上下文描述为相应的特性(Feature),并以此将HttpCont ...

  5. Kubernetes部署单元-Pod

    在 k8s 搞出 pod 概念的时候,其实 docker 官方就已经推出自己的容器编排应用 swarm.这一套服务可以帮助在不同节点上的容器,进行统一的管理,主要针对容器的启停,运维,还有部署,注意我 ...

  6. SpringMVC踩坑2

    Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exc ...

  7. HCIE笔记-第六节-CIDR与ICMP

    项目部 58人 地址:194.2.3.128 /26 研发部 100人 地址: 194.2.3.0/25 市场部 27人 地址: 194.2.3.192/27 财务部 15人 地址:194.2.3.2 ...

  8. Python 一网打尽<排序算法>之堆排序算法中的树

    本文从树数据结构说到二叉堆数据结构,再使用二叉堆的有序性对无序数列排序. 1. 树 树是最基本的数据结构,可以用树映射现实世界中一对多的群体关系.如公司的组织结构.网页中标签之间的关系.操作系统中文件 ...

  9. git冲突解决、线上分支合并、luffy项目后台登陆注册页面分析引入

    今日内容概要 git冲突解决 线上分支合并 登陆注册页面(引入) 手机号是否存在接口 腾讯云短信申请 内容详细 1.git冲突解决 1.1 多人在同一分支开发,出现冲突 # 先将前端项目也做上传到 g ...

  10. 《图解UE4渲染体系》Part 0 引擎基础

    在介绍UE4渲染体系前,我们有必要来先看一下UE4是用什么样的方式来构建游戏场景数据的. 1 Object 在UE4中当我们说Object,通常是指代引擎代码中的UObject类,它是引擎里管理绝大部 ...