Looking for CRC16 app note for Modbus CRC

cancel
Showing results for 
Search instead for 
Did you mean: 

Looking for CRC16 app note for Modbus CRC

3,164 Views
bobmke
Contributor II

I'm using a KE96 processor and I'm trying to use the CRC module to calculate the CRC for a Modbus string.

I use a seed value of 0xfff and a polynomial of 0xa001, but I can't seem to get the right CRC value.

I haven't tried all of the combinations of transposing bit and such yet.

Does anyone know of an app note describing how to generate crc16 for Modbus comms?

Thanks

Labels (1)
8 Replies

1,953 Views
scottm
Senior Contributor II

It's great to see that the CRC can be done in hardware, but as someone who's also doing Modbus RTU stuff right now, I'm curious what the motivation is.  Modbus usually runs at not more than 19.2 kbps and I've always done the CRC calculation in software, even on 8-bit MCUs running at a few MHz.  Are you processing Modbus data at a very high rate, or are you otherwise resource-constrained?

I expect I'll keep using the software implementation for ease of debugging and portability unless I start running it at much higher rates.

Scott

0 Kudos

1,953 Views
nikolausbauly
Contributor II

Well I mainly wanted to get it working, because I like to be capable of using all HW-modules.
The second thing is, that I'm working on µVision right now, and my Professional Version expired recently, so I can only use 32Kb of code size.

0 Kudos

1,953 Views
nikolausbauly
Contributor II

Hey, since the above example did not work for me, and I searched days after days through the net without getting results here is the code i finally came up with:

uint16_t CRC_calc(uint8_t *msg, uint32_t length)
{
uint16_t high;
uint8_t low;
uint8_t i;
uint32_t data_out;
//Enable CRC-Clock
SIM_SCGC6 |= SIM_SCGC6_CRC_MASK;
//Set Polynom 8005 == Transpose(A001)
CRC0->GPOLY_ACCESS16BIT.GPOLYL=0x8005;
//Set Types of Transpose
CRC0->CTRL = CRC_CTRL_TOT(1) | CRC_CTRL_TOTR(2);
//Enable "Seed"-Mode / Disable Data-Mode
CRC0->CTRL |= CRC_CTRL_WAS_MASK;
//Write Seed to lower data register
CRC0->ACCESS16BIT.DATAL=0xFFFF;
//Disable "Seed"-Mode / Enable Data-Mode
CRC0->CTRL &= ~CRC_CTRL_WAS_MASK;
//Write Data in 16bit Pairs
for (i=0; i<length; i=i+2)
{
high = (uint8_t) *(msg+i);
low = (uint8_t) *(msg+i+1);
data_out = (high<<8) | low;
CRC0->ACCESS16BIT.DATAL = (uint16_t)data_out;
}
//Read Calculated CRC from Register
data_out=(CRC0->DATA);
//Get Higher 16 Bits
data_out=data_out>>16;
//Disable CRC-Clock
SIM->SCGC6 &= ~SIM_SCGC6_CRC_MASK;
//Return CRC
return (uint16_t)data_out;
}


.Hope this helps those who look for it.

1,953 Views
timbarry
Contributor II

Hi Bob,

I had the same problem but found some posts on the internet that said you need to use a different polynomial. Something about the CRC hardware doing the computation in the opposite bit order to the code solutions. Use 0x8005 (the reverse of 0xA001), and set the read and write transpose to 1 and it will work. The below code works for me:

void ModbusSlave::_crc_append(uint8_t* buffer, uint8_t* length)

{

    CRC0->GPOLY_ACCESS16BIT.GPOLYL = 0x8005;

    CRC0->CTRL = CRC_CTRL_WAS_MASK | CRC_CTRL_TOT(1) | CRC_CTRL_TOTR(1);

    CRC0->ACCESS16BIT.DATAL = 0xFFFF;

    CRC0->CTRL &= ~CRC_CTRL_WAS_MASK;

    for (int i=0; i<(*length); i++) {

        CRC0->ACCESS8BIT.DATALL = buffer[i];

    }

    buffer[*length] = CRC0->ACCESS8BIT.DATALU;

    buffer[*length+1] = CRC0->ACCESS8BIT.DATALL;

    *length = *length + 2;

}

Tim

280 Views
ErnestoCapuano
Contributor I

Thanks Tim, that helped me a lot, have an excellent day

0 Kudos

1,953 Views
bobmke
Contributor II

Tim,

Thanks, I'll give it a try. I've been off working on some other stuff, but should be back into this in a week or so.

I'll let you know what I find.

0 Kudos

1,953 Views
mjbcswitzerland
Specialist V

Hello Bob

Below is the code from the uTasker MODBUS extension module - it aso has an optional look-up table option for speed against code size.

See the links below if you need a ready-made, industrial proven MODBUS module that works on all KE, KEA, KV, KL, and K parts.

Regards

Mark

Kinetis: µTasker Kinetis support

KE: µTasker FRDM-KE02Z support  / µTasker FRDM-KE02Z40M support  / µTasker FRDM-KE06Z support

MODBUS (Master/Slave/Gateway/Router/RTU/ASCII/RS485/TCP): µTasker MODBUS Extension pack

For the complete "out-of-the-box" Kinetis experience and faster time to market

// Calculate the check sum of an RTU type frame

//

static unsigned short fnCRCRTUFrame(unsigned char *ptrFrame, QUEUE_TRANSFER rxFrameLength)

{

#if defined MODBUS_CRC_FROM_LOOKUP_TABLE // look-up table method requiring more memory but faster

    unsigned char ucCRC_Lo = 0xff;

    unsigned char ucCRC_Hi = 0xff;

    unsigned char ucIndex;

    static const unsigned char ucCRC_lookup[512] = {

        0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40, 0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,

        0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41, 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,

        0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41, 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,

        0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40, 0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,

        0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41, 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,

        0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40, 0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,

        0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40, 0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,

        0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41, 0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,

        0x00,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,0xC6,0x06,0x07,0xC7,0x05,0xC5,0xC4,0x04, 0xCC,0x0C,0x0D,0xCD,0x0F,0xCF,0xCE,0x0E,0x0A,0xCA,0xCB,0x0B,0xC9,0x09,0x08,0xC8,

        0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC, 0x14,0xD4,0xD5,0x15,0xD7,0x17,0x16,0xD6,0xD2,0x12,0x13,0xD3,0x11,0xD1,0xD0,0x10,

        0xF0,0x30,0x31,0xF1,0x33,0xF3,0xF2,0x32,0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4, 0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,0xFA,0x3A,0x3B,0xFB,0x39,0xF9,0xF8,0x38,

        0x28,0xE8,0xE9,0x29,0xEB,0x2B,0x2A,0xEA,0xEE,0x2E,0x2F,0xEF,0x2D,0xED,0xEC,0x2C, 0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,

        0xA0,0x60,0x61,0xA1,0x63,0xA3,0xA2,0x62,0x66,0xA6,0xA7,0x67,0xA5,0x65,0x64,0xA4, 0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,0x6E,0xAE,0xAA,0x6A,0x6B,0xAB,0x69,0xA9,0xA8,0x68,

        0x78,0xB8,0xB9,0x79,0xBB,0x7B,0x7A,0xBA,0xBE,0x7E,0x7F,0xBF,0x7D,0xBD,0xBC,0x7C, 0xB4,0x74,0x75,0xB5,0x77,0xB7,0xB6,0x76,0x72,0xB2,0xB3,0x73,0xB1,0x71,0x70,0xB0,

        0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54, 0x9C,0x5C,0x5D,0x9D,0x5F,0x9F,0x9E,0x5E,0x5A,0x9A,0x9B,0x5B,0x99,0x59,0x58,0x98,

        0x88,0x48,0x49,0x89,0x4B,0x8B,0x8A,0x4A,0x4E,0x8E,0x8F,0x4F,0x8D,0x4D,0x4C,0x8C, 0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40,

    };

    do {

        ucIndex = (ucCRC_Lo ^ *ptrFrame++);

        ucCRC_Lo = (ucCRC_Hi ^ ucCRC_lookup[ucIndex]);

        ucCRC_Hi = ucCRC_lookup[ucIndex + 256];

    } while (--rxFrameLength);

    return ((ucCRC_Hi << 8) | ucCRC_Lo);

#else // shift register method

    #define MODBUS_CRC_POLY 0xa001

    int iBit;

    unsigned short usCRC = 0xffff;

    do {

        usCRC ^= *ptrFrame;

        for (iBit = 0; iBit < 8; iBit++) {

            if (usCRC & 0x0001) {

                usCRC >>= 1;

                usCRC ^= MODBUS_CRC_POLY;

            }

            else {

                usCRC >>= 1;

            }

        }

        ptrFrame++;

    } while (--rxFrameLength);

    return usCRC;

#endif

}

1,953 Views
bobmke
Contributor II

Yeah, I have it working using similar code.

What I was trying to do is use the CRC module built into the micro.

I have example code, but It doesn't come out with the right values.

I thought someone might have figured out how to configure the CRC function registers to calculate the correct CRC for Modbus.

The code from Freescale looks like this:

uint32_t CRC_Cal16(uint32_t seed, void *msg, uint32_t sizeBytes)

{

     uint32_t ctrl_reg,data_out,data_in;

     uint8_t  *pCRCBytes;

     uint32_t sizeWords;

     uint32_t i,j; 

     BYTE *bptr = msg;

 

     CRC_Init();


     CRC0->GPOLY_ACCESS16BIT.GPOLYH = 0x0000;                               // RKS, I added this

     CRC0->GPOLY_ACCESS16BIT.GPOLYL = 0xA001;                               // RKS, I added this

    /* Input seed, Set WaS=1 */

      ctrl_reg  = CRC0->CTRL;

      CRC0->CTRL  = ctrl_reg | CRC_CTRL_WAS_MASK;


     CRC0->ACCESS16BIT.DATAL = seed;

 

  /*Input data, Set
     WaS=0*/

     CRC0->CTRL  = ctrl_reg & 0xFD000000;

  /*Wait for calculation completion*/

  sizeWords = sizeBytes>>1;

  j = 0;


     for(i=0;i<sizeWords;i++){

          data_in = (bptr[j] << 8) | (bptr[j+1]);

           j += 2;

           CRC0->ACCESS16BIT.DATAL =data_in;

      }

     if (j<sizeBytes)

     {

          pCRCBytes = (uint8_t*)&CRC0->ACCESS8BIT.DATALL;

          *pCRCBytes++ = bptr[j];

     }

  
     data_out=CRC0->ACCESS16BIT.DATAL;

     return(data_out);

}

0 Kudos