Shop OBEX P1 Docs P2 Docs Learn Events
Can someone help me understand this crc example. — Parallax Forums

Can someone help me understand this crc example.

T ChapT Chap Posts: 4,223
edited 2014-05-08 16:57 in Propeller 1
Hey all, can someone help me interpret this example (unknown language?) that is used for crc8? I have been reading on the use of crc8 but still can't understand how to make use of the example.

Page 13 of the app note shows the example.

http://www.omron.com/ecb/products/sensor/special/mems/pdf/AN-D6T-01EN_r2.pdf


[D6T-44L/D6T-8L]
PEC check routine Example
PEC is the data used for the error checki
ng method using CRC-8. PEC and is appended to the
end of the communication output.


unsigned char calc_crc( unsigned char data )
{
int index;
unsigned char temp;
for(index=0;index<8;index++){
temp = data;
data <<= 1;
if(temp & 0x80) data ^= 0x07;
}
return data;
}
int D6T_checkPEC( char buf , int pPEC );
{
unsigned char crc;
int i;
crc = calc_crc( 0x14 );
crc = calc_crc( 0x4C ^ crc );
crc = calc_crc( 0x15 ^ crc );
for(i=0;i<pPEC;i++){
crc = calc_crc( readbuff[i] ^ crc );
}
return (crc == readbuff[pPEC]);
}
Other case : Using Stop-Start conditi
on without Repeat Start Condition,
int D6T_checkPEC( char buf , int pPEC );
{
unsigned char crc;
int i;
crc = calc_crc( 0x15 );
for(i=0;i<pPEC;i++){
crc = calc_crc( readbuff[i] ^ crc );
}
return (crc == readbuff[pPEC]);
} 

Comments

  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-03-24 23:35
    Here's a CRC8 method in Spin that I've used a couple times -- maybe this will help.
    pub calc_crc8(p_buf, len) | crc, b
    
    '' Returns CRC8 of len bytes at p_buf
    
      crc := 0
      
      repeat len
        b := byte[p_buf++]
        repeat 8
          if ((crc ^ b) & 1)
            crc := (crc >> 1) ^ $8C
          else
            crc >>= 1
          b >>= 1  
    
      return crc
    
  • T ChapT Chap Posts: 4,223
    edited 2014-05-05 18:10
    Thanks for the example Jon. I tried to plug this in but got nowhere. The examples shown in the app note for the Omron D6T suggests that they are including the I2C address for write, the I2C command, the I2C address for read in the crc computation:
    unsigned char calc_crc( unsigned char data )
    {
    int index;
    unsigned char temp;
    for(index=0;index<8;index++){
    temp = data;
    data <<= 1;
    if(temp & 0x80) data ^= 0x07;
    }
    return data;
    }
    int D6T_checkPEC( char buf , int pPEC );
    {
    unsigned char crc;
    int i;
    crc = calc_crc( 0x14 );    'i2c write address
    crc = calc_crc( 0x4C ^ crc );   'i2c command to tell the device we are going to read the data out
    crc = calc_crc( 0x15 ^ crc );   'i2c read address
    for(i=0;i<pPEC;i++){                 'pec is the crc value, last byte
    crc = calc_crc( readbuff[i] ^ crc );
    }
    return (crc == readbuff[pPEC]);                
    }
    
    The devices have either 11 bytes or 35 bytes of total data depending on the device type(including the final byte as the crc value). What I don't get is why are they also including I2C address and commands in the crc? What does the i2c address have to do with the returned data? Well since it appears that it does include I2C addresses, what I am trying to do is interpret this into SPIN. Any suggestions welcome.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-06 01:02
    I looked at online examples for the SMBus, and translated this table-based version from a PIC forum. Also included at the end is a direct translation of the bit shift version as I see it. A quick spot check and the bit shifts do match with the table, and either one should work. Last words, though, I haven't had a chance to try it out with the D6T yet.
    [SIZE=1][FONT=courier new]' CRC8 for SMBUS
    ' table from http://www.picbasic.co.uk/forum/showthread.php?t=6231
    ' YMMV
    
    PUB CrcD6tCalc(dataPointer) : crc
    ' enter with data pointer to the 34 data bytes, that is, ptat plus 16 sensors at 2 bytes each.
      crc := 0
      crc := CalcCrc($14,crc)  ' they want to include the i2c header
      crc := CalcCrc($4c,crc)  ' that we sent, we assume it is correct
      crc := CalcCrc($15,crc)  ' 
      Repeat 34                ' this is the data to check
        CalcCRC(dataPointer++,crc)
    
    PRI CalcCrc(datum, crc)
      return crcTable[datum ^ crc]
    
    DAT
    crcTable  byte $0,  $7,  $E,  $9,  $1C, $1B, $12, $15, $38, $3F, $36, $31,{
    }              $24, $23, $2A, $2D, $70, $77, $7E, $79, $6C, $6B, $62, $65,{
    }              $48, $4F, $46, $41, $54, $53, $5A, $5D, $E0, $E7, $EE, $E9,{
    }              $FC, $FB, $F2, $F5, $D8, $DF, $D6, $D1, $C4, $C3, $CA, $CD,{
    }              $90, $97, $9E, $99, $8C, $8B, $82, $85, $A8, $AF, $A6, $A1,{
    }              $B4, $B3, $BA, $BD, $C7, $C0, $C9, $CE, $DB, $DC, $D5, $D2,{
    }              $FF, $F8, $F1, $F6, $E3, $E4, $ED, $EA, $B7, $B0, $B9, $BE,{
    }              $AB, $AC, $A5, $A2, $8F, $88, $81, $86, $93, $94, $9D, $9A,{
    }              $27, $20, $29, $2E, $3B, $3C, $35, $32, $1F, $18, $11, $16,{
    }              $3,  $4,  $D,  $A,  $57, $50, $59, $5E, $4B, $4C, $45, $42,{
    }              $6F, $68, $61, $66, $73, $74, $7D, $7A, $89, $8E, $87, $80,{
    }              $95, $92, $9B, $9C, $B1, $B6, $BF, $B8, $AD, $AA, $A3, $A4,{
    }              $F9, $FE, $F7, $F0, $E5, $E2, $EB, $EC, $C1, $C6, $CF, $C8,{
    }              $DD, $DA, $D3, $D4, $69, $6E, $67, $60, $75, $72, $7B, $7C,{
    }              $51, $56, $5F, $58, $4D, $4A, $43, $44, $19, $1E, $17, $10,{
    }              $5,  $2,  $B,  $C,  $21, $26, $2F, $28, $3D, $3A, $33, $34,{
    }              $4E, $49, $40, $47, $52, $55, $5C, $5B, $76, $71, $78, $7F,{
    }              $6A, $6D, $64, $63, $3E, $39, $30, $37, $22, $25, $2C, $2B,{
    }              $6,  $1,  $8,  $F,  $1A, $1D, $14, $13, $AE, $A9, $A0, $A7,{
    }              $B2, $B5, $BC, $BB, $96, $91, $98, $9F, $8A, $8D, $84, $83,{
    }              $DE, $D9, $D0, $D7, $C2, $C5, $CC, $CB, $E6, $E1, $E8, $EF,{
    }              $FA, $FD, $F4,$F3
    
    PRI CalcCrc2(datum, crc) | temp
    ' this is the same thing as the table, but by calculation.
      repeat 8
        temp := datum
        datum <<= 1
        if temp & $80
          datum ^= $07
      return datum ^ crc[/FONT][/SIZE]
    
  • T ChapT Chap Posts: 4,223
    edited 2014-05-06 11:09
    Thanks Tracy. I modified your version slightly, they are not using any XOR on the first I2C value ( $14) in their example. I also made things a little simpler looking for my own purposes. Still no luck. I am curious about their use of unsigned char, which after reading about that type of value, it has some unique properties compared to a typical integer. I have tested with var byte crc and also with crc as a local var (long). From what I can tell, this is exactly what they are doing in the example with the exception of understanding the unsigned char crc. When using crc as a byte declared in var, it becomes a 2 or 3 digit number(obviously), much closer to the 2 or 3 digit actual crc value, but still never the same number.

    Wiki: Char smallest addressable unit of the machine that can contain basic character set. It is an integer type. Actual type can be either signed or unsigned depending on the implementation.

    Unsigned char same size as char, but guaranteed to be unsigned.


    PUB CRCCalc(dataPointer) | q , crc
      crc := 0
      crc := Calc($14)  ' they want to include the i2c header
      crc := Calc($4c ^ crc )  ' that we sent, we assume it is correct
      crc := Calc($15 ^ crc ) 
      go0   ' lcd position
      ser.decf(3, omron[34] ,5)  ' show the received crc value  
      q~
      Repeat 34                ' this is the data to check
        crc := Calc(dataPointer[q] ^ crc)
        q++
      go(1,0)    ' lcd position
      ser.decf(3, crc ,9)     ' show the computed crc
      w(40_000_000)           'pause to see the comparison
    
    
    PUB Calc(datam) | tempp
      repeat 8
        tempp := datam
        datam <<= 1
        if tempp & $80
          datam ^= $07
      return datam              '^ crc
    
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-05-06 12:09
    It looks like the Spin code should be doing the same thing as the C code. They do have a couple of syntax errors in the C code, so it might not be quite right. Can you post some samples of the data you received, and maybe we can figure out how the CRC was calculated?
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-06 12:28
    There's an SMBus calculator online at <http://smbus.org/faq/crc8Applet.htm>. Drat, my browser tells me I am missing the plugin. Does it work for anyone? That might be good for comparison.

    The result should be a single byte, masked, crc &= $000000FF.
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-05-06 12:34
    I tried the calculator, and I got a CRC of DF for the 13-byte sequence 14 4c 15 03 0e 0f 5c 41 23 59 4f 20 26. I get the same value with the C code also.
  • T ChapT Chap Posts: 4,223
    edited 2014-05-06 15:41
    144c15340118011a011e012101110115010d0110010801fd00fa00fd000601f700f800fa00 = 9E

    This is a sample from the Omron device. The crc byte is decimal 175(AF). The applet shows 9E as the result based on the above. So, something is not adding up based on the Omron example in C based on Repeated start.

    The Omron app note shows an example WITHOUT using Repeated start and it excludes the first 2 bytes 14 and 4C, and only uses 15 ( I2C address + read ).

    15340118011a011e012101110115010d0110010801fd00fa00fd000601f700f800fa00 = AF

    This does work on the applet. I thought I had tested this configuration earlier on the Propeller with the code shown and did not get the same answer. I will test again tomorrow now that I see what the formula really should be. I was testing with CRC as a long(local var) without any mask. I also tested with CRC as a byte, no masking obviously.

    Thanks for the suggestions.

    I will test this tomorrow:
    PUB CRCCalc(dataPointer) | q , crc
      crc := 0
      'crc := Calc($14)  ' they want to include the i2c header
      'crc := Calc($4c ^ crc )  ' that we sent, we assume it is correct
      crc := Calc($15 ^ crc ) 
      go0   ' lcd position
      ser.decf(3, omron[34], 3)  ' show the received crc value
      q~
      Repeat 34                ' this is the data to check
        crc := Calc(dataPointer[q] ^ crc)
        q++
      crc &= $000000FF. 
      go(1,0)    ' lcd position
      ser.decf(3, crc, 3)     ' show the computed crc
      w(40_000_000)           'pause to see the comparison
    
    
    
    PUB Calc(datam) | tempp
      repeat 8
        tempp := datam
        datam <<= 1
        if tempp & $80
          datam ^= $07
      return datam      '^ crc
    
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-05-06 16:21
    I get the same results as you. I get a CRC of 9E for all 37 bytes, and AF starting from the third byte.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-06 16:49
    The attached table method with demo gives the same answer as the java applet for the data in Dave's example, header (144c15) + 10 bytes data...
    030e0f5c4123594f2026
    crc = $df

    also the full for the full packet in your example. header (144c15) + 34 bytes data...
    144c15340118011a011e012101110115010d0110010801fd00 fa00fd000601f700f800fa00
    crc = $9e

    That will indeed be puzzling if the D6T kicks out something different.

    I'm not having joy with the bit-shift calculation. I'm still trying to make it come out the same as the table version. The table version has the advantage of being fast, the disadvantage of being big.
  • T ChapT Chap Posts: 4,223
    edited 2014-05-06 17:05
    Tracy, the app note shows all integers declared as unsigned char's. Temp, datum, and crc in your bitshift calc are all using longs. I wonder if these should be replaced with byte var's.
     crc = calc_crc( 0x15 );
    for(i=0;i<pPEC;i++){
    crc = calc_crc( readbuff[i] ^ crc ); 
    

    I think this is the correct example, and I just noticed that in this example that are not using XOR with CRC with $15, I never tried that combo today. I always had $15 ^ CRC even when I removed 14 and 4c.
  • Dave HeinDave Hein Posts: 6,347
    edited 2014-05-06 18:16
    An unsigned char in C is the same as a byte in Spin. Since data the values are shifted to the left it doesn't matter whether an unsigned char or an int are used, as long as the final result is anded with 0xFF.

    The first value isn't XOR'ed because the value of crc is assumed to be zero at the beginning. I believe you initialize crc to zero, so it doesn't matter whether you XOR the first value or not.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-06 18:32
    Okay, here is a bit shift version now working. Duh! I had left out an all important repeat. This now gives the same results as the applet on the practice data.

    Also attached, a version of the table version written without special treatment for the i2c header. Easier to test arbitrary strings. The table version and the bit-shift version give the same results, the former is quick but lots of memory for the table, the latter is a slow bitwise computation but easy on memory usage.

    There are no carries in crc math and no effect from any of the bits that get shifted into a long. Just mask off the low byte at every step or at the end, it doesn't matter. What Dave said.
  • T ChapT Chap Posts: 4,223
    edited 2014-05-06 18:47
    Awesome! I will test this with the sensor in the morning.
  • T ChapT Chap Posts: 4,223
    edited 2014-05-07 08:25
    Well some slight progress.

    Randomly, about 30% are accurate comparisons with the crc value, doesn't matter which version shift/table using
    $15 + 34 bytes of data. (scan 35 bytes)

    I am clearly using repeated start, but their example for repeated start shows the additional i2c values at the top which we have removed from the test.

    About 30% are accurate comparisons with the crc value, using the starting value as $14, $4c, $15 + 34 bytes of data. (scan 37 bytes)

    It seems the crc values coming back are flipping back and forth randomly as if it thinks it is doing a stop/start method OR repeated start. It is completely random which method of calculation it is returning. The sensor may be confused thinking I am sending a repeated start on a loop, and a start sop method on another loop. Otherwise, the crc calculation is working on each read, depending on which method you need to apply to it's response.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-07 14:28
    I've got the D6T hooked up and giving pretty much 100% good response with the crc. Here is a trace of data taken at 1 second intervals. The data returned from the D6T is the first line in each group (happens to start with 3E), and the 35th byte on that line is the device crc8. The second line is the trace while calculating the crc8 locally, and the third line just is showing the match (or not) between the two crcs. The final step I took to get rid of the FF lines was to operate the Prop at clkfreq=10MHz.
    [FONT=courier new][COLOR=#020FC0][SIZE=1]demo of D6T, reading temperatures.
    device found
    3E 02 58 02 5B 02 5C 02 61 02 54 02 53 02 57 02 5B 02 53 02 51 02 53 02 57 02 59 02 54 02 54 02 58 02 A0
    AC 43 41 CE E2 AE D0 30 B0 17 CE 6A AF 4A 53 B0 9F DA B6 05 AB 56 1B 4F 48 F1 51 BE 98 CF C8 78 E0 A0
    A0A0
    3E 02 58 02 5B 02 5C 02 61 02 54 02 53 02 56 02 5B 02 52 02 51 02 53 02 57 02 59 02 54 02 54 02 59 02 9B
    AC 43 41 CE E2 AE D0 30 B0 17 CE 6A AF 4A 54 A5 F4 CC D3 39 1F 53 00 0E 88 BF BC 33 32 90 52 B7 84 9B
    9B9B
    3E 02 58 02 5A 02 5B 02 61 02 54 02 53 02 56 02 5B 02 52 02 50 02 53 02 57 02 59 02 54 02 54 02 58 02 89
    AC 43 41 CE E5 BB AE 4D C4 5C 38 A6 C5 5B 23 E7 3D BD 83 8E 14 62 97 E2 02 00 88 BF 9F DA A3 6E 82 89
    8989
    3E 02 57 02 5A 02 5B 02 61 02 54 02 53 02 56 02 5A 02 52 02 50 02 52 02 57 02 59 02 54 02 54 02 58 02 C2
    AC 43 6C 0D A2 69 9E DD 3D BD 91 F0 60 29 7A 6F 8B B6 B2 19 F8 E8 2F C3 E5 BB A0 67 99 C8 DD 13 F6 C2
    C2C2
    3E 02 58 02 5B 02 5C 02 61 02 54 02 53 02 56 02 5B 02 52 02 50 02 53 02 57 02 59 02 53 02 54 02 58 02 B4
    AC 43 41 CE E2 AE D0 30 B0 17 CE 6A AF 4A 54 A5 F4 CC D3 39 18 46 6B 18 EA 96 63 20 5E 93 5B 88 3E B4
    B4B4
    3E 02 57 02 5A 02 5B 02 5F 02 53 02 52 02 55 02 5A 02 51 02 50 02 51 02 56 02 58 02 53 02 53 02 58 02 28
    AC 43 6C 0D A2 69 9E DD 87 92 49 F6 75 42 65 32 1F 53 0E 24 4B F8 56 AB FD F3 58 81 30 9E 6D 0A B9 28
    2828[/SIZE][/COLOR][/FONT]
    

    The match as you noticed comes from starting at the $15. I think that is because the driver is not simply generating a repeated start; it is generating a stop/start combination.
    [SIZE=1][FONT=courier new]
        crc := Crc8Calc(@array, 34, $6b)  ' 34 bytes, starting with crc=$6b
    
    PUB Crc8Calc(dataPointer, dataCount, crc)
    ' enter with data pointer to data bytes, number of bytes, starting crc
      uarts.tx(0,13)   ' going to show a trace of crc calc
      Repeat dataCount
        crc := Crc8shift(byte[datapointer++] ^ crc)
        uarts.hex(0,crc,2)
        uarts.char(0,32)
      return crc
    
    PUB crc8shift(datum) | temp
      repeat 8
        datum <<= 1
        if datum & $100
            datum ^= $07
      return datum & $ff
    [/FONT][/SIZE]
    
  • T ChapT Chap Posts: 4,223
    edited 2014-05-07 15:00
    Tracy are you using the same I2C method and driver I posted? I realize that my method may not match the C example code, but that is all I can get to produce any response at all.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-07 16:30
    No, I'm using my own i2c object, which also has its roots in Mike's Basic_I2C_Driver. It manipulates dira[..] only, never outa[..], so it has to have pullups, but it can surely detect clock stretching. I patched clock stretching onto this version at every 0->1 transition of scl, probably overkill. I was still seeng those FF'ing packets from time to time, which went away completely only when I had the clock down to clkfreq=10MHz. This device definitely has a timing issue.

    Attaching an archive of my current code.
  • T ChapT Chap Posts: 4,223
    edited 2014-05-07 17:58
    Thanks for posting that code. I made some tweaks and am seeing an error about 1 in 1000 or less. I put the clock back to 80meg, and put a bunch of waits in the i2c code. I made the main loop go faster to scan a lot of samples. This is going to work really well for my purposes of a targeted area of presence detection. Your assistance is highly appreciated.

    PS look in CON in the I2C object, raise del to get rid of any remaining errors. I forgot to edit the file name under OBJ.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-08 12:49
    I tried your modified version with the slowed down I2C, and it works well. I added code to count the number of crc errors and also the the number of times it hit the clock stretching routine. With your delay parameter set as you had it at 3500 ticks, I measured an scl clock frequency of 5kHz, and it kicked out 0.7% of packets with crc errors in 1000 samples. Increasing the dev parameter to 7000 ticks gave an scl clock frequency of 4kHz, then it had 0.0% of packets with crc errors after 3000 samples. As for hits on the clock stretching, there was only one at either delay setting, and that one came at the very first packet.


    I also tried the same test with the I2C object not slowed down. That is the one where I got to 0% error rate only when I reduced the clkfreq to 10MHz. At that point, the scl clock was running at 2.5kHz, and it asked for a clock hold only once, on the very first packet. Here is a summary at other clock rates. The numbers vary slightly from run to run.
    clkfreq=80MHz scl frequency=20kHz, packet crc error rate=41%, hits on clock stretch=179
    clkfreq=40MHz scl frequency=10kHz, packet crc error rate=39%, hits on clock stretch=49
    clkfreq=20MHz scl frequency=5kHz, packet crc error rate=4%, hits on clock stretch=1
    clkfreq=10MHz scl frequency=2.5kHz, packet crc error rate=0%, hits on clock stretch=1 never saw an error.

    Funny, because the device is supposed to work at the original i2c scl speed, 100kHz. So why do we have to put it down to less than 5kHz? It's not a big problem, speed is not necessary, and the crc allows bad reads to be thrown away and retried. The data sheet does say that new data can be retrieved about 4 times per second. I tried interrogation at 1/2 second intervals, but that did not make a difference in the error rate.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-05-08 16:46
    Okay, I think I got it. The D6T wants to see a true Sr (repeated start) at the middle of the read command, not the stop/start combination that the i2c driver was providing. With that change, it works perfectly with the original driver+stretching at Spin speed scl at 20kHz. The crc now includes the $14,$4c,$15, so it has to be initialized to $c5 instead of $6b.

    The clock stretching comes into play at several points sporadically in the read sequence, during and after the Sr. The stretching events in this demo are counted and displayed. Also, the stretching is done with waitpne(), so it is not protected by a timeout.
  • T ChapT Chap Posts: 4,223
    edited 2014-05-08 16:57
    That's good news. There is something about this Omron that seems to bring out the OCD. I was very pleased with the results earlier today, and when you consider you can only get 4 reads per second, the speed really doesn''t matter like you said. I will check out your new discoveries tomorrow AM. I had already been trying with some versions of my code to create the true Sr based on the app note examples, and assumed I was doing so, but got no response at all. It will be nice to wrap this up tomorrow and get some boards made with a dedicated Prop controller.

    Edit. Oh, I can see that this actually required the object to be tweaked, good eye.
Sign In or Register to comment.