DS18B20 ROM码的搜索算法

美信公司(http://www.maximintegrated.com/cn)生产了许多1-Wire®器件产品,硬件电路极致简单,而相应软件就显得复杂。美信网站的《应用笔记187》介绍了单线ROM搜索算法,并提供了TMEX API测试程序的源代码,该算法较为复杂,而且是通用于多平台(windows\JAVA等)的API,无法直接在KeilC上调试并写入单片机。本人在学习理解其算法后,适当修改源代码,并作上了详细的注释和图解,代码在KeilC环境下调试通过,并在一个挂接4个DS18B20的小型1-Wire环境中测试成功,顺利获取4个ROM码。现把算法分析及测试过程成文如下,其中算法部分保留了大部分AN187的内容,但作了许多修改和补充,原文可参见http://www.maximintegrated.com/cn/app-notes/index.mvp/id/187。本文介绍的搜索算法及源代码,对任何现有的或将要推出的1-Wire器件都是有效的。

Maxim的1-Wire®器件都带有一个64位的唯一注册码,生产时通过激光刻蚀在内部只读存储器内(ROM),用于在1-Wire网络中通过1-Wire主机对其寻址。如果1-Wire网络中从机器件数量不定且ROM码未知,则可采用搜索算法查找这些码。下表是64Bit ROM码的组成示意:
(MSB表示Most Significant Bit最高有效位,LSB表示Least Significant Bit最低有效位)       

MSB                        64位ROM码                          LSB

8位CRC

MSB              LSB

48位序列号

MSB              LSB

8位家庭码

MSB               LSB

搜索顺序是从低位到高位的,如下:

第64位←——————————————————————第1位

扫描二维码关注公众号,回复: 10144020 查看本文章

图5. 64位唯一的ROM注册码

3  1-Wire搜索协议

搜索算法采用的是二叉树型结构,搜索过程沿各分节点进行,直到找到器件的ROM码即叶子为止;后续的搜索操作沿着节点上的其它路径进行,按照同样的方式直到找到总线上的所有器件代码。本文后面提供了一个简单示例:4个ROM,每个仅取4位,构造了一棵二叉树,并在此示例上推导出搜索算法的许多细节及实现。

搜索算法首先通过复位(reset)和在线应答脉冲(presence pulse)时隙将1-Wire总线上的所有器件复位;成功地执行该操作后,发送1 个字节的搜索命令;搜索命令使1-Wire器件准备就绪、开始进行搜索操作。

搜索命令分为两类:标准搜索命令(F0 hex)用来搜索连接到网络中所有器件;报警或有条件搜索命令(EC hex)只用来搜索那些处于报警状态下的器件,这种方式缩小了搜索范围,可以快速查找到所需要注意的器件。本文附带代码仅使用了标准搜索命令。

搜索命令发出之后,开始实际的搜索过程。首先总线上的所有从机器件同时发送ROM码(也叫注册码)中的第一位(最低有效位) (参见1)。与所有的1-Wire通信一样,无论是读取数据还是向从机器件写数据,都由1-Wire主机启动每一位操作。按照1-Wire协议设置特性,当所有从机器件同时应答主机时,结果相当于全部发送数据位的逻辑AND;从机发送其ROM码的第一位后,主机启动下一位操作、接着从机发送第一位数据的补码;从两次读到的数据位可以对ROM码的第一位做出几种判断(参见下表)。上述二次读取1-Wire总线上当前位ROM码的正码和反码的操作,本文简述为“二读”

表1. 检索信息位

主机读一位

从机提供当前位正码

主机再读一位

从机提供该位反码

二读后可以判断获得的信息

注意:主线读到的是各个响应从机的“线与”逻辑

0

0

连接器件的该位有0有1,这是一个差异位(混码位)

0

1

连接器件的该位均为0

1

0

连接器件的该位均为1

1

1

没有器件与总线相连

按照搜索算法的要求,“二读”后1-Wire主机必须向总线上的从机发回一个指定位,称为“一写”;如果从机器件中ROM码的当前位的值与该数据位匹配,则继续参与搜索过程;若从机器件的当前位与之不匹配,则该器件转换到休眠状态,直到下一个1-Wire复位信号到来被再次唤醒。其余63位ROM码的搜索依然按照这种‘读两位’、‘写一位’的模式进行重复操作。

按照这种搜索算法进行下去,最终除了一个从机器件外所有从机将进入等待状态,经过一轮检测,就可得到最后保留(未进入等待状态)器件的ROM码。在后续搜索过程中,选用不同的路径(或分支)来查找其它器件的ROM码。需要注意的是本文ROM码的数据位用第1位(最低有效位)到第64 位(最高有效位)表示,而不是第0位到第63位的模式;这样设置允许将差异位置计数器初始值置为0,为以后的比较提供了方便。

上述过程可简述为“二读一写”,“二读”结果为“00”,主机就判定这一位是“差异位”,也可称为“冲突位”“混码位”。举例来说,“二读”就相当于“老师问名”,设想学生的名字就是64位ROM码,老师第1问时学生反应为:“1的不吭声,0就高喊到”;第2问时学生反过来:“0的不吭声,1的高喊到”,老师通过二次答复就可分辨:“不吭声+不吭声”表示一个学生也没有;“不吭声+到”表示大家都是1,“到+不吭声”表示大家都是0;而“到+到”收表示有0有1。试想,如果有0有1,只要有1个喊到,老师就能听到,这种合成效果就是所谓的“线与”,当然这里应称为“声与”。

搜索到“差异位”时,主机如何确定搜索方向(0还是1),这是本算法的核心问题,这也是典型的“二叉树遍历”算法。或许有人会提出一种“笨”算法,就是穷举64位组合,从000…000到111…111遍历,有应答的从机就会被一个一个找出来,这确实是一种简单的“笨”算法,在许多地方很有用,但对于64位ROM来说,假设一位的访问需要100us,64位则需要6.4ms,穷举全部ROM码有264=1844亿亿,极端的遍历时间为37亿年,这确实是一种无法实现的笨办法。


下表列出了1-Wire主机和从机的搜索过程

Master

Slave

发送“复位”信号

产生“存在”脉冲

发送“搜索”命令

全体器件进入“应付搜索”状态,不断地根据主机信号作出反应

从总线读取1位“线与”

全体器件发送64位ROM码的第1位

从总线读取1位“线与”

全体器件发送64位ROM码的第1位的反码

向总线写1位(按算法)

器件们接收此位后,凡与ROM码的第1位不符的器件进入休眠状态

读64位ROM码后续位

器件发后续位ROM码

读64位ROM码后续位的反码

器件发后续位ROM码的反码

写后续各位

器件们接收此位后,凡与ROM码的当前位不符的器件进入休眠状态.

从上表可以看出:如果所有总线上的器件在当前位具有相同值,那么只有一条分支路径可选;总线上没有器件响应的情况是一种异常状态,可能是要查找的器件在搜寻过程中与1-Wire总线脱离。如果出现这种情况,应中止搜索,并发出1-Wire复位信号起始新的搜索过程。如果当前位既有0也有1,这种情况称为位值差异,它对在后续搜索过程中查找器件起关键作用。搜索算法指定在第一轮查询中若出现差异(数据位/补码 = 0/0),则选用‘0’路径。注意:这一点是由本文档中介绍的特定算法决定的,其它算法中或许首先选用‘1’路径。算法需要记录最后一次值差异的位置以供下一次搜索使用。

本文介绍的搜索算法是一个通用的程序,Maxim工程师极巧妙地设计了一些变量,只要在搜索前设置这些变量的初始值,程序就能完成不同的搜索功能,下文中会列出这些变量,但理解其奥秘需要花点时间。比如程序对最初8位ROM中出现的最后一次位差异设置了一个专门的变量保持跟踪,因为64位注册码的前8位是家族码,利用此变量可以在器件的搜索过程中可以按照其家族码进行分类。运用该变量可以有选择性地跳过1-Wire器件的整个分组。如需进行选择性的搜索,可参考关于高级变量搜索的详细解释。

64位ROM码中还包含8位循环冗余校验码(CRC);CRC值用于验证是否搜索到正确的ROM码。ROM码的排列如图1所示。关于CRC校验的原理,内容丰富到需要专文论述,这里不详述,可参见Maxim网站相关笔记。
 

(未完待续)

1-Wire搜索算法详解(2)

4 实例及算法分析

要理解算法,或制定算法,我们需要通过一个实例来解释:

        ROM示例(仅列出前4位)

ROM编号

1234……

ROM1

0011……

ROM2

1010……

ROM3

1111……

ROM4

0001……

   DS18B20 <wbr>1-WIRE <wbr>ROM搜索算法详解(2)
                                                       图8、4个ROM的实例

假设1-Wire挂有4个ROM如上表, 这里ROM码仅设4位,编号从1到4,而实际器件ROM位序号从1到64。右图则是按“先0后1”遍历顺序所构造的二叉树,可见第一次遍历得到ROM4:0001,第二次遍历得到ROM1:0011,依次类推。图中打有⊕的位置就是分叉点,即差异位(或称为混码位)。

现在根据上例观察各次遍历,提炼出算法及实现细节:

1.第一次遍历时,第1位Bit1时遇到第一个差异位,按左序遍历即先0后1的顺序,我们选择0(称为方向0);Bit2不是差异位,算法根据“二读”后方便地选0;Bit3又是差异位,再选择方向0,Bit4也不是差异位。遍历结果得到ROM4。这里可得出结论1:凡遇到新的差异位,选择0

2.第二次遍历,按实例分析,Bit1处再选择方向0;Bit2取0;Bit3不能再选0了,改选1;Bit4取1;得到ROM1。问题随即产生:问题1、Bit1为何走0?此处确为差异位,但已不是首次经过,不能套用结论1。问题2、Bit3处为何改走1?简单分析问题2可得出结论2:凡上次遍历时最后一个走0的差异位本次应走1。很明显,“上次遍历时最后一个差异位”下的左分支已经走过,这次应该往右走了。再回答问题1可以得到结论3:凡上次遍历时最后一个走0的差异位之前的差异位仍按上次遍历的老路走。这个结论比较拗口,观察实例来解释:Bit3是差异位,右分支还没走过,现在我要走Bit3下边的右分支,Bit3之前当然是按上次路线来走了。

3.然后是否就可以进行第三次遍历了呢?且慢,有一个重要的问题还没处理。第二次遍历前,那个“上次遍历时最后一个走0的差异位”应该Bit3,我们先设置一个变量LastDiscripancy来记住这个值,即LastDiscripancy=3;从前面的分析我们知道,这个变量的值是决定本次遍历时算法在哪个位置改道的依据,而且这个变量在本次遍历全过程中应该保持不变的,如果在该节点前或后有任意多少的差异位,均按结论1(新差异位)和结论3(老差异位)来处理。也可以理解成结论2在一次遍历中必须适用且仅适用一次,这也就是每次遍历都能找出一个新的ROM的核心所在。那么第二次遍历后,变量LastDiscripancy应该指向哪个节点呢?直接观察就可发现应该指向Bit1。

4.那么算法中如何实现每次遍历后变量LastDiscripancy的更新呢?要知道这个指针随着每次遍历在或上或下地移动,如第一次遍历后指向Bit3,第二次后指向Bit1,第三次后指向Bit2。还有,第一次开始遍历前、第四次遍历后该变量又指向哪里呢?由于实际ROM多达64位,从机如果数据多的话,差异位也会随之增加,所以算法再引入一个变量Last_Zero,在每次遍历前设为0,即指向起始位置前,然后遍历ROM码的1-64位时,该变量始终指向最后一个走0的差异位,等到遍历完成后,将该变量的值赋给LastDiscripancy即可。这样我们再观察实例的第二次遍历,Last_Zero遍历前为0,Bit1时,符合“走0的差异位”,于是Last_Zero=1,后续Bit3尽管是差异位,但不走0,所以遍历完成后,该值还是1,最后交给LastDiscripancy,自己又指向0,为第三次遍历作好准备。

5.第三次遍历就变得顺理成章:LastDiscripancy=1,Bit1处需运用结论2,算法改走1。而且第三次遍历让Bit2成为了“最后一个走0的差异位”,第四次遍历就在此处改走1了。第四次遍历搜索到最后一个rom,但还得告诉程序结束信号,从而退出搜索。结束信号用什么判定?4个ROM循环4次?当然不是,上述表图只是举例,实际总路线上挂接多少从机是未知的。答案还是利用变量LastDiscripancy来判定,开始搜索前自然有LastDiscripancy=0,但一旦进入搜索程式,每次遍历后该变量总是指向ROM码中间可能的某一位(1-64),直至遍历到最后一个从机,这时必定是所有差异位均走1,如果Last_Zero保持为初始值0,遍历过程中未作修改,遍历完成后LastDiscripancy=Last_Zero=0。以此为条件可判定搜索全部完成。

6.综上,当算法执行到一个差异位时,需判断区分为三种情况:首次遇到型、上次最后走0型、上次非最后走0型。可以通过二个变量的比较来表征这三种情况,即比较当前搜索的位id_bit_number和上次最后走0的位LastDiscrepancy 的大小来判别,如下表:

表3. 搜索路径方向的确定

三种情况

当前搜索位 vs 最后走0差异位

路径(变量:search_direction)

结论2:上轮最后差异位

Id_bit_number=LastDiscripancy

选1

结论3:非最后差异位

Id_bit_number < LastDiscripancy

同上次 (来自存储的最近ROM码)

结论1:新遇差异位

Id_bit_number > LastDiscripancy

选0

其中变量Id_bit_number表示当前搜索位,每一次遍历时从1至64步进;

变量search_direction表示当前节点经过二读及判断后确定的搜索方向,是0还是1;

其他变量说明请参见流程图中附文。



当单线总线上挂有多个DS18B20时,系统对总线上器件的数量和每个器件ROM码的识别是通过DS18820的搜索ROM命令与算法配合来实现的。

  1.ROM搜索原理
  
  根据单线总线协议,当主机发出搜索ROM命令后,从机应答时从64位ROM码的最低位开始,先发送原码,然后发送该位的补码,之后主机写入1位数据,ROM码最低位与此数据相同的DS18B20继续应答,反之则不再应答,如此循环“读2位、写1位”的过程,直至读到一个完整ROM码为止,然后复位总线,进行下一次搜索。下面对读、写进行具体分析。

  (1)读2位
  
  由于主机发出搜索命令后,所有从机都会将自己ROM码的第一位(最低位)的原码和补码放到总线上,那么就有下面四种可能的情况:

  若所有器件第一位都为0:那么所有器件放到总线上的原码都为0,补码都为1,读2位得到为“01”;若所有器件第一位都为1:同上可知,读到的值为“10”;若器件中有些为1,而有些为0:那么,放原码和补码时,都会有器件放0到总线上,根据“线与”

  逻辑可知读到的结果为“00”;总线上无器件:此情况下,没有器件拉低总线,读到为“11”。

  (2)写1位
  
  主机写1位的目的是为了排除和定位,具体要写入什么数据需要根据读2位的结果而定:若读到为“11”,表明无器件则结束搜索过程,无需再写入;若读到为“01”或“10”,表明所有器件此位相同,则应对应写入“0”或“1”,继续读下一位;若读到为“00”,表明总线上的器件在该位上数据发生冲突,此时,写入1位数据具有“排除”的作用,如果器件ROM编码在该位上的数据与写入的数据相同,则继续保持与总线的联系,如果不同则此器件在本次搜索中从总线上“排除”,不再响应主机发布的命令,直到主机进行下一次复位。

  2.ROM搜索策略
  
  根据上述ROM搜索原理的描述,可得到如下图所示的树形搜索策略图,其中圆形节点内所示即为对应位的ROM码。

  由图可知,只有遇到读两位为“00”才会出现分叉,这也是搜索的关键所在。每一次搜索由根(即开始节点)沿树的一条路径进行,直到叶子(最后一位ROM码),读回一个ROM码,为了确保每次搜索所选路径不同,必须对所经过的分又进行记录。其具体流程规则为:

  (1)复位总线,发送ROM搜索命令;(2)按读两位、写一位的方式进行,寻找“00”读码。在读到“00”之前若为“10”或“01”,直接记录对应位ROM码,并写入该码;若为“11”退出搜索。

  (3)第一次搜索中读到“00”时,先全部写“0”,选择ROM码为“0”的路径,将最高“00”读码(即图中最接近叶子)位置记录并保存下来为“最高00位”。

  (4)之后每次搜索中,遇到“00”读码后,比较此位与“最高00位”的位置,若在“最高00位”之前,则一律写上一次搜索在此位置写入的值;若位置相同,则写“1”;若在之后则写“0”。

  在每一次搜索完成后,要将“最高00位”更新为本次搜索中写“0”的最高“00”读码位置,这也是最为重要的一步。

  (5)当“最高00位”更新为0,即已到树的根部,则表明已搜索完全部ROM码,过程结束。

  3.ROM搜索实例
  
  假设某一总线上挂有5个DS18820,且其ROM码分别为:xxx000、xxx001、xxx010、xxxl00和xxxl01。仅以前三位码字为例,则有如下图所示搜索树,其中共有4个分叉(即“00”读码冲突)。

  根据前述算法可知其过程为:

  (1)第一次搜索中,遇到“叉1”、“叉2”和“叉3”时均直接写“0”,从而得到ROM码“000”,并且“最高00位”记为3,即“又3”位置;
  
  (2)第二次搜索时,“叉1”和“叉2”位置在“最高00位”之前,仍然写上次写的值“0”,而“又3”处,位置相同,写入“1”,同时要将“最高00位”更新为2,即“叉2”位置;本次得到ROM码为“001”;
  
  (3)第三次搜索,到“叉2”处写“1”,并将“最高00位”更新为1,写的到ROM码“010":

  (4)第四次搜索,在“叉1”处,应写“1”,但应注意的是,此时并不更新“最高00位”,要到本次搜索结束才进行更新。继续搜索,到“叉4”处,在“最高00位”之后,写“0”。本次搜索得到ROM码“100”,同时“最高00位”需更新为3,即“叉4”位置。

  (5)第五次搜索,“叉1”在“最高00位”之前,仍然写上次写的值“1”,“叉4”也应写“1”,搜索结束得到ROM码“101”,而且本次搜索中没有写“0”的分叉(“00”位),因此“最高00位”更新为0,搜索结束,5个器件的ROM码已全部得到。

发布了136 篇原创文章 · 获赞 71 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/u012308586/article/details/104782647