DS18B20数字温度计 (三) 1-WIRE总线 ROM搜索算法和实际测试
目录
- DS18B20数字温度计 (一) 电气特性, 寄生供电模式和远距离接线
- DS18B20数字温度计 (二) 测温, ROM和CRC算法
- DS18B20数字温度计 (三) 1-WIRE总线 ROM搜索算法和实际测试
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...
示例搜索过程
搜索步骤如下
- 单线总线控制端(以下简称总控)执行 RESET, 所有的 DS18B20设备(以下简称设备)响应这个RESET
- 总控执行 Search ROM 命令
- 总控读取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 总线上没有设备
- 总控写入一个bit, 比如写入0, 表示在后面的搜索中屏蔽 ROM2 和 ROM3, 仅留下 ROM1 和 ROM4
- 总控再执行两次读操作, 读到的值为0,1, 这表示总线上所有设备在这一位上的值都是0
- 总控写入一个bit, 因为值是确定的, 这次写入的是0
- 总控再执行两次读操作, 读到的值为0,0, 这表示总线上还有多个设备, 在这一位上的值不同
- 总控写入一个bit, 这次写入0, 这将屏蔽 ROM1, 仅留下 ROM4
- 总控重复进行三步操作, 读出 ROM4 剩余的位, 完成第一次搜索
- 总控再次重复之前的搜索直到第7位
- 总控写入一个bit, 这次写入1, 将屏蔽 ROM4, 仅保留 ROM1
- 总控通过重复三步操作, 读出 ROM1 剩余的位
- 总控再次重复之前的搜索直到第3位
- 总控写入一个bit, 这次写入1, 将屏蔽 ROM1 和 ROM4 仅保留 ROM2 和 ROM3
- 重复之前的逻辑, 当所有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进行分叉.
遍历逻辑
在每一轮遍历中
- 从低位开始, 每一位进行两次读, 得到这一位的值和补码
- 对前面的结果进行判断
- 如果为11, 说明没有设备, 直接退出
- 如果为01, 说明这一位都是0, 写入 Buff, 同时将 Stack 这一位设成 1, 表示这一位已确认
- 如果为10, 说明这一位都是1, 写入 Buff, 同时将 Stack 这一位设成 1, 表示这一位已确认
- 如果为00, 说明这一位产生了分叉, 需要继续判断
- 对分叉的判断, 与 Split_Point 记录的值进行比较
- 如果当前位置比已知的分叉点更浅, 说明还没到该分叉的位置, 继续设置成 Buff 中上一次使用的值, Stack不变
- 如果当前位置等于分叉点, 说明已经到了上次定好的分叉位置, 上次已经用0分叉过了, 这次就用1进行分叉, 这一位就确认了, 将 Stack 这一位设成 1, 表示已确认
- 如果当前位置比已知的分叉点位置还要深, 说明发现了新的分叉点(例如用1分叉后, 进入了新的子树, 发现下面还有分叉), 更新 Split_Point 记录分叉点位置, 将 Stack 这一位设成 0 (未确认), 用默认的0继续往下走
- 在这轮遍历结束后, Buff 就得到了一个新的地址
- 检查 Split_Point 是否需要往上挪: 在 Stack 上找到 Split_Point 标识的位置, 如果值为1, 则将 Split_Point 设置到最浅的一个0的位置. (例如这次正好在分叉点使用1分叉, 当前点确认了, 而之后又全是确认的情况, 需要将分叉点往上移)
- 结束条件: 和深度遍历一样, 每一轮遍历后分叉点可能会上下变化, 当分叉点的位置为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 ␍␊
参考
- 单线总线搜索算法 1-WIRE SEARCH ALGORITHM https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/187.html
DS18B20数字温度计 (三) 1-WIRE总线 ROM搜索算法和实际测试的更多相关文章
- DS18B20数字温度计 (一) 电气特性, 供电和接线方式
目录 DS18B20数字温度计 (一) 电气特性, 供电和接线方式 DS18B20数字温度计 (二) 测温, ROM和CRC校验 DS18B20数字温度计 (三) 1-WIRE总线ROM搜索算法 DS ...
- STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- 【python】题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
# encoding:utf-8 # p001_1234threeNums.py def threeNums(): '''题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多 ...
- php实现数字格式化,数字每三位加逗号的功能函数
原地址:http://www.jb51.net/article/73781.htm php实现数字格式化,数字每三位加逗号的功能函数,具体代码如下: ? 1 2 3 4 5 6 7 8 9 10 11 ...
- JavaScript进阶(四)js字符串转换成数字的三种方法
js字符串转换成数字的三种方法 在js读取文本框或者其它表单数据的时候获得的值是字符串类型的,例如两个文本框a和b,如果获得a的value值为11,b的value值为9 ,那么a.value要小于b. ...
- Android ROM开发(三)——精简官方ROM并且内置ROOT权限,开启Romer之路
Android ROM开发(三)--精简官方ROM并且内置ROOT权限,开启Romer之路 相信ROM的相关信息大家通过前几篇的学习都是有所了解了,这里就不在一一提示了,这里我们下载一个官方包,我们还 ...
- Java基于opencv实现图像数字识别(三)—灰度化和二值化
Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...
- python练习实例1--------给定数字组成三位数
题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? 来看第一种解法 num = [1, 2, 3, 4] """ 根据题中'互不相同' ...
- js字符串转换为数字的三种方法。(转换函数)(强制类型转换)(利用js变量弱类型转换)
js字符串转换为数字的三种方法.(转换函数)(强制类型转换)(利用js变量弱类型转换) 一.总结 js字符串转换为数字的三种方法(parseInt("1234blue"))(Num ...
随机推荐
- Blazor组件自做三 : 使用JS隔离封装ZXing扫码
Blazor组件自做三 : 使用JS隔离封装ZXing扫码 本文基础步骤参考前两篇文章 Blazor组件自做一 : 使用JS隔离封装viewerjs库 Blazor组件自做二 : 使用JS隔离制作手写 ...
- Masa Blazor in Blazor Day
2022年第一场Blazor中文社区的开发者分享活动,我们的团队也全程参与其中,在议程中,也分享了我们团队的Blazor 管理后台模板,针对于Blazor,先科普一波,避免有些朋友不了解,Blazor ...
- ethool的使用
ethtool命令 网络配置 ethtool命令用于获取以太网卡的配置信息,或者修改这些配置.这个命令比较复杂,功能特别多 语法 ethtool [ -a | -c | -g | -i | -d | ...
- 二进制免安装方式,配置mysql
mysql 5.7.22版本 二进制包安装方法 环境标准化采样: 检查系统内是否有其他mysqlrpm -qa | grep mysql 是否存在mysql用户和用户组grep mysql /etc/ ...
- pip:带你认识一个 Python 开发工作流程中的重要工具
摘要:许多Python项目使用pip包管理器来管理它们的依赖项.它包含在Python安装程序中,是Python中依赖项管理的重要工具. 本文分享自华为云社区<使用Python的pip管理项目的依 ...
- 微信小程序wx.login先执行onLaunch与onLoad加载顺序问题
@ 目录 遇到问题 请求api返回需要先登录,实际上登录已成功 问题分析 解决问题 自定义回调函数 app.js index.js 扩展提问 学习交流 随机数字随机幸运数+ My Blog 技术交流 ...
- Failed to load resource: the server responded with a status of 404 ()
今天遇到了一个一开始感觉很莫名其妙的报错 在编写页面的时候把原先写在html页面里的js代码单独拿出来做成一个JavaScriptUtil文件,放在了和html页面同一个目录下.运行之后在对应的页面c ...
- 二次封装这几个 element-ui 组件后,大大减少了我 CRUD 的时间
element-ui 因其组件丰富.可拓展性强.文档详细等优点成为 Vue 最火的第三方 UI 框架.element-ui 其本身就针对后台系统设计了很多实用的组件,基本上满足了平时的开发需求. 既然 ...
- 大一/初学者学C语言前必看!!!(建议收藏)
目录 数据类型 常量.变量 数组 字符串.转义字符 选择语句 循环语句 函数 操作符 结构体 指针 神秘的学习资料基地jq.qq.com/?_wv=1027&k=5kWJsY1z 一.数据类 ...
- [AcWing 796] 子矩阵的和
点击查看代码 #include<iostream> using namespace std; const int N = 1e3 + 10; int a[N][N], s[N][N]; i ...