使用天祥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

 

posted @ 2020-02-24 17:45  jqdy  阅读(721)  评论(0编辑  收藏  举报