The minimum information required to create a CRC calculator?

Hello Everyone,

I am checking the datasheet of an acceleration sensor. In the page 17 there is an instruction how to calculate CRC4 value to validate SPI messages, e.g. polynomial of x^4+1 and seed value of 0b1010 is given and the calculation order is MSB to LSB. To my knowledge there should be some more information, like whether the output is XORed or reflected, CRC is reflected, residue etc.

How would you calculate the CRC4 for this sensor? Are there common standard definitions that could be used in this case?

Thanks!
Adam

That is enough information. The 4 bits is weird, it is often CRC-8 or CRC-16 or CRC-32.
Here you can see a few examples that is already in the standard library: avr-libc: <util/crc16.h>: CRC Computations.

Do you have a few datasets from the sensor ? to make a sketch with a CRC-4 and to be able to confirm that it is right.

I don't understand how it is calculated. I think these are the options:

  1. Use CRC-8 and take only the 4 lowest bits from the result.
  2. Use CRC-4 with only the 4 lowest bits of each databyte and ignore the higest 4 bits.
  3. Use CRC-4 no the 4 lowest and the 4 highest bits of each databyte.

I've used this CRC-4 code for a pressure sensor:

// C Code example for CRC-4 calculation:
// MS5837-30BA pressure sensor

unsigned char crc4(unsigned int n_prom[]) // n_prom defined as 8x unsigned int (n_prom[8])
{
    int cnt; // simple counter
    unsigned int n_rem = 0; // crc remainder
    unsigned char n_bit;

    n_prom[0] = ((n_prom[0]) & 0x0FFF); // CRC byte is replaced by 0
    n_prom[7] = 0; // Subsidiary value, set to 0
    for (cnt = 0; cnt < 16; cnt++) // operation is performed on bytes
    { // choose LSB or MSB
    if (cnt%2==1) n_rem ^= (unsigned short) ((n_prom[cnt>>1]) & 0x00FF);
    else n_rem ^= (unsigned short) (n_prom[cnt>>1]>>8);
        for (n_bit = 8; n_bit > 0; n_bit--)
            {
            if (n_rem & (0x8000)) n_rem = (n_rem << 1) ^ 0x3000;
            else n_rem = (n_rem << 1);
            }
    }
    n_rem = ((n_rem >> 12) & 0x000F); // final 4-bit remainder is CRC code
    return (n_rem ^ 0x00); //final XOR code set to zero
}

Normally they’d supply a worked example. As they haven’t, I suppose capture a message and fiddle with with the algorithm until you get it to work.

Koepel:
That is enough information. The 4 bits is weird, it is often CRC-8 or CRC-16 or CRC-32.
Here you can see a few examples that is already in the standard library: avr-libc: <util/crc16.h>: CRC Computations.

Do you have a few datasets from the sensor ? to make a sketch with a CRC-4 and to be able to confirm that it is right.

I don't understand how it is calculated. I think these are the options:

  1. Use CRC-8 and take only the 4 lowest bits from the result.
  2. Use CRC-4 with only the 4 lowest bits of each databyte and ignore the higest 4 bits.
  3. Use CRC-4 no the 4 lowest and the 4 highest bits of each databyte.

I haven't started to program the code yet, but I am thinking about doing so.

pcbbc:
Normally they’d supply a worked example. As they haven’t, I suppose capture a message and fiddle with with the algorithm until you get it to work.

Maybe I need to write them. :smiley:

Page 10. Crappy, but it's something...

@westfw, thank you, I was looking for a Application Note.

Looking at it, I think it is a nibble-wise XOR of all bits when the seed is copied into the lowest nibble of the data.
I think that is the same as a byte-wise XOR and then a XOR of both nibbles of the result.
It feels weird, so I hope to get some feedback :cold_sweat:

// Test sketch, without initializing the SPI

#include <SPI.h>

void setup()
{
  Serial.begin( 9600);
  Serial.println( "---------Test sketch---------");

  // The sensor uses 32 bit.
  // The Arduino Uno puts the lowest byte at the lowest memory address.
  uint32_t cmsData;

  // test if the lowest byte is at the lowest memory location
  cmsData = 0x000000A5;  // set lowest byte

  byte *p = (byte *) &cmsData;
  if( *p == 0xA5)
  {
    Serial.println( "Okay, the lowest byte is at the lowest memory location");
  }
  else
  {
    Serial.println( "Error, is the lowest byte not in the lowest memory location ?");
  }

  // cmsData = getCmsData();
  
  // cmsData = 0x00000000;          // test data
  // cmsData = 0xFFFFFFFF;          // test data
  // cmsData = 0x1EE12205;          // test data
  cmsData = 0x1234ABC0;          // test data

  Serial.print( "cmsData = ");
  printCmsData( cmsData);           // print it in HEX format
  Serial.print( ", crc = ");
  Serial.print( "0x0");             // the CRC will be in the lowest nibble
  Serial.print( cmsCRC( cmsData), HEX);
  Serial.println();
}

void loop()
{
}

uint32_t getCmsData()
{
  // The Arduino SPI can do 16 bits, but not 32 bits, so 8 bits is used.
  // Get 4 bytes, the higest byte is the first one from the sensor.

  uint8_t rxData[4];
  uint8_t txData = 0;

  for( int i=3; i>=0; i--)
  {
    rxData[i] = SPI.transfer( txData);
  }

  // The first and highest byte is in rxData[3].
  // The last and lowest byte is in rxData[0].
  // That is already the right order for the 32 bits variable
  uint32_t *p = (uint32_t *) rxData;
  return( *p);
}

void printCmsData( uint32_t data)
{
  char buffer[20];

  Serial.print( "0x");
  ultoa( data, buffer, 16);
  for( int i=0; i<(int)(8-strlen(buffer)); i++)
  {
    Serial.print( "0");
  }
  Serial.print( buffer);
}

byte cmsCRC( uint32_t data)
{
  // Remove the lowest four CRC bits and put the seed 0b00001010 (0x0A) in it.
  data &= 0xFFFFFFF0;   // clear lowest 4 bits
  data |= 0x0000000A;   // set seed bits
  
  byte x;
  byte *p = (byte *) &data;

  // XOR all bytes
  x = p[0] ^ p[1] ^ p[2] ^ p[3]; 

  // XOR high nibble with low nibble
  byte highNibble = x >> 4;
  byte lowNibble  = x & 0x0F;

  byte crc = highNibble ^ lowNibble;
  crc &= 0x0F;

  return( crc);
}

westfw:
https://www.siliconsensing.com/media/30750/orion-eval-boards-draft-2.pdf
Page 10. Crappy, but it's something...

It is already something.
Basically I thought I understood how a CRC is calculated. Apparently there are more tricks.

Koepel:
@westfw, thank you, I was looking for a Application Note.

Looking at it, I think it is a nibble-wise XOR of all bits when the seed is copied into the lowest nibble of the data.
I think that is the same as a byte-wise XOR and then a XOR of both nibbles of the result.
It feels weird, so I hope to get some feedback :cold_sweat:

Thank you Koepel, for putting this code together! Although my knowledge in C++ is a bit weak, I will try to implement it. So far the results of the C++ console application are different when I check the calculation manually. I hope that my family will allow me to play with my little jewel tomorrow, so I can implement it directly.

P.S. I am not sure if I will be able to do it, but creating a lookup table would be also great.

Hello Everyone,

I haven't got around to creating a lookup table yet, but Koepel's code indeed worked. I can communicate with the sensor.

I'll share my experience once I've tried more.

P.S. Just out of curiosity I tried to calculate CRC with the following function, but it didn't work. I wonder why.

char myCRC(uint32_t data)
{
    data &= 0xFFFFFFF0;   // clear lowest 4 bits
    data |= 0x0000000A;   // set seed bits
    char mycrc = 0;
    mycrc = (data >> 28) ^ (data >> 24) ^ (data >> 20) ^ (data >> 16) ^ (data >> 12) ^ (data >> 8) ^ (data >> 4);
    mycrc &= 0x0F;
    return mycrc;
}

Haha, so close.

Did you forget the last four bits ^data?

a7

Because it throws away the seed value initialised into the low 4 bits?

char myCRC(uint32_t data)
{
    data &= 0xFFFFFFF0;   // clear lowest 4 bits
    data |= 0x0000000A;   // set seed bits
    char mycrc = 0;
    mycrc = (data >> 28) ^ (data >> 24) ^ (data >> 20) ^ (data >> 16) ^ (data >> 12) ^ (data >> 8) ^ (data >> 4) ^ data;
    mycrc &= 0x0F;
    return mycrc;
}

The code presented by Koepel in post #6 is probably going to be far more efficient. Although it's possible the compiler might have a good optimisation for all those non-byte aligned shifts.

This is nothing more than 4 independent parity bits over each nibble of the message. 2 bits with odd, 2 bits with even parity. Granted 1 bit of parity is a 1 bit CRC, but usually there's some feedback between bits in multi-bit CRCs.

It accidentally worked ? :o
I used what they wrote in the datasheet, but it didn't feel like a standard CRC.