3
\$\begingroup\$

The data frames that are always sent using CRC are as follows: enter image description here a CRC calculated for each byte (DATA)

But to make the connection faster, could you send a single CRC for a whole frame of data? the first bit of the frame corresponds to the first bit of the CRC and then I send the data frame, and at the end of the frame I send the remaining data of the CRC [7: 1]

enter image description here Is this last option viable? I know that it is not verified byte by byte, but in this way the sending of the entire frame is faster and therefore it is less likely that noise will enter in that period of time that it takes to send the frame? or is it very risky?

\$\endgroup\$
7
  • 12
    \$\begingroup\$ Of course, in Modbus protocol you send 2 CRC bytes at the end of the data sequence, which applies to the whole sequence. Actually, I have never seen CRC for each data byte. \$\endgroup\$ Nov 30, 2021 at 14:22
  • 4
    \$\begingroup\$ Are you confusing parity per byte with crc per frame? \$\endgroup\$
    – Jeroen3
    Nov 30, 2021 at 14:45
  • 1
    \$\begingroup\$ Yes. If any one byte fails, the whole message is garbage. Error correction is different : to protect N bytes of message against any single error takes (about) log(N) bytes of ECC data. Multiple errors is more difficult, and then it may make sense to ECC each byte, or word. \$\endgroup\$ Nov 30, 2021 at 15:19
  • 2
    \$\begingroup\$ a CRC calculated for each byte (DATA). CRC is essentially a checksum of a frame. Fails have sender resend frame. A CRC for every byte essentially means, you are sending the data twice. Makes no sense. And if the connection is reliable, 50% of bandwith is wasted. And some poor embedded controller has been wasted on a custom protocol. There are many comms standards, use one! \$\endgroup\$ Nov 30, 2021 at 20:37
  • 2
    \$\begingroup\$ If you've ever used a CRC library in any programming language you will notice that the function does not accept one byte. They normally accept strings or an array of bytes. CRC is not meant to calculate single bytes. For single bytes you have parity which is usually calculated automatically for you in hardware. \$\endgroup\$
    – slebetman
    Dec 1, 2021 at 4:12

5 Answers 5

14
\$\begingroup\$

It doesn't make sense to calculate CRC per byte. Normal UART protocols tend to be designed like this:

  • 1-2 sync bytes.
  • 1 byte packet size.
  • n bytes of data payload.
  • 2 bytes checksum, CRC-16.

The width of the checksum/CRC should be in relation to the amount of data. CRC-8 might be sufficient if you just have a few bytes of data overall.

\$\endgroup\$
6
  • \$\begingroup\$ in this case, with which byte is the CRC value calculated? \$\endgroup\$ Nov 30, 2021 at 14:36
  • 2
    \$\begingroup\$ @RicardoCasimiro CRC is calculated on the whole packet minus sync bytes. \$\endgroup\$
    – Lundin
    Nov 30, 2021 at 15:10
  • 3
    \$\begingroup\$ @Lundin: Or with sync bytes. Including a constant prefix in a CRC is exactly equivalent to using a different IV for the CRC calculation. \$\endgroup\$
    – Ben Voigt
    Nov 30, 2021 at 16:25
  • 1
    \$\begingroup\$ @RicardoCasimiro: You said "which byte" (singular), which makes me wonder if you're missing the point. One CRC can be calculated from a whole buffer of many bytes, not just of one byte. See en.wikipedia.org/wiki/Cyclic_redundancy_check#CRC-32_algorithm for pseudocode and C which take an array of bytes. Or Compute CRC32 Hash on codegolf.SE (In hardware, you'd this on the fly during sending, so you're ready to send the CRC bits right after the last data bit.) For example Ethernet does this, with CRC coming last in the frame. \$\endgroup\$ Dec 1, 2021 at 9:54
  • 1
    \$\begingroup\$ @Lundin CRC is calculated in such a way that you can save the result for the sync bytes and start calculations using that instead of zero. This value can easily be hardcoded, or at worst computed once per sync sequence. That's what Ben meant by IV - initialization vector. Assuming sync bytes are constant of course. Not to mention that, honestly, calculating CRC-16 of two bytes is cheap anyway. On a modern Cortex-M MCU it shouldn't impact the speed of communication over a medium as slow as UART. \$\endgroup\$
    – jaskij
    Dec 1, 2021 at 17:46
13
\$\begingroup\$

Using CRC to detect errors on a byte basis doesn't make sense. CRC's are typically used on a multi-byte, or muti-word basis, sometimes referred to as a frame.

The math behind CRC's is based on polynomial division with binary coefficients. The raw message is the dividend, and the divisor is the generator, or CRC polynomial.

A good generator polynomial will 1) detect all single bit errors, 2) detect all double bit errors within a frame, 3) detect all odd number of errors, 4) detect all burst errors of length r or less, where r is the length of the CRC, 5) detect many burst errors of length >r, and 6) detects greater than 99.9% of all errors.

This being the case any of the many commonly used CRC polynomials (CRC-8, CRC-16, CRC-16, CRC-CCITT) will suffice for most applications - for any but the most impaired channels.

With CRC (as opposed to forward error correcting, FEC codes), your application needs be able to 1) live with a dropped message, or 2) have a means (handshake or acknowledge) to have the sender re-transmit a dropped message.

CRCs in Serial Comm

In serial communication links, a CRC can be generated using a Linear Feedback Shift Register (LFSR). The serial bit stream is fed into one end of the LFSR, clocked (or shifted through) and at the end of this process the output of the LFSR is the CRC. The diagram below shows this process at a high level:

enter image description here

The diagram below shows an implementation I used to send variable length messages composed of 16-bit words from one unit to another.

enter image description here

This is an implementation of the CRC16-CCITT CRC, enter image description here

A couple of words of caution, speaking from experience. Both ends of the communication link need to agree on the bit order (MSB or LSB first), initial value for the LFSR (all 0's or all 1's), whether or not data bits are reflected, and whether or not the CRC is complimented before it is appended to the message

\$\endgroup\$
6
\$\begingroup\$

Usually the CRC is calculated per single transaction, not single byte. CRC can be calculated for any amount of data. for its mathematical computation, a divisor (called polynomial) is applied on each section of data and shifted towards the end of data. When reached the end, the final remainder is called the checksum value of that amount of data. your data can be of any size. the checksum length is fixed (e.g. 2 bytes) no matter if your data was for example 2 bytes or 200 bytes.

\$\endgroup\$
0
4
\$\begingroup\$

If you require an optimal polynomial for your payload size I suggest you check ou this publication "Selection of Cyclic Redundancy Code and Checksum Algorithms to Ensure Critical Data Integrity".

Based on this document you need to choose the best polynomial with the highest HD (Hamming Distance) for the maximum payload size. Specifically Table 7. presents some of the best recommended polynomials to use to obtain the required or highest HD level for your payload.

As you can see from this table there are very few polynomials for just a payload of 8 bits ... For instance 0xBAAD would be HD=6 for any payload < 20 bits.

\$\endgroup\$
0
\$\begingroup\$

As others have said, a CRC is calculated over many bytes, not just one. I won't duplicate the very good explanations of CRC here.

Your question states the CRC is added for each byte. This sounds more like one of the following:

  • Parity (a check bit)
  • Forward error correction (FEC)

Parity is a single bit that provides a sort of verification that the others are all correct. There's a nice explanation of parity calculation here: https://stackoverflow.com/a/15591444/917444. This allows you to discard any bytes that have errors in them. In serial communications, this can lead to missing characters or whatever in the received data, which you may or may not be able to tolerate. Parity can only detect single bit errors, so if two bits get flipped, then it won't spot the problem. More elaborate solutions exist for that case (CRC being one of them).

Forward Error Correction (FEC) is a method of sending additional information that can be used, in conjunction with the data itself, to reconstruct the data if it gets modified in transit. The point here is that you're sending redundant data in the hope that enough of it makes it to the receiver that it can be reconstructed properly.

There are a variety of ways to do FEC, all depending on what you're trying to achieve and how many errors you want to fix. There's a good Wikipedia on the subject if you want and introduction.

One very simple example of FEC is used in the infra red signals in the average TV remote. They simply send the entire message twice in a row (so each button press actually sends the same command twice to the TV). The receiver receives these commands and acts on them - but has some special logic so that if you press "volume up" it actually only does it once and not twice.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.