Wednesday, April 13, 2016

Function to Calculate a CRC16 Checksum

Leave a Comment

I'm working on a library to provide simple reliable communication over an RS232 or RS485 connection. Part of this code involves using a CRC16 checksum on the data to detect corruption from line noise. I've created a function to calculate a CRC16 checksum, but it doesn't seem to be outputting correct values.

The relevant code I've written can be found here.

I'm checking my output against this online CRC calculator.

I've come to the conclusion that either my understanding of how to calculate a CRC16 is wrong, or the online calculator is wrong (the former seems more likely). Can someone tell me where I might be going wrong?

Thanks.

5 Answers

Answers 1

There are several details you need to 'match up' with for a particular CRC implementation - even using the same polynomial there can be different results because of minor differences in how data bits are handled, using a particular initial value for the CRC (sometimes it's zero, sometimes 0xffff), and/or inverting the bits of the CRC. For example, sometimes one implementation will work from the low order bits of the data bytes up, while sometimes they'll work from the high order bits down (as yours currently does).

Also, you need to 'push out' the last bits of the CRC after you've run all the data bits through.

Keep in mind that CRC algorithms were designed to be implemented in hardware, so some of how bit ordering is handled may not make so much sense from a software point of view.

If you want to match the CRC16 with polynomial 0x8005 as shown on the lammertbies.nl CRC calculator page, you need to make the following changes to your CRC function:

  • a) run the data bits through the CRC loop starting from the least significant bit instead of from the most significant bit
  • b) push the last 16 bits of the CRC out of the CRC register after you've finished with the input data
  • c) reverse the CRC bits (I'm guessing this bit is a carry over from hardware implementations)

So, your function might look like:

uint16_t gen_crc16(const uint8_t *data, uint16_t size) {     uint16_t out = 0;     int bits_read = 0, bit_flag;      /* Sanity check: */     if(data == NULL)         return 0;      while(size > 0)     {         bit_flag = out >> 15;          /* Get next bit: */         out <<= 1;         out |= (*data >> bits_read) & 1; // item a) work from the least significant bits          /* Increment bit counter: */         bits_read++;         if(bits_read > 7)         {             bits_read = 0;             data++;             size--;         }          /* Cycle check: */         if(bit_flag)             out ^= CRC16;      }      // item b) "push out" the last 16 bits     int i;     for (i = 0; i < 16; ++i) {         bit_flag = out >> 15;         out <<= 1;         if(bit_flag)             out ^= CRC16;     }      // item c) reverse the bits     uint16_t crc = 0;     i = 0x8000;     int j = 0x0001;     for (; i != 0; i >>=1, j <<= 1) {         if (i & out) crc |= j;     }      return crc; } 

That function returns 0xbb3d for me when I pass in "123456789".

Answers 2

Here follows a working code to calculate crc16 CCITT. I tested it and the results matched with those provided by http://www.lammertbies.nl/comm/info/crc-calculation.html.

unsigned short crc16(const unsigned char* data_p, unsigned char length){     unsigned char x;     unsigned short crc = 0xFFFF;      while (length--){         x = crc >> 8 ^ *data_p++;         x ^= x>>4;         crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^ ((unsigned short)(x <<5)) ^ ((unsigned short)x);     }     return crc; } 

Answers 3

There are several different varieties of CRC-16. See wiki page.

Every of those will return different results from the same input.

So you must carefully select correct one for your program.

Answers 4

for (pos = 0; pos < len; pos++) { crc ^= (uint16_t)buf[pos];          // XOR byte into least sig. byte of crc  for (i = 8; i != 0; i--) {    // Loop over each bit   if ((crc & 0x0001) != 0) {      // If the LSB is set     crc >>= 1;                    // Shift right and XOR 0xA001     crc ^= CRC16;   }   else                            // Else LSB is not set     crc >>= 1;                    // Just shift right } return crc; 

Answers 5

There is a correct and copy-pastable listing (with examples) of the C++ code for CRC16 on the 18-20 pages in this manual.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment