Search code examples
c#serial-portcrcmodbuscrc16

CRC-16/Modbus Implementation in C# malfunction


I'm currently setting up the communication between a controller for a step motor and a computer, coding an application in C# (it is the first time I use this programming language, and although I'm not a computer scientist but an industrial engineer, reason why I'm sure there are some ways of optimizing the function which I don't know, any recommendation on that matter would also be very appreciated). Therefore, I've been using the RS-485 that the controller has to communicate with it, and I've implemented an algorithm that generates the CRC(Cyclic Redundancy Check) bytes required.

And there is where my problem begins. I can't find the reason why my function doesn't generate the correct CRC value. I have checked with some online calculators of CRC and I've also used the example that appears in the Modbus Guide (where it also explains how is the code implemented).

Here is the code I've written for the calculus of the CRC:

class Program
    {
        static void Main(string[] args)
        {
            // 0x05, 0x06, 0x17, 0x70, 0x00, 0x01 
            byte[] prueba = new byte[] { 0x02, 0x07 };
            byte[] result = Aux.CRC(prueba);
            Console.WriteLine(result[0] + " " + result[1]);
        }
    }

class Aux{
public static byte[] CRC(byte[] to_evaluate)
        {
            byte[] CRC_Byte = new byte[2] { 0, 0 };
            UInt16 CRC_Register = 0xFFFF;            //16 bits 1111.1111.1111.1111
            UInt16 CRC_pol = 0xa001;                 //16 bits 1010.0000.0000.0001

            foreach (UInt16 byte_val in to_evaluate)
            {
                CRC_Register ^= byte_val;
                Console.WriteLine("XOR inicial : {0:X}", CRC_Register);

                for (byte i = 0; i < 8; i++)
                {
                    CRC_Register >>= 1;
                    Console.WriteLine("Desplazamiento " + (i + 1) + ": {0:X}", CRC_Register);

                    if ((CRC_Register & 1) != 0)
                    {
                        CRC_Register ^= CRC_pol;
                        Console.WriteLine("XOR: {0:X}", CRC_Register);
                    }
                }
            }
            Console.WriteLine("{0:X}",CRC_Register);

            byte low_byte_CRC = (byte)((CRC_Register << 8) >> 8);
            byte high_byte_CRC = (byte)(CRC_Register >> 8);

            CRC_Byte[0] = low_byte_CRC;
            CRC_Byte[1] = high_byte_CRC;

            return CRC_Byte;
        }
}

The expected result using the test array attached and the polinomial 0xa001 is 0x1241 for CRC_Register, and {0x41,0x12} for the CRC_Byte.


Solution

  • I had to implement a CRC check for PPP once in C# and it was absolutely no fun!

    I found in this link the code that should correctly generate the CRC. It follows the CRC Generation procedure from section 6.2.2 on page 39 of the document you shared the link to.

    // Compute the MODBUS RTU CRC
    UInt16 ModRTU_CRC(byte[] buf, int len)
    {
        UInt16 crc = 0xFFFF;
      
        for (int pos = 0; pos < len; pos++) 
        {
            crc ^= (UInt16)buf[pos];          // XOR byte into least sig. byte of crc
      
            for (int 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 ^= 0xA001;
                }
                else                            // Else LSB is not set
                {                            
                    crc >>= 1;                    // Just shift right
                }
            }
        }
        // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
        return crc;  
    }