zoukankan      html  css  js  c++  java
  • (原创)巩固理解基于DS18B20的1-wire协议(MCU,经验)

    1.Abstract

        如前篇随笔所写,将以前遇到最难懂的两个部分重拾一下。前一篇写的是I2C协议(http://www.cnblogs.com/hechengfei/p/4117840.html),这一篇就来写基于DS18B20的1-wire协议。以前用到它的时候是借助别人写的文章和配套程序,整了一个一知半解;现在重新学习一遍,我想参考的资料就只有唯一一份了——芯片手册,最为权威和齐全的。在平静下来写这个的时候,着实花了很大精力将手册从头看到尾,部分的时序图在草稿纸上画了画,理解也更加深刻了一些。

        总线的结构都是差不多的,相对于上篇中的I2C总线,1-wire总线结构从逻辑上来说还简单一点,要说复杂的地方,那就是比较严格的时序了——规定的时间内必须完成某事,否则容易出岔子。关于1-wire总线的优劣势,在仔细分析完一个具体的器件以后,再来做结论比较适合,下面开始正文。

    2.Content

        挂在1-wire总线上的,一般都只有一个主器件,一个或者多个从器件;而且通常情况下单个器件的情况居多。构成主器件的,都是微控制器或者可编程逻辑器件(CPLD/FPGA),从器件就是各个厂商生产的具体功能芯片了(尤其是DALLAS Semiconductor 公司)。下面以一个具体的实例为例,分析下它是如何工作的。器件使用典型的DS18B20温度传感器,在1-wire总线结构中,它只能作从器件。

      2.1 协议分析

        和分析I2C的协议一样,把挂在总线上的器件分为两种;一种是主器件,它具有控制总线的权利,主动与从器件打交道;另外一种是从器件,它受主器件的指挥;一般都是站在主器件的角度去看总线(因为从器件是受控对象,它的功能已经实现好了,只需要给它特定的指令,它就会自动地去做某件事儿)。

        和其他总线通信一样,主器件需要知道从器件的一些基本信息——器件地址和器件内部的控制信息。对于DS18B20来说,解决它的器件地址还真不是一件容易的事儿,看看它的地址信息是怎么分配的。

    image FIG2.1 DS18B20内部ROM信息

        DS18B20的内部ROM有64位,分为8个字节(8 * 8 = 64bit);第一个字节是器件家族的固定编码,DS18B20为28H;中间的6个字节48位为器件的序列号,也就是器件地址(最为关注的就是它了);最后一个字节是前面 8 + 48 = 56 位的循环冗余校验码(CRC CODE),若是主器件想读取该器件的ROM信息,可以将前56位编码做一个循环冗余校验,生成一个8bit的循环冗余校验码,然后和最后的这一个字节进行对比,若相同,则说明读取信息是正确的,否则,主器件的读取是有误的。

        从上面的分析中,找到了关键的地址信息——中间的这个48bit序列号,按照数学的逻辑组合,它可以分配2的48次方281,474,976,710,656个地址,以亿为单位算,有281474亿个地址!实在是太多了,所以器件生产商将每一个器件都做了一个固定的序列号;不过这样有一个麻烦的是,拿到手中的器件的地址信息最开始是不知道的,要想获得某个器件的地址,必须得用主器件将它的这个序列号读取出来;然而实际上,在1-wire总线上挂了多个DS18B20的器件,要将它们的地址都获取出来,是非常麻烦的;芯片手册里给出了一种算法,将不同的器件识别出来,这里的识别指的不是准确的获取它们的序列信息。所以通常情况下,在未知器件序列号的情况下,都是采用单个从器件跟主器件连接的。要想主器件上一根I/O口线连接多个从器件,那么就得想想其他办法了。

        这里提出一种简单的方法供参考。鉴于DS18B20的数据输出口传输的是数字信号,故MCU的数据端口也应该配置的数据输入输出端口;将它们抽象起来看,就是一个数字端口能分时的与多个数字端口连接,常采用方法就是用选择器,n个地址的选择器就可以分2的n次方个连接端口,下图是以8选1的集成芯片74151做例子解释。

    image FIG2.2 74151多路选择器

        本图的重要目的是为了解释接口的关系,图纸并不完全。左边是DS18B20的插座,数据接入选择器的输入端,输出端和输入端口选择的地址接在MCU输入输出端子上。如果还需要接更多的DS18B20器件,可以采用选择器路数更多的功能芯片,如16选1,32选1等;如果还需要接有更多的从器件,按照这种逻辑可以使用CPLD/FPGA器件,它们的引脚数比较众多,是做数字设计的好帮手。这样用多路选择器的方法可以解决多个DS18B20分时共用一个数据端口的问题。

        由上述可知,1-wire总线上在同一个时间点上主器件只和一个从器件相连,构成一对一的关系,所以主器件知道从器件的地址信息就不再有意义了。在1-wire通信中,主器件需要了解从器件的控制信息显得更为重要一些了。

        对应于主器件,和从器件通信只有两件事儿,其一是向从器件写数据,另外一个是从从器件读数据。基于DS18B20的1-wire通信协议将器件的控制信息全部列举出来,主器件只需要发送这些控制命令,就可以执行相应的操作了。

    image FIG2.3 DS18B20的指令对照表

        由于DS18B20内部的寄存器不多(只有七个,其中有三个作为保留),所以对器件进行读写的控制就比较简单,发送相应的读写命令指令以后,器件就会按照协议进行数据交换。整体的读写操作流程如下图所示。

    image FIG2.4 主器件向从器件写数据

    image FIG2.5 主器件从从器件读数据

        和其他的通信协议一样,主器件的写操作分为五大步骤,建立通信、写ROM操作指令、写控制操作指令、数据交换、结束通信。

        1-wire协议对它们进行了详细地规定;建立通信是一段480us~960us的低电平复位,如下图所示。

    image FIG2.5 DS18B20的初始化

        操作首先是主控给一段480-960us的低电平,然后释放总线(将总线拉高);紧接着DS18B20等待15-60us以后,输出一个时常为60-240us的低电平,表示收到主器件发来的初始化信息;之后将总线释放掉。主器件可以通过从器件返回的这个低电平判断器件是否正常连接(相当于一个应答)。

        写ROM操作指令。具体主器件是如何将数据一位一位地通过1-wire传送到从器件,这个在后边讲述;这里将它抽象出来,作为一个指令来叙述。 写ROM的操作指令有5个,如下表所示。

    表2.1 ROM指令对照表

    ROM指令 指令功能
    33H READ ROM
    55H MATCH ROM
    F0H SERACH ROM
    ECH ALARM SERACH ROM
    CCH SKIP ROM

        虽然器件给出了五种ROM的操作指令,但是在实际应用中,主器件与从器件的连接是1对1的,所以一般情况下是不需要对ROM数据进行太多的处理的,所以CCH(SKIP ROM)这条ROM指令用得最多。

        写一个控制操作指令。在主器件进行完写ROM操作指令以后,紧接着就是要写一个控制指令了,控制指令表由图FIG2.3给出了,这里只将控制指令部分和指令功能部分列成表给出。

    表2.2 控制指令对照表

    控制指令 指令功能
    44H Convert T
    BEH Read Scratchpad
    4EH Write Scratchpad
    48H Copy Scratchpad
    B8H Recall E2
    B4H Read Power Supply

        简单的解释一下吧,44H(Convert T)指令是让从器件进行温度转换的指令,在正确读取温度数据之前,需要对当前的温度进行转换;BEH(Read Scratchpad)指令是将从器件中的9个RAM数据读取出来,DS18B20的内部RAM结构图如下图所示。

    image FIG2.6 DS18B20内部RAM一览表

        前两个数据是温度转换数据,紧接着的两个是温度上下限控制数据,第五个是配置寄存器,第6到第8个是保留寄存器,第9个是前边数据的CRC校验数据。与掉电数据丢失的SCRATCHPAD RAM块相比,器件内部设置了一个掉电不丢失的电可擦除E2RAM,用于保存温度上下限控制数据和配置寄存器数据。

        这些数据是按BYTE0 到BUTE8的顺序逐步读出的。若需要读取E2RAM块的数据,则需要先执行将E2RAM的数据写入到SCRATCHPAD的控制指令。下面简要的说明一下其他控制指令的功能。

        BEH(READ SCRATCHPAD)指令是读取SCRATCHPAD的指令,执行此条指令以后,从器件会将内部SCRAPTCHPAD的数据逐步的发送至1-wire上。当然在读的这个过程中,主器件随时可以中断读操作,只对特定感兴趣的数据读取。

       4EH(WRITE SCRATCHPAD)指令是写SCRATCHPAD指令,执行此条指令以后,主器件需要完整地将温控上下限数据(TH 与TL)和配置数据(CONFIG)依次发送到1-wire上。值得注意的是此条指令的写只是将数据存放到SCRATCHPAD中,而不是E2RAM中,它是会掉电丢失的。

       48H(COPY SCRACTCHPAD)指令是将SCRATCHPAD中的温控上下限数据(TH与TL)和配置数据(CONFIG)写到E2RAM中,用于掉电数据保持。执行此条指令以后后续不需要再写数据。

        B8H(RECALL SCRATCHPAD)指令是将E2RAM的数据读到SCRATCHPAD中,一般是用于读取E2RAM的数据进行校验的场合。执行此条指令以后后续不需要再写数据。

        B4H(READ POWER SUPPLY)指令是读取芯片的供电方式。一种是独立的电源供电,返回1;另外一种是用DQ引脚端复合供电,返回0。执行此条指令以后后续不需要再写数据。

        了解总体的数据读写操作流程以后,剩下的就是具体的读写操作过程了。因为读写的操作过程数据线上只有0和1的变化,没有时钟的辅助,所以DS18B20对时序的控制非常严格。还是站在主器件的角度去看。

    image FIG2.7 主器件写一位0或者1数据时序操作

        主器件的写是一个特定时间内完成特定数据的变化的过程。主器件首先要发送一个大于1us的低电平,后续就发送相应的数据位,整个1位写的时常至少为60us。如上图左边写0的过程,主器件首先发送一个大于1us的低电平电平,由于数据是0,所以后续的电平为低电平,直至整个位长时间结束(至少60 - 1us)。写完1位数据以后,需要释放数据线,否则数据线低电平时间超过480us,则导致器件开始初始化操作。右边写1的过程也是一样,主器件首先发送一个大于1us的低电平,由于数据是1,所以发送完低电平以后就需要释放数据线,数据线高电平时间一直保持到整个位长时间结束(至少60-1us);与发送1相比,最后可以不需要释放总线(因为数据线本就是高电平)。图中灰色的方框内给出了典型的操作时间;在前15个us以内,写操作主控需要将数据线拉低至少1us,然后将要发送的数据电平发送到数据上并保持稳定,这整段的时间最好在15us左右,余下的15+30 = 45us是从器件DS18B20读取数据(电平采样)的过程;采样完成以后,需要释放数据线,两位数据读取的时间要大于1us。

    image FIG2.8 主器件读取一位0或者1数据的时序操作

        理解了主器件写一位数据的操作流程,那么来理解主器件读一位数据的操作流程就比较简单了,它们的格式都是一样的。主器件读取一位数据之前,也需要发送一个大于1us的低电平,然后等待从器件将相应的数据放到数据线上直到稳定;主器件等待一段时间以后对数据线进行采样,如果数据线为高电平,表示从器件发送了一个1;相反,如果数据线为低电平,则表示从器件发送一个0数据。整个读取一位数据的时长至少为60us,1位数据读取完毕以后,从器件会自动的将数据线释放掉,做好发送下一位数据的准备。

        插一段理解。纵观通信特点,读和写都的过程都是差不多的;每一次读或写的开始,主器件都需要发送一个至少1us时常的低电平,可以认为是数据传输的开始,紧接着是将数据放到数据线并稳定下来,这整个的时常大约为15us,余下的时间就是等待主器件或者从器件的采样操作,最后就是将数据线释放掉,准备下一次数据的传输操作。

      2.2 基于MCU的协议实现

        用MCS-51来实现一下,采用的是传统51单片机,12M晶振,故指令时间最短为1us。

       2.2.1 DS18B20 复位

    bit Init_DS18B20(void)
    {
     bit dat=0;          // DQ复位
     DQ = 1;            
     DelayUs2x(5);    // 稍做延时,高电平维持一小段时间
     DQ = 0;         //单片机将DQ拉低
     DelayUs2x(200); //精确延时 大于 480us 小于960us
     DelayUs2x(200);
     DQ = 1;        // 拉高总线
     DelayUs2x(50); //15~60us 后 接收60-240us的存在脉冲
     dat=DQ;        //如果x=0则初始化成功, x=1则初始化失败
     DelayUs2x(25); //稍作延时返回
     return dat;
    }

        2.2.2 MCU读取一个字节

    unsigned char ReadOneChar(void)
    {
    unsigned char i=0;
    unsigned char dat = 0;
    for (i=8;i>0;i--)
     {
      DQ = 0; // 给低电平信号
      dat>>=1;
      _Nop();
      _Nop();  // 维持一段时间
      DQ = 1; // 给脉冲信号
      if(DQ)
       dat|=0x80;
      DelayUs2x(25); // MCU采样
     }
     return(dat);
    }

       2.2.3 MCU写一个字节

    void WriteOneChar(unsigned char dat)
    {
     unsigned char i=0;
     for (i=8; i>0; i--)
     {
      DQ = 0;            // 先发送一个低电平
      _Nop();
      _Nop();             // 低电平时常大于1us 
      DQ = dat&0x01;     // 将数据发送到总线上
      DelayUs2x(25);     // 保持数据一段时间
      DQ = 1;             // 释放数据线
      dat>>=1;
     }
    DelayUs2x(25);         // 时序缓冲处理,可省略
    }

       2.2.4 读取温度转换值

    unsigned int ReadTemperature(void)
    {
    unsigned char a=0;
    unsigned int b=0;
    unsigned int t=0;
    Init_DS18B20();
    WriteOneChar(0xCC); // 跳过读序号列号的操作
    WriteOneChar(0x44); // 启动温度转换
    DelayMs(750);        // 等待数据转换完毕
    Init_DS18B20();        // 初始化DS18B20
    WriteOneChar(0xCC); //跳过读序号列号的操作 
    WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
    a=ReadOneChar();   //低位
    b=ReadOneChar();   //高位
    
    b<<=8;
    t=a+b;                // 数据合并
    
    return(t);
    }

        值得注意的是,主器件每次对从器件的操作,都需要符合固定的通信格式,详细地可以参照2.1节协议分析。

      2.3 协议验证

        限于手上只有一个DS18B20温度芯片,所以直接将它与MCU相连就可以了,采用默认的12位温度转换方式。不考虑扩展的情况。测试的工具是一个量程为0置100度的液体温度计,温度计的分辨率为0.1度。下面是一个室内室外早晚的测试结果表。

    表2.2 温度测试表

    测量次数 温度计测量值(度) DS18B20测量值(度)
    1 12.5 12.496
    2 16.5 16.496
    3 18.7 18.695
    4 14.8 14.795

         对照上述的结果表,可以看出测量结果还是比较精确的。产生误差的原因有两个方面,一个是传感器量化的误差,另外一个是温度计的测量精度有限,人眼来读取数值时难免会有些误差。

        从上述的结果分析中可以看出,主器件MCU与从器件DS18B20之间按照1-wire的通信方式是成功的。

    3.Conclusion

        总体上来说,1-wire协议还是不复杂的,但是对时序的要求还是比较高;相对其他的两线或者三线协议,1线协议要实现单根线上挂多个器件是非常麻烦,这或许也是1-wire协议用得不是很广泛的原因吧。

    4.Reference

    1). AT24C02 datasheet

  • 相关阅读:
    深入nginx之《获取用户的真实IP》
    深入Nginx之《常用参数配置技巧》
    深入Nginx之《HTTP请求报文与HTTP响应报文》
    webapck html-loader 静态html模块化
    webpack四个基础概念
    从原生Android 跳转到hbuilder项目
    移动端适配方案 flexible.js
    vue使用px2rem
    koa2 post请求ctx.request.body空获取不到的解决办法
    url、href、src
  • 原文地址:https://www.cnblogs.com/hechengfei/p/4121321.html
Copyright © 2011-2022 走看看