4
votes

I am using the ModBus RTU, and I'm trying to figure out how to calculate the CRC16. I don't need a code example. I am simply curious about the mechanism. I have learned that a basic CRC is a polynomial division of the data word, which is padded with zeros, depending on the length of the polynomial. The following test example is supposed to check if my basic understanding is correct:

  • data word: 0100 1011
  • polynomial: 1001 (x3+1)
  • padded by 3 bits because of highest exponent x3
  • calculation: 0100 1011 000 / 1001 -> remainder: 011

Calculation.

01001011000
 1001
 0000011000
      1001
      01010
       1001
       0011 

Edit1: So far verified by Mark Adler in previous comments/answers.

Searching for an answer I have seen a lot of different approaches with reversing, dependence on little or big endian, etc., which alter the outcome from the given 011.

Modbus RTU CRC16

Of course I would love to understand how different versions of CRCs work, but my main interest is to simply understand what mechanism is applied here. So far I know:

  • x16+x15+x2+1 is the polynomial: 0x18005 or 0b11000000000000101
  • initial value is 0xFFFF
  • example message in hex: 01 10 C0 03 00 01
  • CRC16 of above message in hex: C9CD

I did calculate this manually like the example above, but I'd rather not write this down in binary in this question. I presume my transformation into binary is correct. What I don't know is how to incorporate the initial value -- is it used to pad the data word with it instead of zeros? Or do I need to reverse the answer? Something else?

  • 1st attempt: Padding by 16 bits with zeros. Calculated remainder in binary would be 1111 1111 1001 1011 which is FF9B in hex and incorrect for CrC16/Modbus, but correct for CRC16/Bypass

  • 2nd attempt: Padding by 16 bits with ones, due to initial value. Calculated remainder in binary would be 0000 0000 0110 0100 which is 0064 in hex and incorrect.

It would be great if someone could explain, or clarify my assumptions. I honestly did spent many hours searching for an answer, but every explanation is based on code examples in C/C++ or others, which I don't understand. Thanks in advance.

EDIT1: According to this site, "1st attempt" points to another CRC16-method with same polynomial but a different initial value (0x0000), which tells me, the calculation should be correct. crccalc How do I incorporate the initial value?

EDIT2: Mark Adlers Answer does the trick. However, now that I can compute CRC16/Modbus there are some questions left for clearification. Not needed but appreciated.

A) The order of computation would be: ... ?

  • 1st applying RefIn for complete input (including padded bits)
  • 2nd xor InitValue with (in CRC16) for the first 16 bits
  • 3rd applying RefOut for complete output/remainder (remainder maximum 16 bits in CRC16)

B) Referring to RefIn and RefOut: Is it always reflecting 8 bits for input and all bits for output nonetheless I use CRC8 or CRC16 or CRC32?

C) What do the 3rd (check) and 8th (XorOut) column in the website I am referring to mean? The latter seems rather easy, I am guessing its apllied by computing the value xor after RefOut just like the InitValue?

1

1 Answers

0
votes

Let's take this a step at a time. You now know how to correctly calculate CRC-16/BUYPASS, so we'll start from there.

Let's take a look CRC-16/CCITT-FALSE. That one has an initial value that is not zero, but still has RefIn and RefOut as false, like CRC-16/BUYPASS. To compute the CRC-16/CCITT-FALSE on your data, you exclusive-or the first 16 bits of your data with the Init value of 0xffff. That gives fe ef C0 03 00 01. Now do what you know on that, but with the polynomial 0x11021. You will get what is in the table, 0xb53f.

Now you know how to apply Init. The next step is dealing with RefIn and RefOut being true. We'll use CRC-16/ARC as an example. RefIn means that we reflect the bits in each byte of input. RefOut means that we reflect the bits of the remainder. The input message is then: 80 08 03 c0 00 80. Dividing by the polynomial 0x18005 we get 0xb34b. Now we reflect all of those bits (not in each byte, but all 16 bits), and we get 0xd2cd. That is what you see as the result in the table.

We now have what we need to compute CRC-16/MODBUS, which has both a non-zero Init value (0xffff) and RefIn and RefOut as true. We start with the message with the bits in each byte reflected and the first 16 bits inverted. That is 7f f7 03 c0 00 80. Divide by 0x18005 and you get the remainder 0xb393. Reflect those bits and we get 0xc9cd, the expected result.

The exclusive-or of Init is applied after the reflection, which you can verify using CRC-16/RIELLO in that table.

Answers for added questions:

A) RefIn has nothing to do with the padded bits. You reflect the input bytes. However in a real calculation, you reflect the polynomial instead, which takes care of both reflections.

B) Yes.

C) Yes, XorOut is the what you exclusive-or the final result with. Check is the CRC of the nine bytes "123456789" in ASCII.