zoukankan      html  css  js  c++  java
  • 使用天祥TX-1C调试DS18B20温度传感器的收获

        翻查DS18B20的DataSheet编写操作函数,其过程遇到了不少坎,记下来备查。

    1. 对于单总线的DS18B20芯片,首先严格按照时序图写出正确的“写0”、“写1”和“读0、1”的基础函数,再以此写出其他基础操作的命令。

            我在编制“写0”函数的时候,省却了最后拉高总线的一句bus=1,结果后续操作过程中大部分功能都正确,只有写精度的部分总是出错,写进去9位的“0x1f”,总是得到10位的“0x3f”,翻论坛、看datasheet,反复调试,浪费的大量时间,最后才发现症结所在,并经过测试该错误得以重现。

     

     1 sbit bus = P2^2;    //天祥板DS18B20的DQ数据脚直连P2^2
     2 
     3 //写0
     4 void DS18B20_WriteZero(void)
     5 {
     6     bus = 1;
     7     bus = 0;
     8     DelaySpecial(6);    //保持低电平时间60~120us,实际约71
     9     bus = 1;       //就是这里!开始没有该语句
    10 }
      //写1
    1
    void DS18B20_WriteOne() 2 { 3 bus = 1; 4 bus = 0; 5 _nop_(); //保持低电平时间大于1us 6 bus = 1; 7 DelaySpecial(6);//在主设备初始化写后,DS18B20读的时间要持续15~60us,实际71us 8 }
     1 /**********************************************************************************
     2 *函数名称:    unsigned char DS18B20_Init(void)
     3 *函数描述:    每次执行ROM command之前,必须进行DS18B20初始化
     4 *入口参数:    无
     5 *出口参数:    1/0。1:失败;0:成功
     6 *备    注:    严格执行时序图时间要求,若晶振不是11.0592MHz,需要从新设定各个等待时间
     7 **********************************************************************************/
     8 bit DS18B20_Init(void)
     9 {
    10     bus = 0;                //主设备发送复位脉冲(Tx)拉低单总线
    11     DelaySpecial(50);       //最小480us,实际约500us
    12     bus = 1;                //主设备放开总线进入接收模式(Rx)
    13     //DS18B20检测到上升沿信号,等待15~60um后,拉低单总线60~240um,作为应答脉冲
    14     DelaySpecial(6);        //此处取71us后
    15     if(bus == 0)            //取样检测是否为低电平
    16     {
    17         bus = 1;            //释放总线
    18         DelaySpecial(42);   //要求整个Master Rx周期的时间最小480us,此处补足所缺时间
    19         return 1;
    20     }
    21     else                    //此处可根据需要修改错误处理
    22     {
    23         DS18B20_ShowErrorCode(ERROR_MESSAGE_INIT_FAILURE); 24         Beep();
    25         return 0;
    26     }
    27 }
     1 /***************************************************************************
     2 *函数名称:    void DelaySpecial(unsigned char num)
     3 *函数描述:    精确延时函数
     4 *入口参数:    序号(unsigned char,<256)
     5 *出口参数:    无
     6 *备    注:    晶振11.0592,num为0时为13us,之后每加一,延长约10us
     7 *num    延时    num     延时    num    延时    num     延时    num   延时
     8 *0      13     10      109     20     208    30      305    40    401
     9 *1      22     11      119     21     217    31      315    41    411
    10 *2      31     12      129     22     227    32      325    42    421
    11 *3      41     13      139     23     237    33      335    43    431
    12 *4      51     14      148     24     247    34      344    44    441
    13 *5      61     15      158     25     256    35      353    45    451
    14 *6      71     16      168     26     266    36      363    46    461
    15 *7      80     17      178     27     276    37      373    47    470
    16 *8      90     18      187     28     286    38      383    48    480
    17 *9      100    19      197     29     295    39      393    49    490
    18 ***************************************************************************/
    19 void DelaySpecial(unsigned char num)
    20 {
    21     while(num--)_nop_();
    22 }
    /**********************************************************************************
    *函数名称:    bit DS18B20_ReadBit(void)
    *函数描述:    主设备从DS18B20读数据,读出“1”或者“0”
    *入口参数:    无
    *出口参数:    读出的位
    *备    注:    操作DS18B20的基础方法,每次读出一位
    **********************************************************************************/
    bit DS18B20_ReadBit(void)
    {
        bit result;
        bus = 1;
        _nop_();//要不要均可
        bus = 0; 
        _nop_();//置低电平后至少保持1us
        bus = 1; 
        _nop_();//时序图推荐读总线的时间放在15us的最后,因此多加了几个_nop_()
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        result = bus;
        DelaySpecial(4);//DS18B20提供的数据在主设备信号下降沿15us后可用,实际31us
        return result;    
    }
     1 /**********************************************************************************
     2 *函数名称:    unsigned char DS18B20_ReadByte(void)
     3 *函数描述:    主设备从DS18B20读数据,读出一个字节
     4 *入口参数:    无
     5 *出口参数:    读出的字节
     6 *备    注:    操作DS18B20的基础方法,每次读出一位
     7 **********************************************************************************/
     8 unsigned char DS18B20_ReadByte(void)
     9 {
    10     unsigned char byteResult, i;
    11     bit result;
    12     for(i=0; i<8; i++)
    13     {
    14         byteResult >>= 1;
    15 16         result = DS18B20_ReadBit();
    17         byteResult |= (((unsigned char)result)<<7);
    18 19     }
    20     return byteResult;
    21 }
     1 /**********************************************************************************
     2 *函数名称:    void DS18B20_WriteCommandOrByte(unsigned char data_)
     3 *函数描述:    向DS18B20写一个字节数据
     4 *入口参数:    要写入的字节数据
     5 *出口参数:    无
     6 *备    注:    操作DS18B20的基础方法,每次写入一个字节
     7 **********************************************************************************/
     8 void DS18B20_WriteCommandOrByte(unsigned char data_)
     9 {
    10     unsigned char i = 8;
    11     while(i--)
    12     {
    13         if(data_ & 0x01)
    14         {
    15             DS18B20_WriteOne();
    16         }
    17         else
    18         {
    19             DS18B20_WriteZero();
    20         }
    21         data_ >>= 1;
    22     }
    23 }

     DS18B20_WriteCommandOrByte()中使用到的预定义命令如下:

     1 #define ROM_COMMAND_SEARCH_ROM              0xF0
     2 #define ROM_COMMAND_READ_ROM                0x33
     3 #define ROM_COMMAND_MATCH_ROM               0x55
     4 #define ROM_COMMAND_SKIP_ROM                0xCC
     5 #define ROM_COMMAND_ALARM_SEARCH            0xEC
     6 
     7 #define FUNCTION_COMMAND_CONVERT_T          0x44
     8 #define FUNCTION_COMMAND_WRTIE_SCRATCHPAD   0x4E
     9 #define FUNCTION_COMMAND_READ_SCRATCHPAD    0xBE
    10 #define FUNCTION_COMMAND_COPY_SCRATCHPAD    0x48
    11 #define FUNCTION_COMMAND_RECALL_EEPROM      0xB8
    12 #define FUNCTION_COMMAND_READ_POWER_SUPPLY  0xB4

         2.本来想封装大部分功能,方便使用时调用,但是该芯片的操作非常复杂,整来整去代码的体积太大。看来该芯片还是不太适合过多的封装,因此总结了该芯片使用方法的规律

            整个使用方法都集中在datasheet的“ROM Commands Flowchart”和“DS18B20 Function Commands Flowchart”两张图中,这是使用的核心所在。

            我只调试了单个DS18B20采用单独供电的情况,这里有几点需要说明,备忘。

      2.1 每次操作的顺序如下,使用哪个Rom Command,跟哪几个Function Command,顺序是什么,均根据功能需要,查阅这两个Flow Chart。

        1)   初始化(DS18B20_Init())

        2)  一个Rom Command(DS18B20_WriteCommandOrByte (**))

        3)   连续多个Function Command(DS18B20_WriteCommandOrByte(**))

       2.2  Rom Command共有5个。

      DS18B20芯片接收到初始化命令(DS18B20_Init())之后,对再次接到信号逐次判断是33H、55H、F0H、ECH还是CCH,如果对上了哪一个,就往哪个分支上走去。如果对不上,该芯片会退回到初始状态,如果还想操作它,必须从初始化命令开始从新再来。

      这5个Rom操作命令分别是:

      a)   33H,Read Rom Command:该命令只能用在总线上只有一个从设备的情况,使主设备不经过Search Rom过程,直接读取从设备的64位Rom编码(从低到高分别是:类别信息,ID,CRC信息)

      b)   55H,Match Rom Command:该命令后跟着发送64位Rom代码,表示要操作的对象

      c)   F0H,Search Rom Command:通过该命令,主设备获得总线上连接的所有从设备的信息

      d)   ECH,Alarm Search Command:搜寻温度报警命令

      e)   CCH,Skip Rom Command:跳过Rom操作命令,对于系统上只有一只单总线芯片的情况,发送该命令后,即可发送Function Command了。

      2.3  Function Command共有六个

      如同Rom Command一样,芯片也是逐个比对功能命令,对上哪一个就往那里走去。

      a)   44H,Convert Temperature转化温度命令,DS18B20接到后开始进行温度转化,注意从9位精度一直到12位精度,转化所需的时间越来越长。这里有两种办法处理等待的时间:一个是,需根据所设的精度调整等待时间,见下图。第二个是,转化未完成前DS18B20一直将总线置低电平,可以用DS18B20_ReadBit()进行判断,如果读到高电平则说明转化已完成,否则返回继续等待;需要注意的是,为了防止出现意外情况需要设置一个等待时间限度,防止程序死锁,下面程序中给出循环次数15000次,大概2秒的时间。

    /**********************************************************************************
    *函数名称:float DS18B20_SingleAndExternalPowerGetT(void)
    *函数描述:对于仅挂有1个DS18B20且使用外部电源供电的系统,取得测量的温度
    *入口参数:无
    *出口参数:是否成功,如果返回-100表示转化失败
    *备    注:还有很大优化空间
    *温度配置寄存器结构----------------------------------------------------------------
    *bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
    *0    R1   R0   1    1    1    1    1
    *R1 R0 Resolution Max Conversion Time
    *0  0  9-bit      93.75 ms (tCONV/8)
    *0  1  10-bit     187.5 ms (tCONV/4)
    *1  0  11-bit     375 ms   (tCONV/2)
    *1  1  12-bit     750 ms   (tCONV)
    *Th、Tl结构------------------------------------------------------------------------
    *        bit15 b14 b13 b12 b11 b10 b9  b8  b7  b6  b5  b4  b3   b2   b1   b0
    *        S     S   S   S   S   2^6 2^5 2^4 2^3 2^2 2^1 2^0 2^-1 2^-2 2^-3 2^-4
    *   9位                                                    有效
    *  10位                                                    有效 有效
    *  11位                                                    有效 有效 有效
    *  12位                                                    有效 有效 有效 有效
    *        --------------------------------- -----------------------------------
    *                MS Byte(thTlCr[0])                  LS Byte(thTlCr[1])
    *S(signal):1=负,0=正
    *bit10~bit4,共7位,代表整数部分
    *bit3~bit0,共4位,代表小数部分
    **********************************************************************************/
    float DS18B20_SingleAndExternalPowerGetT()
    {
        unsigned int i = 15000; //见上面文字说明部分
        unsigned char tem = 0;
        unsigned char tlTmCr[3] = {0};
        float t;
        DS18B20_Init();
        DS18B20_WriteCommandOrByte(ROM_COMMAND_SKIP_ROM);      //仅有一个测温器件的,先执行一下本命令
        DS18B20_WriteCommandOrByte(FUNCTION_COMMAND_CONVERT_T);//发起温度转化命令
        while(DS18B20_ReadBit()==0 || i--==0);          //见上面文字说明部分
        if (DS18B20_GetTlTMCr(tlTmCr))
        {
            t = ((tlTmCr[1] & 0x07) << 4) | ((tlTmCr[0] & 0xf0) >> 4);//得到温度的整数部分
            if (tlTmCr[1] & 0xf8)//前5位是1,代表温度为负
            {
                switch (tlTmCr[2])//根据温度配置寄存器R1、R0的值确定转化的温度是9、10、11还是12的
                {
                case 0x1f:
                    t += (~((tlTmCr[0] & 0x08) >> 3) +1) * 0.5;
                    break;
                case 0x3f:
                    t += (~((tlTmCr[0] & 0x0c) >> 2) +1) * 0.25;
                    break;
                case 0x5f:
                    t += (~((tlTmCr[0] & 0x0e) >> 1) +1) * 0.125;
                    break;
                case 0x7f:
                    t += (~(tlTmCr[0] & 0x0c) +1) * 0.0625;
                    break;
                }
                t = -t;
            }
            else
            {
                switch (tlTmCr[2])//根据温度配置寄存器R1、R0的值确定转化的温度是9、10、11还是12的
                {
                case 0x1f:
                    t += ((tlTmCr[0] & 0x08)>>3) * 0.5;
                    break;
                case 0x3f:
                    t += ((tlTmCr[0] & 0x0c)>>2) * 0.25;
                    break;
                case 0x5f:
                    t += ((tlTmCr[0] & 0x0e)>>1) * 0.125;
                    break;
                case 0x7f:
                    t += (tlTmCr[0] & 0x0f) * 0.0625;
                    break;
                }
            }
            return t;
        }
        return -100;
    }
     1 /**********************************************************************************
     2 *函数名称:    bit DS18B20_GetTlTMCr(unsigned char * thTlCr)
     3 *函数描述:    取得Th和Tl的值
     4 *入口参数:    指向16位温度寄存器低8位、高8位和位数配置寄存器数组的指针
     5 *出口参数:    无
     6 *备    注:    入口参数的指针指向连续三个字节,分别是Tlsb、Tmsb、ConfigurationRegister
     7 **********************************************************************************/
     8 bit DS18B20_GetTlTMCr(unsigned char *tlTmCr)
     9 {
    10     unsigned char scratchPad[9] = {0};
    11     if(DS18B20_GetScrtchPad(scratchPad))    //最高位为0,代表crc验证错误,Th Tl均直接返回错误代码0
    12     {
    13         *tlTmCr = scratchPad[0];
    14         *(tlTmCr+1) = scratchPad[1];
    15         *(tlTmCr+2) = scratchPad[4];
    16         return 1;
    17     }
    18     else
    19     {
    20         return 0;
    21     }
    22 }    

      b)   48H,Copy ScratchPad:将温度上下限和配置寄存器的内容拷贝到EEPROM

      c)   4EH,Write ScratchPad:主设备依次写入TH(Byte2,温度报警高限)、TL(Byte3,温度报警低限)和温度配置寄存器(Byte4)。

     1 /**********************************************************************************
     2 *函数名称:    void DS18B20_Config(char tHighAlarm, char tLowAlarm, unsigned char bits)
     3 *函数描述:    写高低报警温度和温度配置寄存器
     4 *入口参数:    tHighAlarm、tLowAlarm带符号数(只能设置成整温度报警值),bits=9、10、11、12位
     5 *出口参数:    无
     6 *备    注:    
     7 *THAlarm and TLAlarm Register
     8 *BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0
     9 *Sign 2^6  2^5  2^4  2^3  2^2  2^1  2^0
    10 **********************************************************************************/
    11 void DS18B20_Config(char tLowAlarm, char tHighAlarm, unsigned char bits)
    12 {
    13     unsigned char code arrayBit[] = { 0x1f, 0x3f, 0x5f, 0x7f };
    14     DS18B20_Init();
    15     DS18B20_WriteCommandOrByte(ROM_COMMAND_SKIP_ROM);//cc
    16     DS18B20_WriteCommandOrByte(FUNCTION_COMMAND_WRTIE_SCRATCHPAD);//4e
    17     DS18B20_WriteCommandOrByte(tHighAlarm);
    18     DS18B20_WriteCommandOrByte(tLowAlarm);
    19     DS18B20_WriteCommandOrByte(arrayBit[bits - 9]);
    20 }

      d)   BEH,Read ScratchPad依次读取ScratchPad内容,掌握转化的温度、上下限温度报警值以及精度配置寄存器的内容都需要执行该操作。

     1 /**********************************************************************************
     2 *函数名称:    bit DS18B20_ReadScrtchPad(*scrtchPad)
     3 *函数描述:    读取ScrtchPad的内容
     4 *入口参数:    指向ScrtchPad的指针,ScrtchPad本身长9字节
     5 *出口参数:    读Scratchp是否成功,1=成功,0=失败
     6 *备    注:    无
     7 **********************************************************************************/
     8 bit DS18B20_GetScrtchPad(unsigned char *scrtchPad)
     9 {
    10     unsigned char i; 
    11     if (!DS18B20_Init()) return 0;
    12     DS18B20_WriteCommandOrByte(ROM_COMMAND_SKIP_ROM);
    13     DS18B20_WriteCommandOrByte(FUNCTION_COMMAND_READ_SCRATCHPAD);
    14     for(i = 0; i<9; i++)
    15     {
    16         *(scrtchPad + i) = DS18B20_ReadByte();
    17     }
    18     if(calcrc_bytes(scrtchPad, 9))
    19         return 1;
    20     return 0;
    21 }
     1 /********************************************************/
     2 /*DS18B20的CRC8校验程序,抄来的,仅根据习惯修改了返回值1代表成功,0代表失败
     3 /********************************************************/ 
     4 unsigned char calcrc_1byte(unsigned char abyte)
     5 {
     6     unsigned char i,crc_1byte; 
     7     crc_1byte=0;   
     8     //设定crc_1byte初值为0 
     9     for(i = 0; i < 8; i++) 
    10     {
    11         if(((crc_1byte^abyte)&0x01))
    12         {
    13             crc_1byte^=0x18;
    14             crc_1byte>>=1;
    15             crc_1byte|=0x80;
    16         }
    17         else
    18             crc_1byte>>=1;
    19         abyte>>=1; 
    20     }
    21     return crc_1byte;
    22 }
    23 /***************************************************************************
    24 *函数名称:    bit calcrc_bytes(unsigned char *p,unsigned char len)
    25 *函数描述:    CRC校验
    26 *入口参数:    指向最末一字节是CRC信息的指针;长度
    27 *出口参数:    是否成功
    28 *备    注:    1=校验成功;0=校验失败
    29 ***************************************************************************/
    30 bit calcrc_bytes(unsigned char *p,unsigned char len)
    31 {
    32     unsigned char crc=0;
    33     while(len--) //len为总共要校验的字节数
    34     {
    35         crc=calcrc_1byte(crc^*p++);
    36     }
    37     if(crc)//如果crc不等于0,数据传输错误
    38     {
    39         return 0;
    40     }
    41     else
    42     {
    43         return 1;  //若最终返回的crc为0,则数据传输正确
    44     }
    45 }

      ScratchPad从0~8共9个字节,其结构见下图,最后一个字节是前8个字节的CRC信息。如果不做CRC校验的话,读到所需的字节后,可以发送一个初始化信号DS18B20_Init()后,终止后面的读取动作。

      温度转化完成后就存储在ScrathPad的第0、1字节中。第1个字节是温度的高8位,第0个字节是温度的低8位。

    • 高8位的高5位代表正负号(Bit15~Bit11),正温度时均为0,负温度时均为1;
    • 高8位的低3位(Bit10~Bit8)和低8位的高4位(Bit7~Bit4)组合成温度的整数部分(共7位),无论温度为正、为负,这7位直接就是温度的整数部分;
    • 低8位的低4位(Bit3~Bit0)是温度的小数部分,注意:温度为正时直接换算即可,当温度为负时,小数部分要取反、加一后再换算。
      •   9位精度时  :只用Bit3,1代表0.5℃
      •   10位精度时:使用Bit3、Bit2,1代表0.25℃,如该两位为10B时,温度为2*0.25=0.50℃
      •   11位精度时:使用Bit3、Bit2和Bit1,1代表0.125℃,如该三位为101B时,温度为5*0.125=0.625℃
      •   12位精度时:Bit3~Bit0都使用,1代表0.0625℃
          • 如该四位为1011B时,温度为11*0.0625=0.6875℃
          • 在温度为负时,同样的1011B,先取反得到0100B,再加一得到0101B,温度为-5*0.0625 = -0.3125℃

      e)   B8H,Recall EEPROM:将存储到EEPROM中的上下限报警温度和温度精度配置寄存器的内容,写回到ScratchPad中,相当于48H号命令Copy ScratchPad的逆操作。

      f)    B4H,Read Power Supply:检查从设备是独立供电模式,还是寄生电源模式。

    ROM Commands Flowchart 

     

    DS18B20 Function Commands Flowchart

  • 相关阅读:
    项目笔记:统计页面功能实现
    jquery easyui datagrid实现数据改动
    Skia图片解码模块流程分析
    TRIZ的成功案例
    基于HTML5的Web SCADA工控移动应用
    webservices系列(五)——javaweb整合Axis2及多service配置
    org.hibernate.PropertyValueException: not-null property references a null or transient value: model.
    线程池和异步线程
    [leetcode]Implement strStr()
    Python工作日类库Busines Holiday介绍
  • 原文地址:https://www.cnblogs.com/jqdy/p/12357688.html
Copyright © 2011-2022 走看看