CRC verification experiment of Modbus

Time:2020-9-17

CRC verification experiment of Modbus

formulation

Name   : "CRC-16/MODBUS"
   Width  : 16
   Poly   : 8005
   Init   : FFFF
   RefIn  : True
   RefOut : True
   XorOut : 0000
   Check  : ?

First, refer to the Modbus calibration program given by some manufacturers

const unsigned char auchCRCHi[] = {

From the above program, we can see that the method used is the direct drive table method, but the generated table is inconsistent with the table generated by direct calculation of forward poly.

The reason is that MODBUS uses the option of inverting input bytes, so the generated tables are different

So how is this table generated?

poly        18005

Because the CRC in MODBUS has selected the option “find”, let’s see how this table is generated

unsigned short crcTable[256] = {0};
unsigned short poly = 0xa001;
void crcTableCreate(void)
{
    int i = 0;
    for(i = 0; i < 256; i++)
    {
        unsigned short crc = i;
        for(int j = 0; j < 8; j++)
        {
            if(crc & 0x0001)
            {
                crc = (crc >> 1) ^ poly;
            }
            else
            {
                crc = crc >> 1;
            }
        }
        crcTable[i] = crc;
    }
}

0000,c0c1,c181,0140,c301,03c0,0280,c241,c601,06c0,0780,c741,0500,c5c1,c481,0440,
cc01,0cc0,0d80,cd41,0f00,cfc1,ce81,0e40,0a00,cac1,cb81,0b40,c901,09c0,0880,c841,
d801,18c0,1980,d941,1b00,dbc1,da81,1a40,1e00,dec1,df81,1f40,dd01,1dc0,1c80,dc41,
1400,d4c1,d581,1540,d701,17c0,1680,d641,d201,12c0,1380,d341,1100,d1c1,d081,1040,
f001,30c0,3180,f141,3300,f3c1,f281,3240,3600,f6c1,f781,3740,f501,35c0,3480,f441,
3c00,fcc1,fd81,3d40,ff01,3fc0,3e80,fe41,fa01,3ac0,3b80,fb41,3900,f9c1,f881,3840,
2800,e8c1,e981,2940,eb01,2bc0,2a80,ea41,ee01,2ec0,2f80,ef41,2d00,edc1,ec81,2c40,
e401,24c0,2580,e541,2700,e7c1,e681,2640,2200,e2c1,e381,2340,e101,21c0,2080,e041,
a001,60c0,6180,a141,6300,a3c1,a281,6240,6600,a6c1,a781,6740,a501,65c0,6480,a441,
6c00,acc1,ad81,6d40,af01,6fc0,6e80,ae41,aa01,6ac0,6b80,ab41,6900,a9c1,a881,6840,
7800,b8c1,b981,7940,bb01,7bc0,7a80,ba41,be01,7ec0,7f80,bf41,7d00,bdc1,bc81,7c40,
b401,74c0,7580,b541,7700,b7c1,b681,7640,7200,b2c1,b381,7340,b101,71c0,7080,b041,
5000,90c1,9181,5140,9301,53c0,5280,9241,9601,56c0,5780,9741,5500,95c1,9481,5440,
9c01,5cc0,5d80,9d41,5f00,9fc1,9e81,5e40,5a00,9ac1,9b81,5b40,9901,59c0,5880,9841,
8801,48c0,4980,8941,4b00,8bc1,8a81,4a40,4e00,8ec1,8f81,4f40,8d01,4dc0,4c80,8c41,
4400,84c1,8581,4540,8701,47c0,4680,8641,8201,42c0,4380,8341,4100,81c1,8081,4040,

The tables generated by this method are consistent with those given by the government, but the high and low bits are reversed. How to understand this? It is because the low bit first XOR method, then the byte filling method is inconsistent with the forward direction, and the reverse mode is equivalent to that the data is filled on the left side of the data string. Therefore, the last 8 bits of the CRC generated are instead advanced with the last in data Line XOR.

The formula of direct drive table method in this way is also different from poly

unsigned short crcUpdate3(unsigned short crcIn, unsigned char data)
{
    unsigned short result = 0;
    //result = (crcIn << 8) ^ crcTable[(crcIn >> 8) ^ data]; // poly
    result = (crcIn >> 8) ^ crcTable[(crcIn & 0xff) ^ data]; // reversed poly
    return result;
}

unsigned short crcCheck3(unsigned char *pData, unsigned char size)
{
    unsigned short crcResult = 0xffff;//Initial Value
    for(int i = 0; i < size; i++)
    {
        crcResult = crcUpdate3(crcResult, *(pData+i));
    }
    return crcResult;
}

If you want to get the same result from the three methods, the initial register value of the direct drive table method must be 0 to achieve the same output results of the three methods. Otherwise, the direct drive table method can get the same result as the other two methods, because the direct drive table method is a variation of the direct counting method.

The following is the specific implementation of three consistent results for Modbus,The initial value of the register is set to 0

unsigned short crcUpdate(unsigned short crcIn, unsigned char data)
{
    unsigned short result = 0;
    unsigned int poly = (0xa001 << 1) + 1;
    unsigned int tmp =  crcIn;
    for(int i = 0; i < 8; i++)
    {
        if(data & 0x01)
        {
            tmp += 0x10000;
        }
        data >>= 1;
        if(tmp & 0x0001)
        {
            tmp ^= poly;
        }
        tmp >>= 1;
    }
    result = tmp & 0xffff;
    return result;
}

unsigned short crcCheck(unsigned char *pData, unsigned char size)
{
    unsigned short crcResult = 0x0000;//Initial Value
    for(int i = 0; i < size; i++)
    {
        crcResult = crcUpdate(crcResult, *(pData+i));
    }
    for(int i = 0; i < 2; i++)
    {
        crcResult = crcUpdate(crcResult, 0x00);
    }
    return crcResult;
}

unsigned short crcUpdate2(unsigned short crcIn, unsigned char data)
{
    unsigned short result = 0;
    result = (crcIn >> 8 | data << 8) ^ crcTable[(crcIn & 0xff)];
    return result;
}

unsigned short crcCheck2(unsigned char *pData, unsigned char size)
{
    unsigned short crcResult = 0x0000;//Initial Value
    for(int i = 0; i < size; i++)
    {
        crcResult = crcUpdate2(crcResult, *(pData+i));
    }
    for(int i = 0; i < 2; i++)
    {
        crcResult = crcUpdate2(crcResult, 0x00);
    }
    return crcResult;
}

unsigned short crcUpdate3(unsigned short crcIn, unsigned char data)
{
    unsigned short result = 0;
    result = (crcIn >> 8) ^ crcTable[(crcIn & 0xff) ^ data];
    return result;
}

unsigned short crcCheck3(unsigned char *pData, unsigned char size)
{
    unsigned short crcResult = 0x0000;//Initial Value
    for(int i = 0; i < size; i++)
    {
        crcResult = crcUpdate3(crcResult, *(pData+i));
    }
    return crcResult;
}

The byte is inverted and the small bit is calculated first because some hardware circuits are sent by LSB first. If the calculation of small bits is started, CRC calculation can be started directly for the data entered by hardware, which is convenient for the implementation of hardware circuit.

Recommended Today

How to share queues with hypertools 2.5

Share queue with swote To realize asynchronous IO between processes, the general idea is to use redis queue. Based on the development of swote, the queue can also be realized through high-performance shared memory table. Copy the code from the HTTP tutorial on swoole’s official website, and configure four worker processes to simulate multiple producers […]