2

I am working on calculating a CRC16-CCITT/KERMIT so that I can check data integrity on transmissions of 64-byte data packets between a C# Winforms Application and a microcontroller (PSoC5LP/Arm Cortex-M3). I'm close, but just not quite getting the CRC calculation to line up between the two and having a tough time figuring out why. The example data packet I am calculating the CRC for is:

02 03 01 02 03 04 05 07 08 09 0A 0B 00 00 06 0E 0C 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

The CRC for this packet comes out to 0x4D8C in both my Winforms application as well as for this online CRC calculator

Since these line up, I'm assuming the calculation in the C# calculation is legit. Regardless, here's the code. Pulled from this page with the only change being the I hardcoded the polynomial (0x8408):

public static class Crc16
{
    const ushort polynomial = 0x8408;
    static readonly ushort[] table = new ushort[256];

    public static ushort ComputeChecksum(byte[] bytes)
    {
        ushort crc = 0;
        for (int i = 0; i < bytes.Length; ++i)
        {
            byte index = (byte)(crc ^ bytes[i]);
            crc = (ushort)((crc >> 8) ^ table[index]);
        }
        return crc;
    }

    static Crc16()
    {
        ushort value;
        ushort temp;
        for (ushort i = 0; i < table.Length; ++i)
        {
            value = 0;
            temp = i;
            for (byte j = 0; j < 8; ++j)
            {
                if (((value ^ temp) & 0x0001) != 0)
                {
                    value = (ushort)((value >> 1) ^ polynomial);
                }
                else
                {
                    value >>= 1;
                }
                temp >>= 1;
            }
            table[i] = value;
        }
    }
}

Now the gotcha is getting a working CRC16-CCITT/KERMIT calculator function for the microcontroller that will generate the same CRC for this packet of data.

Here's what I currently have (pulled from this answer):

uint16_t crc16k(uint16_t crc, uint8_t *mem, uint8_t len) {
    uint8_t *data = mem;

    if (data == NULL){
        return 0;
    }

    crc = ~crc;
    crc &= 0xffff;

    while (len--) {
        crc ^= *data++;
        for (uint8_t k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ 0x8408 : crc >> 1;
    }

    crc ^= 0xFFFF;
    return crc;
}

I am calling this as such:

uint16_t crc_calc = crc16k(0x0000, message_in, 64);

Here's where I'm getting some funnies. that data packet is 64-bytes, but in actuality (for this packet), only the first 29 bytes are data I am using. The rest is just padding out to meet 64 bytes. When I calculate the CRC on the WinForms side, it looks like it is using all 64 bytes, including the padding. When I do the same on the microcontroller side, I get a result of 0xE918. What's weird is if I limit the length parameter to just the 29 bytes I'm interested in, I get 0x4C8B, which is perilously close to the 0x4D8C I'm looking for. I also noticed that in the online calculator I used, it claims the XOR on the output should be 0x0000. The C function I am using XORs 0xFFFF on the output. Changing this to 0x0000 (and processing all 64 bytes) gives me 0x16E7.

So I'm not sure where the problem lies. This is the first time I've worked with a CRC so I could be missing something obvious. Any ideas?

Thank you in advance for the help! Let me know if there's any additional information I should provide.

klutt
  • 30,332
  • 17
  • 55
  • 95
K_Trenholm
  • 482
  • 5
  • 20
  • Which options are you using on the webpage to get correct results? The sunshine webpage explain the options : http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html Not sure why you have the table. You can either use the table method or no table method. – jdweng Sep 11 '19 at 16:37
  • Which options are you using on the webpage to get correct results? The sunshine webpage explain the options : http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html Not sure why you have the table. You can either use the table method or no table method. when you generate the checksum you for 16crc you do the checksum for length x bytes (not include CRC). Then when you verify you do it for x + 2 (including crc) bytes and will get either all zeroes or all ones (depending on the options). – jdweng Sep 11 '19 at 16:42

1 Answers1

4

Of course I get it less than 10 minutes after posting the question (isn't that always how it goes?)

The C code I thought was for CRC-16/KERMIT looks to actually be CRC-16/X-25. I think I got confused because the question I took the code from the answer of was asking about KERMIT, but the answer says it's X-25.

Removing the bitwise invert of crc at the start of the function:

crc = ~crc;

AS WELL AS removing

crc ^= 0xFFFF;  

This leaves me with:

uint16_t crc16k(uint16_t crc, uint8_t *mem, uint8_t len) {
    uint8_t *data = mem;

    if (data == NULL){
        return 0;
    }

    while (len--) {
        crc ^= *data++;
        for (uint8_t k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ 0x8408 : crc >> 1;
    }

    return crc;
}

This seems to be working and the CRC matches up between my Winforms app and the PSoC5. I suppose the "near match" I had earlier was just coincidence the numbers were similar? I'd love to hear an explanation if someone has one.

K_Trenholm
  • 482
  • 5
  • 20
  • 1
    Are you aware that `crc ^= 0x0000;` is a no-op? As well as `crc &= 0xFFFF;` on a uint16 – Ctx Sep 11 '19 at 16:54
  • @Ctx Oh hey you're absolutely right lol. it didn't occur to me when the online calcualtor said XorOut = 0x0000 that means just don't XOR it. I've removed that line entirely as there's no point to it (and edited the answer to match). Yeah the mask on crc isn't needed. I was also wondering why it was there (maybe whatever it was originally written for "unsigned" is a 32 bit type?). In any case, you're right, I don't need it when I'm working with uint16. – K_Trenholm Sep 11 '19 at 16:59
  • @K_Trenholm - is there a reason that you don't use a 256 entry lookup table in the C code, which speeds up the code? You could just copy/translate the C# code to C code with only minor changes (the C code doesn't need to be part of a class). – rcgldr Sep 11 '19 at 20:39
  • @rcgldr No specific reason other than the C code that I happened to find for calculating CRC16 didn't use a table. I'll probably go back and make both ends table based before finishing this project. – K_Trenholm Sep 11 '19 at 21:05