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?
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:
Use CRC-8 and take only the 4 lowest bits from the result.
Use CRC-4 with only the 4 lowest bits of each databyte and ignore the higest 4 bits.
Use CRC-4 no the 4 lowest and the 4 highest bits of each databyte.
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:
Use CRC-8 and take only the 4 lowest bits from the result.
Use CRC-4 with only the 4 lowest bits of each databyte and ignore the higest 4 bits.
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.
@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
// 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);
}
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
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.
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.