1、硬件CRC校验和软件CRC校验的多项式,以及初始值
#define CRC_Hardware_POLYNOMIAL_16B 0x8005
//硬件CRC多项式为0x8005
//CRC16=x^16 + x^15 + x^2 + 1,因为bit15=1,bit2=1,bit0=1,所以正向校验的多项式的值为0x8005
//CRC校验分为正向校验和反向校验,而软件CRC使用的是反向校验,其多项式为0xA001,其原因如下:
//uint32_t d;
//d= __RBIT(CRC_Hardware_POLYNOMIAL_16B)>>16;
//执行后d=0xA001,所以软件CRC使用的是反向校验。
#define CRC_SoftWare_POLYNOMIAL_16B 0xA001
#define CRC16_INIT_VALUE 0xFFFF
2、软件CRC校验
1)、通过计算得到CRC校验值
CRC(Cyclic Redundancy Check),即CRC循环冗余校验;
先向“多项式寄存器”写入0xA001,再向“CRC寄存器”存入0xFFFF,
第1个8位字符优先和“CRC寄存器”的值进行相异或(XOR),更新“CRC寄存器”,然后判断“CRC寄存器”的bit0,
如果bit0=1,则将“CRC寄存器”右移一位,最高为补0,接着将“多项式寄存器”和“CRC寄存器”的值进行相异或(XOR),更新“CRC寄存器”;
如果bit0=0,则将“CRC寄存器”右移一位,最高为补0;
判断“CRC寄存器”的bit0总计8次,所以“CRC寄存器”右移是8次;
第1个8位字符处理完后,就将第2个8位字符和“CRC寄存器”的值进行相异或(XOR),然后更新“CRC寄存器”,最后判断“CRC寄存器”的bit0,
处理方法同上。
直至将所有字节处理完,最后将“CRC寄存器”的值返回,这就是CRC校验值。
//函数功能:CRC循环冗余校验
uint16_t FrequencyConverter_Crc16(uint8_t *data,uint8_t len)
{
uint16_t ccitt16 = CRC_SoftWare_POLYNOMIAL_16B; //向“多项式寄存器”写入0xA001
uint16_t crc = CRC16_INIT_VALUE; //向“CRC寄存器”存入0xFFFF
int i;
for(;len>0;len--)
{
crc^=*data; //异或
for(i=0;i<8;i++)
{
if(crc & 0x0001) //最低位为1
{
crc>>=1; //将最低位的1移出,剩下的数与0xA001异或
crc^=ccitt16;
}
else //最高位为0
{
crc>>=1; //直接移位
}
}
data++;
}
return crc;
}
2)、通过查表得到CRC校验值
union
{ uint8_t b[2];
uint16_t d; //b[1]和d的高8位值相等;b[0]和d的低8位值相等;小端存储方式
}modbus_serial_crc;
/* Table of CRC values for high byte */
const unsigned char modbus_auchCRCHi[] = {
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
};
/* Table of CRC values for low byte */
const char modbus_auchCRCLo[] = {
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
};
//函数功能:计算CRC16,其CRC值存放在modbus_serial_crc.d中;
//这是小端计算方法
void modbus_calc_crc(uint8_t *buf,uint8_t len)
{ uint8_t i;
uint8_t uIndex;
uint8_t data;
modbus_serial_crc.d=0xFFFF;
//设置modbus_serial_crc.d的初始值,为计算发送数据的CRC做准备;
for(i=0;i<len;i++)
{
data=buf[i];
uIndex = (modbus_serial_crc.b[0]) ^ data; // calculate the CRC
modbus_serial_crc.b[0] = (modbus_serial_crc.b[1]) ^ modbus_auchCRCHi[uIndex];
modbus_serial_crc.b[1] = modbus_auchCRCLo[uIndex];
}
}
3、硬件CRC校验
CRC_HandleTypeDef hcrc;
void CRC16_Init(void)
{
__HAL_RCC_CRC_CLK_ENABLE();//使能CRC外设时钟
hcrc.Instance = CRC;
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;//告诉编译器将使用用户定义的“多项式”
hcrc.Init.GeneratingPolynomial = CRC_Hardware_POLYNOMIAL_16B;
//设置CRC_POL寄存器的值
//用户定义的“CRC多项式”,CRC_Hardware_POLYNOMIAL_16B是用户定义的“多项式”值
hcrc.Init.CRCLength = CRC_POLYLENGTH_16B;
//CRC_CR寄存器Bit4:3(REV_IN[1:0]),POLYSIZE[1:0]=01b,指定“多项式”长度为16位
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;//告诉编译器将使用用户定义的“CRC初始值”
hcrc.Init.InitValue=CRC16_INIT_VALUE;
//如果使用DEFAULT_INIT_VALUE_DISABLE,则使用hcrc.Init.InitValue的值配置RC_INIT寄存器Bits 31:0(CRC_INIT[31:0])
//用户定义的“CRC初始值”,User-defined CRC init value
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
//告诉后面的计算函数将对“16位字节的数据块”进行CRC计算,调用CRC_Handle_8()
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
//CRC_CR寄存器Bit6:5(REV_IN[1:0]),REV_IN[1:0]=01b,输入8位数据执行“位反转”
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
//CRC_CR寄存器Bit7(REV_OUT),令REV_OUT=1,输出数据执行“位反转”
HAL_CRC_Init(&hcrc);//初始化硬件CRC
}
4、测试程序
void Test_CRC16(void)
{
uint32_t uwCRCValue;
uwCRCValue= __RBIT(CRC_Hardware_POLYNOMIAL_16B)>>16;
//执行后uwCRCValue=0xA001
printf("CRC_Hardware_POLYNOMIAL_16B=0x%X\r\n",CRC_Hardware_POLYNOMIAL_16B);
printf("uwCRCValue=0x%X\r\n",uwCRCValue);
printf("CRC_Hardware_POLYNOMIAL_16B=0x%X\r\n",CRC_Hardware_POLYNOMIAL_16B);
//软件计算CRC
printf("SoftwareCRC1=0x%X\r\n",uwCRCValue);
modbus_calc_crc( (uint8_t *)&CRC16_DATA8,BUFFER_SIZE );//查表计算CRC
printf("SoftwareCRC2=0x%X\r\n",modbus_serial_crc.d);
CRC16_Init();//在使用硬件CRC计算CRC之前,必须初始化一次
uwCRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)&CRC16_DATA8, BUFFER_SIZE);
//硬件计算CRC
printf("hCRC1=0x%X\r\n",uwCRCValue);
if (uwCRCValue != uwExpectedCRCValue)
{
printf("hCRC1 Error!\r\n");
}
CRC16_Init();//在使用硬件CRC计算CRC之前,必须初始化一次
uwCRCValue = HAL_CRC_Accumulate(&hcrc, (uint32_t *)&CRC16_DATA8, BUFFER_SIZE);
//硬件计算CRC
printf("hCRC2=0x%X\r\n",uwCRCValue);
if (uwCRCValue != uwExpectedCRCValue)
{
printf("hCRC2 Error!\r\n");
}
}
5、测试结果和验证
6、CRC在线计算器
在线CRC计算工具:CRC(循环冗余校验)在线计算_ip33.com