打印

DS18B20测温芯片的专业驱动-带CRC校验

[复制链接]
6517|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
古道热肠|  楼主 | 2007-4-29 16:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  在社区众人的帮助下,DS18B20驱动程序加入了CRC8的校验,如此可确保取得的温度数据是真实的。代码如下,请多多指教!
  本驱动程序在盛唐EPS11仿真编程器上测试运行良好。

#define MACRO_Delay_1uS() _nop_();_nop_();_nop_();_nop_();
#define Delay_100uS() Delay_100us()


void Delay_1uS(void)
{
    _nop_();
    _nop_();
    _nop_();
    _nop_();
}

void Delay_15uS(void)
{
    uchar data uci;

    for(uci=0; uci<5; uci++)
    {
        _nop_();
    }

}

void Delay_60uS(void)
{
    uchar data uci;

//    for(uci=0; uci<60; uci++)
    for(uci=0; uci<20; uci++)//循环一次占用9个机器周期
    {
        _nop_();
    }


}

//--------------DS18B20测温集成电路驱动------------------
sbit c_DS18B20_IO = P1^2;
void DS18B20_WriteOneBit_0(void)
{
    c_DS18B20_IO = 1;
    c_DS18B20_IO = 0;
    Delay_15uS();

    Delay_15uS();
    Delay_15uS();

    c_DS18B20_IO = 1;
}
void DS18B20_WriteOneBit_1(void)
{
    c_DS18B20_IO = 1;
    c_DS18B20_IO = 0;
    Delay_15uS();
    c_DS18B20_IO = 1;
    Delay_15uS();
    Delay_15uS();
}

bool DS18B20_ReadOneBit(void)
{
    bool bReturnValue;

    c_DS18B20_IO = 1;
    c_DS18B20_IO = 0;
    
    Delay_1uS();
    c_DS18B20_IO = 1;
    Delay_1uS();
    Delay_1uS();

    bReturnValue = c_DS18B20_IO;

    Delay_15uS();
    Delay_15uS();

    return bReturnValue;
}

bool DS18B20_Reset(void)
{
//    bool bReturnValue;
    uchar uci;


    c_DS18B20_IO = 1;
    c_DS18B20_IO = 0;

    for(uci=0; uci<8; uci++)
    {
        Delay_60uS();
    }
    c_DS18B20_IO = 1;
    Delay_60uS();

//    while(c_DS18B20_IO);
    
    if(c_DS18B20_IO == 1)
    {
        Delay_60uS();
        if(c_DS18B20_IO == 1)
        {
            return false;
        }
        else
        {
            for(uci=0; uci<8; uci++)
            {
                Delay_60uS();
            }
            return true;
        }

    }
    else
    {
        for(uci=0; uci<8; uci++)
        {
            Delay_60uS();
        }
        return true;
    }
    /*
        for(uci=0; uci<8; uci++)
        {
            Delay_60uS();
        }
        return true;
*/
}

void DS18B20_WriteOneByte(uchar ucWriteData)
{
    uchar uci;
    uchar ucTemp;

    ucTemp = ucWriteData;
    for(uci=0; uci<8; uci++)
    {
        if(ucTemp & 0x01)
        {
            DS18B20_WriteOneBit_1();
        }
        else
        {
            DS18B20_WriteOneBit_0();
        }
        ucTemp >>= 1 ;
    }
}

uchar  DS18B20_ReadOneByte(void)
{
    uchar uci;
    uchar ucReadData;
    uchar ucMaskCode;

    ucReadData = 0;
    ucMaskCode = 0x01;
    for(uci=0; uci<8; uci++)
    {
        if(DS18B20_ReadOneBit())
        {
            ucReadData |= ucMaskCode;
        }
        ucMaskCode <<= 1 ;
    }
    return ucReadData;
}

void DS18B20_VCC5V_On(void)
{
    SetPin26_V5OE_Enable();
}
//读取特征字
//64Bit
bool DS18B20_ReadRomCode(uchar *ucData)
{
    uchar uci;
    bool bReturnValue;

    DS18B20_VCC5V_On();   //定义函数
    Delay_10MS(1);

    DISABLE();

    if(DS18B20_Reset())
    {
        DS18B20_WriteOneByte(0x33);        
        for(uci=0; uci<8; uci++)
        {
            ucData[uci] = DS18B20_ReadOneByte();
        }
        bReturnValue = true;
    }
    else
    {
        bReturnValue = false;
    }

    //开启中断
      ENABLE();
    return (bReturnValue);
}

bool DS18B20_CheckCRC8(uchar *ucDataBuff,uchar ucDataLength)
{
//查表异或
uchar code TAB_CRC_8[] = {
      0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
    157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
    35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
    190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
    70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
    219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
    101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
    248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
    140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
    17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
    175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
    50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
    202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
    87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
    233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
    116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
    };


    uchar ucCRCValue;
//    uchar ucNewData;
    uchar ucTabIndex;    //查表索引值
    uchar ucCount;

    ucCRCValue = 0;

    for(ucCount=0; ucCount<ucDataLength; ucCount++)
    {
//        ucNewData = ucDataBuff[ucCount];        //Use in Debug 
//        ucTabIndex = ucCRCValue^ ucNewData;

        ucTabIndex = ucCRCValue^ ucDataBuff[ucCount];
        ucCRCValue = TAB_CRC_8[ucTabIndex];
    }

    if(ucCRCValue == 0)
    {
        return true;
      }
    else
    {
        return false;
      }

}



//初始化DS18B20
bool InitDS18B20(void)
{
    bool bReturnValue;

    DS18B20_VCC5V_On();   //定义函数
    Delay_10MS(1);
    DISABLE();

    if(DS18B20_Reset())
    {

        DS18B20_WriteOneByte(0xCC);        

        DS18B20_WriteOneByte(0x4E);        
        DS18B20_WriteOneByte(0x64);        
        DS18B20_WriteOneByte(0x8A);        
        DS18B20_WriteOneByte(0x1F);
        bReturnValue = true;
    }
    else
    {
        bReturnValue = false;
    }
        //开启中断
      ENABLE();
    return (bReturnValue);
}

//读取温度值
bool DS18B20_GetTemperature(uchar *ucTemperatureDataBuff)
{
    uchar ucTemperatureLowByte;
    uchar ucTemperatureHighByte;
    bool bReturnValue;

    uchar ucTempDataBuff[9];        //用于映像DS18B20内容字节内容的缓冲区

    DS18B20_VCC5V_On();   //开启电源开关.
    Delay_10MS(1);

    DISABLE();

    if(DS18B20_Reset())
    {
        DS18B20_WriteOneByte(0xCC);        
        DS18B20_WriteOneByte(0x44);        

        ENABLE();
        Delay(100);
        DISABLE();

        DS18B20_Reset();
        DS18B20_WriteOneByte(0xCC);        
        DS18B20_WriteOneByte(0xBE);        

        ucTempDataBuff[0] = DS18B20_ReadOneByte();
        ucTempDataBuff[1] = DS18B20_ReadOneByte();
        ucTempDataBuff[2] = DS18B20_ReadOneByte();
        ucTempDataBuff[3] = DS18B20_ReadOneByte();
        ucTempDataBuff[4] = DS18B20_ReadOneByte();
        ucTempDataBuff[5] = DS18B20_ReadOneByte();
        ucTempDataBuff[6] = DS18B20_ReadOneByte();
        ucTempDataBuff[7] = DS18B20_ReadOneByte();
        ucTempDataBuff[8] = DS18B20_ReadOneByte();

        if(DS18B20_CheckCRC8(ucTempDataBuff,9))
        {
        //进行数据格式的转换。
            ucTemperatureLowByte = ucTempDataBuff[0];
            ucTemperatureHighByte = ucTempDataBuff[1];

            ucTemperatureDataBuff[0] = (ucTemperatureLowByte >> 4)+(ucTemperatureHighByte<<4);    //温度整数部分
            ucTemperatureDataBuff[1] = (ucTemperatureLowByte & 0x0F)? 5:0;    //温度小数部分.

            bReturnValue = true;
           }
        else
        {
        //显示Err字样
            ucTemperatureDataBuff[0] = 0x0E;
            ucTemperatureDataBuff[1] = 0x12;            //字模索引值
            ucTemperatureDataBuff[2] = 0x12;            //字模索引值

            bReturnValue = false;
        }
    }
    else
    {
        //显示Err字样
        ucTemperatureDataBuff[0] = 0x0E;
        ucTemperatureDataBuff[1] = 0x12;            //字模索引值
        ucTemperatureDataBuff[2] = 0x12;            //字模索引值

        bReturnValue = false;
    }

    //开启中断
      ENABLE();
    return (bReturnValue);
}

相关帖子

沙发
古道热肠|  楼主 | 2007-4-29 16:46 | 只看该作者

补充一下调试环境

  系统主MCU为STC89C516RD+ 运行在6时钟模式,机器晶振为18。423MHZ,其它环境,延时函数需作相应的调整

使用特权

评论回复
板凳
chunyang| | 2007-4-29 17:16 | 只看该作者

给穿条裤子

使用特权

评论回复
地板
古道热肠|  楼主 | 2007-4-30 10:08 | 只看该作者

如何写好C程序

  笔者以为,可读性、可移植性、可维性、可靠性比玩弄技巧,片面追求速度与代码长度更为重要,希望能以上述程序为例进行相关讨论。现在的单片机资源比90年代丰富多了,凡事宜讲效率。

使用特权

评论回复
5
drq1997| | 2007-4-30 11:51 | 只看该作者

占个位听听

使用特权

评论回复
6
古道热肠|  楼主 | 2007-4-30 13:10 | 只看该作者

有人喜欢听,先说上两句

   程序要好读,个人以为要从以下几点做起:
  1、常量与变量要分清,常量用c_开头(Const的缩写),变量用匈牙利命名法,uc(unsigned Char 的缩写)。
    2、大小写要用好,大写字母开头的小写单词容易辩认,宜用于常量与变量的命名,全用大写字母构成的单词多用于缩写如ROM(Read Only Memory的缩写)。
    3、减少幻数的使用,如先定义true为1 false 为0,再在程序中只用这2个单词给布尔变量赋值,就是一种好习惯。
   

使用特权

评论回复
7
bcaiyo| | 2007-4-30 15:33 | 只看该作者

不错,继续啊!

使用特权

评论回复
8
古道热肠|  楼主 | 2007-4-30 16:43 | 只看该作者

如何提高可移植性

    程序要通用,不作重复的无用功。提高程序的可移植性,本人以为以下几点当重视:
   1、能用C语言解决的,决不用汇编语言,至多混合C与汇编编程,全汇编的方式是程序可移植的一大罪臣。
   2、底层与上层要独立,底层面向硬件,实现物理层通讯。上层面向应用,实现协议层会话,硬件与底层、底层与驱动层、驱动层与应用层要像垒高楼,而不能像蜂窝。
   3、通用与专用要分开,如系统中用到的延时函数,一定要独立出来,单独调试,如此一来,整个系统都会稳定运行,移植到其它系统,单调这几个函数就搞定了,短暂的可用宏来整,长的可用子程序来实现。

使用特权

评论回复
9
王紫豪| | 2007-4-30 23:29 | 只看该作者

最受不了匈牙利命名法。。

使用特权

评论回复
10
古道热肠|  楼主 | 2007-5-5 10:27 | 只看该作者

没有规矩,不成方圆

   “匈牙利命名法”是微秒软极力推荐的一种规范变量的好方法,得到大家的一致公认,楼上如此反对,不是好主意。好的东西,一定要学,自由散漫难成大器。

使用特权

评论回复
11
古道热肠|  楼主 | 2007-5-5 10:32 | 只看该作者

DS18B20芯片的CRC8算法用计算法实现

    五一期间,抽空把DS1818B20芯片的CRC8算法的计算法编制完成如下,请指教,软件在盛唐EPS11仿真编程器上测试通过,运行良好。
uchar CalculateCRCValue(uchar ucNewData,uchar ucCRCValue)
{
    bool bXorValue;        //
    uchar ucTempValue;        //模拟CRC硬件内部的8位数据寄存器
    uchar ucCount;

    for(ucCount=0; ucCount<8; ucCount++)
    {
        //提取新数据位与CRC寄存器的内容的异或值的D0位作为内部通道的输入
        bXorValue = (ucNewData ^ ucCRCValue)& 0x01;

        //执行X4、X5的异或操作
        if(bXorValue)
        {
            ucTempValue = ucCRCValue ^ 0x18;
        }
        else
        {
            ucTempValue = ucCRCValue;
        }
        //CRC寄存器无进位右移
        ucTempValue >>= 1;

        //补上进位位,即D0位
        if(bXorValue)
        {
            ucCRCValue = ucTempValue | 0x80;
        }
        else
        {
            ucCRCValue = ucTempValue;
        }
        
        //为处理下一位作准备
        ucNewData >>= 1;
    }

    return ucCRCValue;
}


//入口参数:待校验的数据缓冲区和数据长度

//测试数据,取自盛唐EPS11仿真编程器
//0x28 0xB4 0xC1 0xE9 0x00 0x00 0x00 0xF0        //DS18B20 64bit Rom Code 
//对应的校验码为:  0xE1 0xE4 0x1C 0x4B 0x66 0xB8 0xF0 0x00

//DS18B20的家族代码为0x28,再加上CRC校验,确保读写通讯可靠。
bool DS18B20_CheckCRC8(uchar *ucDataBuff,uchar ucDataLength)
{
    uchar ucCRCValue;
    uchar ucCount;

    ucCRCValue = 0;

    for(ucCount=0; ucCount<ucDataLength; ucCount++)
    {
        ucCRCValue = CalculateCRCValue(ucDataBuff[ucCount],ucCRCValue);
    }

    if(ucCRCValue == 0)
    {
        return true;
      }
    else
    {
        return false;
      }
}



使用特权

评论回复
12
古道热肠|  楼主 | 2007-5-5 10:39 | 只看该作者

用汇编优化计算速度

    如果对上述算法的速度不满意,可使用汇编代码实现函数的优化,当然查表法是速度最快的,但占用较大的代码空间。
uchar CalculateCRCValue(uchar ucNewData,uchar ucCRCValue)
汇编程序实现上述函数的代码如下,为一单独文件

$NOMOD51

P0    DATA    080H
P1    DATA    090H
P2    DATA    0A0H
P3    DATA    0B0H
T0    BIT    0B0H.4
AC    BIT    0D0H.6
T1    BIT    0B0H.5
T2    BIT    090H.0
EA    BIT    0A8H.7
STC89C58RD_P4    DATA    0E8H
IE    DATA    0A8H
EXF2    BIT    0C8H.6
RD    BIT    0B0H.7
ES    BIT    0A8H.4
IP    DATA    0B8H
RI    BIT    098H.0
INT0    BIT    0B0H.2
CY    BIT    0D0H.7
TI    BIT    098H.1
INT1    BIT    0B0H.3
RCAP2H    DATA    0CBH
PS    BIT    0B8H.4
SP    DATA    081H
T2EX    BIT    090H.1
OV    BIT    0D0H.2
RCAP2L    DATA    0CAH
C_T2    BIT    0C8H.1
WR    BIT    0B0H.6
RCLK    BIT    0C8H.5
TCLK    BIT    0C8H.4
SBUF    DATA    099H
PCON    DATA    087H
SCON    DATA    098H
TMOD    DATA    089H
TCON    DATA    088H
IE0    BIT    088H.1
IE1    BIT    088H.3
B    DATA    0F0H
CP_RL2    BIT    0C8H.0
ACC    DATA    0E0H
ET0    BIT    0A8H.1
ET1    BIT    0A8H.3
TF0    BIT    088H.5
ET2    BIT    0A8H.5
TF1    BIT    088H.7
TF2    BIT    0C8H.7
RB8    BIT    098H.2
TH0    DATA    08CH
EX0    BIT    0A8H.0
IT0    BIT    088H.0
TH1    DATA    08DH
TB8    BIT    098H.3
EX1    BIT    0A8H.2
IT1    BIT    088H.2
TH2    DATA    0CDH
P    BIT    0D0H.0
SM0    BIT    098H.7
TL0    DATA    08AH
SM1    BIT    098H.6
TL1    DATA    08BH
SM2    BIT    098H.5
TL2    DATA    0CCH
PT0    BIT    0B8H.1
PT1    BIT    0B8H.3
RS0    BIT    0D0H.3
PT2    BIT    0B8H.5
TR0    BIT    088H.4
RS1    BIT    0D0H.4
TR1    BIT    088H.6
TR2    BIT    0C8H.2
PX0    BIT    0B8H.0
PX1    BIT    0B8H.2
DPH    DATA    083H
DPL    DATA    082H
EXEN2    BIT    0C8H.3
REN    BIT    098H.4
T2CON    DATA    0C8H
RXD    BIT    0B0H.0
TXD    BIT    0B0H.1
F0    BIT    0D0H.5
PSW    DATA    0D0H


NAME    CALCULATECRC8

?PR?_CalculateCRCValue?CALCULATECRC8     SEGMENT CODE 
?DT?_CalculateCRCValue?CALCULATECRC8     SEGMENT DATA OVERLAYABLE 
    PUBLIC    _CalculateCRCValue

    RSEG  ?DT?_CalculateCRCValue?CALCULATECRC8
?_CalculateCRCValue?BYTE:
  ucNewData?040:   DS   1
 ucCRCValue?041:   DS   1
    ORG  2
    ucCount?042:   DS   1
ucReturnValue?043:   DS   1


; #pragma src  (CalculateCRCValue.a51) small

; #define uchar unsigned char 
; uchar CalculateCRCValue(uchar ucNewData,uchar ucCRCValue)

    RSEG  ?PR?_CalculateCRCValue?CALCULATECRC8
_CalculateCRCValue:
    USING    0
            ; SOURCE LINE # 6
    MOV      ucNewData?040,R7
    MOV      ucCRCValue?041,R5
; {
            ; SOURCE LINE # 7
;     uchar ucCount;
;     uchar ucReturnValue;

;---------------开始计算CRC操作---------------------
    
    MOV ACC,ucNewData?040
;初始化
DO_CRC:    PUSH ACC        ;save accumulator
    PUSH B            ;save the B register
    PUSH ACC        ;save bits to be shifted
    MOV B,#8        ;set shift = 8 bits ;

    ;---------------main loop-----------------------------
CRC_LOOP:    
;        //提取新数据位与CRC寄存器的内容的异或值的D0位作为内部通道的输入

    XRL A,ucCRCValue?041    ;calculate CRC
    RRC A            ;move it to the carry
;        //执行X4、X5的异或操作

    MOV A,ucCRCValue?041    ;get the last CRC value
    JNC ZERO        ;skip if data = 0
    XRL A,#18H        ;update the CRC value
;
  ZERO:    
;        CRC寄存器整体右移1位
    RRC A            ;position the new CRC
    MOV ucCRCValue?041,A    ;store the new CRC
;        //处理新的数据位作准备

    POP ACC            ;get the remaining bits
    RR A            ;position the next bit
    PUSH ACC        ;save the remaining bits

    DJNZ B,CRC_LOOP    ;repeat for eight bits
    ;//-------------------------loop end----------------------


    POP ACC    ;clean up the stack
    POP B    ;restore the B register
    POP ACC    ;restore the accumulator


;---------------结束计算CRC操作-------------------------
;     ucReturnValue = ucCRCValue;
            ; SOURCE LINE # 11
    MOV      ucReturnValue?043,ucCRCValue?041
;     return(ucReturnValue);
            ; SOURCE LINE # 12
    MOV      R7,ucReturnValue?043

; }
            ; SOURCE LINE # 14
?C0001:
    RET      
; END OF _CalculateCRCValue

    END

使用特权

评论回复
13
王紫豪| | 2007-5-5 21:58 | 只看该作者

re 古道热肠

谢谢指教啊!!
非常欣赏你能共享自己的东西,呵呵!
不过我用c的话遵循 K&R风格,可能本科看linux 内核受的映像吧。感觉不管用哪一种风格编程都不是问题,关键是自己要统一。。

使用特权

评论回复
14
tongfeiwa| | 2007-5-5 22:02 | 只看该作者

汇编

我还是比较喜欢用汇编的

使用特权

评论回复
15
古道热肠|  楼主 | 2007-5-6 11:40 | 只看该作者

汇编,让人欢喜让人忧

  汇编,喜欢您的短小精悍,喜欢您的知根见底,喜欢您永远第一的速度,喜欢您...... 多少年来,一直狂热于您,多年以后,不得不弃您而去,生活的压力,生活的阅历,让我们变与了一个务实的人,可移植,可靠,可读,低投入高产出一系列外在因素让我们选择了“C”这个大众情人。

使用特权

评论回复
16
tage| | 2007-5-6 14:14 | 只看该作者

顶一下,,

我只会点汇编,我感觉 18B20 和 IIC等还是汇编看的舒服,,,,

使用特权

评论回复
17
cuijuan923| | 2007-7-15 09:53 | 只看该作者

帮忙啊

我在用MSP430F149驱动DS18B20,但是C程序到现在也没调试出来,您能不能帮帮忙?
谢谢了


cuijuan923@yahoo.com

使用特权

评论回复
18
古道热肠|  楼主 | 2007-7-15 16:18 | 只看该作者

用哪单片机调试,过程都一样

  建议调试步骤如下:
1、首先用示波器校准延时函数
void Delay_1uS(void)
void Delay_15uS(void)
void Delay_60uS(void)
2、调试Reset函数
  调试过程中可打开死循环,检查DS18B20是否有响应.
3、Reset成功后,再调试测温函数就比较顺利了,首先读ROM ID进行比较,如果相符,后面就不成问题了。 

如果没有示波器,就用软仿真,看延时执行的汇编指令,计算占用的总时间,当初我就是这样完成的。
DS18B20通讯协议对时间要示比较严格,务必细看DataSheet。

使用特权

评论回复
19
beanandpeach| | 2012-10-29 23:58 | 只看该作者
good

使用特权

评论回复
20
zywwwyz| | 2013-3-20 23:03 | 只看该作者
受教了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

284

主题

6411

帖子

16

粉丝