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 ...
随机推荐
- mysql-cluster-gpl-7.5.10-linux-glibc2.12-x86_64.tar.gz (有必要解释一下)
大部分软件我们接触的时候会发现,起的名称有点怪异,所以我觉得有必要解释一下. 比如: mysql-cluster-gpl-7.5.10-linux-glibc2.12-x86_64.tar.gz 名称 ...
- SpringCloud分布式尝试记录
服务提供端: 客户消费端:
- 新手入门C语言第九章:C函数
C 函数 函数是一组一起执行一个任务的语句.每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数.您可以把代码划分到不同的函数中.如何划分代码到不同的函数中 ...
- 计算机系统5-> 计组与体系结构2 | MIPS指令集(上)| 指令系统
系列的上一篇计算机系统4-> 计组与体系结构1 | 基础概念与系统评估,学习了一些计算机的基础概念,将一些基本的计算机组成部分的功能和相互联系了解了一下,其中很重要的一个抽象思想就是软硬件的接口 ...
- windwos 下编译 qsqlibase 驱动(firebird 和 interbase)
编译环境:mingw-w64 使用qtcreator打开ibase.pro,ibase.pro位置例如:R:\qt-everywhere-opensource-src-4.8.5\src\plugin ...
- 【职场必备】6个免费良心网站&职场办公网站(收藏血赚)
1.随机自动生成头像的网站:https://www.tool22.com/Tools-SJTX.html2.迅捷PDF转换器:https://app.xunjiepdf.com/3.全网音乐下载:① ...
- 【面试普通人VS高手系列】b树和b+树的理解
数据结构与算法问题,困扰了无数的小伙伴. 很多小伙伴对数据结构与算法的认知有一个误区,认为工作中没有用到,为什么面试要问,问了能解决实际问题? 图灵奖获得者: Niklaus Wirth 说过: 程序 ...
- Linux:文件解压、复制和移动的若干坑
Linux下进行文件的解压.复制.移动应该是最常见的操作了.尤其是我们在项目中使用大量的数据集文件(比如机器学习)时.然而使用这些命令时一不留神就会掉进坑里,这篇文章我们就来细数用Shell进行文件操 ...
- 5. VIM编辑器
Vim编辑器中设置了3种模式-命令模式.末行模式和编辑模式 命令模式:控制光标移动,可对文本进行复制.粘贴.删除和查找等工作. 输入模式:正常的文本录入. 末行模式:保存或退出文档,以及设置编辑环境. ...
- 如何使用 pyqt 实现 Groove 音乐播放器
前言 Win10 自带了一款很好用的音乐播放器 -- Groove 音乐,但是只能支持本地音乐的播放.2020年3月底到4月底,自己一直在学习 PyQt5,然后从 2020年5月开始,着手使用 PyQ ...