kda406
Joined: 17 Sep 2003 Posts: 97 Location: Atlanta, GA, USA
|
How to use PIC18F CRC Hardware Module |
Posted: Thu Apr 16, 2020 1:16 pm |
|
|
I wanted to use the CRC module in the newer PIC18F devices. I have been using a CRC calculator that can do any polynomial up to an order of 32 bits for more than a decade and it works great. But it’s slower than hardware and takes up precious code space. I had problems getting started with the hardware CRC module. I worked them out and will share my results here. Once you see the code fragments, I hope you will find it much easier to use on your projects.
CRCs have a name, a polynomial, an initialization value, an XOR function, and directional indicators that are sometimes called reverse, reflected, or inversion. CRC definitions typically come with a “check” value. The check is the answer to the successful creation of a CRC from 9 bytes of ASCII data, 1-9. The CCS manual and the EX_CRC_HW.c example skips most of these required CRC items.
Starting with the polynomial, let's examine CRC-16-USB. It is x^16 + x^15 + x^2 + 1, often listed as X16+X15+X2+1, and referred to as 0x8005. The order of 16 bits is known and if you convert 8005h to binary, you’ll see there is a 1 on the 15th bit, a 1 on the 3rd bit, and a 1 at the end. Those correspond to the X15, X2, and 1 in the listed polynomial. Being a 16 bit CRC, the X16 is implied. You must understand this to setup the setup_crc() CCS function properly.
Knowing this, we can look at a simple example. The polynomial for Dallas/Maxim CRC-8 for 1-Wire devices is described as 0x31. Converting 0x31 to binary we see there is a 1 in the 5th, 4th bits and the LSB. That means its polynomial is X5+X4+1, right? Wrong!
Code: | setup_crc(5,4); // Incorrect |
As I said above, we must also include the “order” bit, which is not included in the polynomial value used to describe it. This is an 8 bit polynomial, so the correct setup for 0x31 is:
Code: | setup_crc(8,5,4); // Correct |
Note that the +1 is shown in the hex description of the polynomial, but is not included in the CCS setup command.
When the CRC is reflected, that means we need to use the CRC_LITTLE_ENDIAN definition in your device’s header file. The documentation on this is abysmal and I will show you how to do this below. NOTE: You must also reflect the answer when you use this method and CCS provides no automation for this! The Microchip hardware CRC module has a method to reflect the output, but it is not available through the CCS methods described in the manual.
The Microchip hardware CRC module also has XOR registers for the XOR function I mentioned above. Unfortunately, CCS seems to skip over this entirely. It is not that hard to XOR your result in software, so I show the work-aound in the USB and X.25 code snippets below.
I made these examples as straight code snippets instead of functions for educational simplicity:
Code: |
char iCrcTest[9] = {'1','2','3','4','5','6','7','8','9'};
UINT8 myCrc8;
UINT16 myCrc16;
fprintf(TERM,"\n\rCommon 8 Bit CRC tests:\n\r");
// CRC-8-MAXIM, X8+X5+X4+1, poly=0x31, init=0x00, refin=true, refout=true, xorout=0x00, check=0xa1
setup_crc(8,5,4,CRC_LITTLE_ENDIAN); // 0x31, reflected in
crc_init(0x00); // Set accumulator to init value
myCrc8=crc_calc8(iCrcTest,9); // Hardware CRC calculation
myCrc8=CRC_FlexiReflect(myCrc8, 8); // Reflected output
fprintf(TERM,"0x%02X CRC-8-MAXIM (0xA1)\n\r",myCrc8);
// CRC-8-SMBUS, X8+X2+X1+1, poly=0x07, init=0x00, refin=false, refout=false, xorout=0x0, check=0xf4
setup_crc(8,2,1); // 0x07, nonreflective
crc_init(0x00); // Set accumulator to init value
myCrc8=crc_calc8(iCrcTest,9); // Hardware CRC calculation
fprintf(TERM,"0x%02X CRC-8-SMBUS (0xF4)\n\r",myCrc8);
// CRC-8/BLUETOOTH, X8+X7+X5+X2+X1+1, poly=0xa7, refin=true, refout=true, xorout=0x00, check=0x26
setup_crc(8,7,5,2,1,CRC_LITTLE_ENDIAN); // 0xA7, reflective in
crc_init(0x00); // Set accumulator to init value
myCrc8=crc_calc8(iCrcTest,9); // Hardware CRC calculation
myCrc8=CRC_FlexiReflect(myCrc8, 8); // Reflected output
fprintf(TERM,"0x%02X CRC-8-BLUETOOTH (0x26)\n\r",myCrc8);
fprintf(TERM,"\n\rCommon 16 Bit CRC tests:\n\r");
// CRC-16-XMODEM, X16+x12+X5+1, poly=0x1021, init=0x00, refin=false, refout=false, xorout=0x00, check=0x31c3
setup_crc(16,12,5); // 0x1021, nonreflective
crc_init(0x00); // Set accumulator to init value
myCrc16=crc_calc16(iCrcTest,9,8); // Hardware CRC calculation
fprintf(TERM,"0x%04LX CRC-16-XMODEM (0x31C3)\n\r",myCrc16);
// CRC-16-KERMIT, X16+x12+X5+1, poly=0x1021, init=0x00, refin=true, refout=true, xorout=0x00, check=0x2189
setup_crc(16,12,5,CRC_LITTLE_ENDIAN); // 0x1021, reflective in
crc_init(0x00); // Set accumulator to init value
myCrc16=crc_calc16(iCrcTest,9,8); // Hardware CRC calculation
myCrc16=CRC_FlexiReflect(myCrc16, 16); // Reflected output
fprintf(TERM,"0x%04LX CRC-16-KERMET (0x2189)\n\r",myCrc16);
// CRC-16-X.25, X16+x12+X5+1, poly=0x1021, init=0xFFFF, refin=true, refout=true, xorout=0xFFFF, check=0x906e
setup_crc(16,12,5,CRC_LITTLE_ENDIAN); // 0x1021, reflective in
crc_init(0xFFFF); // Set the accumulator to init value
myCrc16=crc_calc16(iCrcTest,9,8); // Hardware CRC calculation
myCrc16=CRC_FlexiReflect(myCrc16, 16); // Reflected output
myCrc16 ^= 0xFFFF; // XOR output
fprintf(TERM,"0x%04LX CRC-16-X.25 (0x906E)\n\r",myCrc16);
// CRC-16-MODBUS, X16+x15+X2+1, poly=0x8005, init=0xFFFF, refin=true, refout=true, xorout=0x00, check=0x4b37
setup_crc(16,15,2,CRC_LITTLE_ENDIAN); // 0x8005, reflective in
crc_init(0xFFFF); // Set the accumulator to init value
myCrc16=crc_calc16(iCrcTest,9,8); // Hardware CRC calculation
myCrc16=CRC_FlexiReflect(myCrc16, 16); // Reflected output
fprintf(TERM,"0x%04LX CRC-16-MODBUS (0x4B37)\n\r",myCrc16);
// CRC-16-USB, X16+x15+X2+1, poly=0x8005, init=0xFFFF, refin=true, refout=true, xorout=0xFFFF, check=0xb4c8
setup_crc(16,15,2,CRC_LITTLE_ENDIAN); // CRC is 0x8005 X16+x15+X2+1 XOR 0xFFFF Reflective Init 0xFFFF (USB)
crc_init(0xFFFF); // Load the CRC accumulator
myCrc16=crc_calc16(iCrcTest,9,8); //Calculates the CRC
myCrc16=CRC_FlexiReflect(myCrc16, 16); // Reflect the output
myCrc16 ^= 0xFFFF;
fprintf(TERM,"0x%04LX CRC-16-USB (0xB4C8)\n\r",myCrc16);
|
Some of those functions need the reflected output. I have used this function on many projects on many compilers over decades. I don't remember if I coded this myself, got it from a class, from the web, or a colleague. I make no ownership claims.
Code: | // reflects the lower 'bitnum' bits of 'crc'
UINT32 CRC_FlexiReflect(UINT32 crc, INT8 bitnum) {
UINT32 i, j=1, crcout=0;
for (i=(UINT32)1<<(bitnum-1); i; i>>=1) {
if (crc & i) crcout|=j;
j<<= 1;
}
return (crcout);
} |
The code snippets above yield the following results:
Code: | Common 8 Bit CRC tests:
0xA1 CRC-8-MAXIM (0xA1)
0xF4 CRC-8-SMBUS (0xF4)
0x26 CRC-8-BLUETOOTH (0x26)
Common 16 Bit CRC tests:
0x31C3 CRC-16-XMODEM (0x31C3)
0x2189 CRC-16-KERMET (0x2189)
0x906E CRC-16-X.25 (0x906E)
0x4B37 CRC-16-MODBUS (0x4B37)
0xB4C8 CRC-16-USB (0xB4C8) |
Notice how Xmodem, Kermit, and X.25 are all known as CRC-16-CCITT and are almost identical. They use different inversion, initialization, and XOR methods. The Microchip CRC module handles all these internally, but right now it appears CCS offers no way of doing so directly. You must XOR and reverse your data based on the order of your selected polynomial.
I have tested the above on PIC18F67K40 and PIC18F45K40 physical devices. My goal here is to help people get started with the CRC hardware module with less hassle than I experienced. I am sure there are code improvements to be made along the way.
If you are just getting started with CRC or need some references, here are some good ones that I use.
Overview of CRC polynomials and uses:
https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Polynomial_representations_of_cyclic_redundancy_checks
Extremely detailed CRC definitions for polynomials, initialization, reflection, XOR, check values:
http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.cat.crc-8-bluetooth
Online CRC checking calculator to verify your results. Here are the 8 and 16 bit pages:
https://crccalc.com/?crc=123456789&method=crc8&datatype=ascii&outtype=hex
https://crccalc.com/?crc=123456789&method=crc16&datatype=ascii&outtype=hex
-Kyle
Last edited by kda406 on Fri Apr 17, 2020 9:55 am; edited 3 times in total |
|