Go Down

Topic: CRC for Dallas 1-Wire (Read 13973 times) previous topic - next topic

mkjackson

It can be tricky to interpret Dallas 1-Wire CRCs. The following shows how to do the 16-bit CRC for data, and also includes usage of the 8-bit CRC. The main things I learned are
  • the Dallas chips (at least the DS2450 I'm using) inverts the 16-bit CRC data before transmitting it, and puts the bytes in opposite order to what I expected. The bit-inversion and why it's a good idea are mentioned in the Maxim application note (http://www.maxim-ic.com/app-notes/index.mvp/id/27) but not in the chip documentation.
  • There's a neat trick where you compute the CRC on the transmitted message and transmitted CRC and you should always get the same result if there's no transmission error.
Hopefully this will save some headaches :-).

The new 16-bit CRC routine is:
Code: [Select]
/*
 Dallas 1-wire CRC routines for Arduino with examples of usage.
 The 16-bit routine is new.
 The 8-bit routine is from http://github.com/paeaetech/paeae/tree/master/Libraries/ds2482/
 
 Copyright (C) 2010 Kairama Inc

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
// Dallas 1-wire 16-bit CRC calculation. Developed from Maxim Application Note 27.
uint16_t crc16( uint8_t *data, uint8_t len)
{
     uint16_t crc=0;
     
     for (uint8_t i=0; i<len;i++)
     {
           uint8_t inbyte = data[i];
           for (uint8_t j=0;j<8;j++)
           {
                 uint8_t mix = (byte(crc)^ inbyte) & 0x01;
                 crc = crc >> 1;
                 if (mix)
                       crc = crc ^ 0xA001;
                 
                 inbyte = inbyte >> 1;
           }
     }
     return crc;
}


and the 8-bit version from http://github.com/paeaetech/paeae/tree/master/Libraries/ds2482/ is:
Code: [Select]
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"

uint8_t crc8( uint8_t *addr, uint8_t len)
{
     uint8_t crc=0;
     
     for (uint8_t i=0; i<len;i++)
     {
           uint8_t inbyte = addr[i];
           for (uint8_t j=0;j<8;j++)
           {
                 uint8_t mix = (crc ^ inbyte) & 0x01;
                 crc >>= 1;
                 if (mix)
                       crc ^= 0x8C;
                 
                 inbyte >>= 1;
           }
     }
     return crc;
}


The output from a talkative test routine is:
Code: [Select]
Setup: Maxim 1-wire CRC Examples...

First, 8-bit CRC. We use a valid 64-bit ROM+CRC address
ROM code with CRC 0xFF 0x00 0x07 0x5F 0x07 0xB7 0x00 0x68
For the 7-byte ROM code alone, computed 8-bit CRC is 0x37
This should equal the correct value of 0x37

Another approach is to compute the CRC of the message&transmitted CRC.
The resulting 8-bit CRC is 0x00
This should always be 0 if there was no error in transmission.
----------
Now the 16-bit CRC, using a valid 7-byte sequence where the CRC is known
Data=0xAA 0x06 0x00 0x00 0x00
The CRC as transmitted by a DS2450 is 0xE7 0x6F

For message alone, computed CRC is 0x9018
The two are not equal because the DS2450 transmits the complement of the CRC!
Inverting what the DS2450 sends results in: 0x18 0x90
Which is almost correct, but the bytes are reversed
Inverting what the DS2450 sends and swapping byte order results in: 0x90 0x18
Which finally agrees with the computed CRC.

Another approach is to compute the CRC of the message&transmitted CRC
For message plus CRC, result is =0xB001
This should always equal 0xB001 if there was no error in transmission
==========================================


The code to generate this is:
Code: [Select]
uint8_t TempSensA[8] = {0x10,0x50,0xA9,0x0A,0x02,0x08,0x00,0x37}; // DS18S20

void setup()
{
       Serial.begin(9600);        // set up Serial library at 9600 bps
       Serial.print("Setup: Maxim 1-wire CRC Examples...\n\n");
}

void loop()
{
     uint8_t Data[10], length, CRCByte, i, CRCCorrect[2], CRCDS2450[2];
       uint16_t CRCWord;

       Serial.print("First, 8-bit CRC. We use a valid 64-bit ROM+CRC address\n");

       length=8;
       Serial.print("ROM code with CRC "); PrintHex8(Data,length); Serial.print("\n");

       length=7; // this time just the data bits
       CRCByte=crc8(TempSensA,length);
       Serial.print("For the 7-byte ROM code alone, computed 8-bit CRC is ");
       PrintHex8(&CRCByte,1); Serial.print("\n");
       Serial.print("This should equal the correct value of ");
       PrintHex8(&TempSensA[7],1); Serial.print("\n\n");

       length=8;
       CRCByte=crc8(TempSensA,length); // this time include the CRC
       Serial.print("Another approach is to compute the CRC of the message&transmitted CRC.\n");
       Serial.print("The resulting 8-bit CRC is "); PrintHex8(&CRCByte,1); Serial.print("\n");
       Serial.print("This should always be 0 if there was no error in transmission.\n");
       Serial.print("----------\n");

       Serial.print("Now the 16-bit CRC, using a valid 7-byte sequence where the CRC is known\n");
       Data[0]=0xAA; Data[1]=0x06; Data[2]=0x00; Data[3]=0x00; Data[4]=0x00; Data[5]=0xE7; Data[6]=0x6F;
       CRCDS2450[0]=Data[5]; CRCDS2450[1]=Data[6];

       length=5;
       Serial.print("Data="); PrintHex8(Data,length); Serial.print("\n");
       Serial.print("The CRC as transmitted by a DS2450 is "); PrintHex8(CRCDS2450,2); Serial.print("\n\n");
       
       CRCWord=crc16(Data,length);
       Serial.print("For message alone, computed CRC is "); PrintHex16(&CRCWord, 1); Serial.print("\n");
       Serial.print("The two are not equal because the DS2450 transmits the complement of the CRC!\n");
       
       CRCCorrect[0]=~CRCDS2450[0];
       CRCCorrect[1]=~CRCDS2450[1];
       Serial.print("Inverting what the DS2450 sends results in: "); PrintHex8(CRCCorrect,2); Serial.print("\n");
       Serial.print("Which is almost correct, but the bytes are reversed\n");

       CRCCorrect[0]=~CRCDS2450[1];
       CRCCorrect[1]=~CRCDS2450[0];      
       Serial.print("Inverting what the DS2450 sends and swapping byte order results in: ");
       PrintHex8(CRCCorrect,2); Serial.print("\n");
       Serial.print("Which finally agrees with the computed CRC.\n\n");
       
       length=7;
       CRCWord=crc16(Data,length);

       Serial.print("Another approach is to compute the CRC of the message&transmitted CRC\n");
       Serial.print("For message plus CRC, result is ="); PrintHex16(&CRCWord, 1); Serial.print("\n");
       Serial.print("This should always equal 0xB001 if there was no error in transmission\n");
     
       Serial.print("==========================================\n");    
       delay(10000);
}

Go Up