CRC校验 (计算方式) 通用源码(兼容CRC4/5/6/7/8/16/32/自定义)

下面是 CRC校验 通用代码。实际应用可以根据CRC类型裁剪代码,减少代码量。计算方式CRC 缺点计算量大,浪费CPU资源,查表方式可以减少计算量(查表方式源码链接

#include <stdio.h>
#include <string.h>


/**Private typedef*************************************************************/
/* USER CODE BEGIN PT */
typedef enum
{
	CRC4_ITU = 0,
	CRC5_EPC,
	CRC5_ITU,
	CRC5_USB,
	CRC6_ITU,
	CRC7_MMC,
	CRC8,
	CRC8_ITU,
	CRC8_ROHC,
	CRC8_MAXIM,
	CRC16_IBM,
	CRC16_MAXIM,
	CRC16_USB,
	CRC16_MODBUS,
	CRC16_CCITT,
	CRC16_CCITT_FALSE,
	CRC16_X25,
	CRC16_XMODEM,
	CRC16_DNP,
	CRC32,
	CRC32_MPEG2,
	CRC_NUM
}CrcTypeE;

typedef enum
{
    E_FALSE = 0, //假(错误)
    E_TRUE = !E_FALSE //真(正确)
}BoolTypeE;

typedef struct
{
	char *Name;
    unsigned char Width; //宽度,即CRC比特数。
    unsigned int  Poly; //生成多项式的简写,以16进制表示。例如:CRC-32即是0x04C11DB7,忽略了最高位的"1",即完整的生成项是0x104C11DB7。
    unsigned int  CrcInit; //初始值,这是算法开始时寄存器(crc)的初始化预置值,十六进制表示。
    unsigned int  XorOut; //计算结果与此参数异或后得到最终的CRC值。
    BoolTypeE RefIn; //待测数据的每个字节是否按位反转,E_TRUE或E_FALSE。
    BoolTypeE RefOut; //在计算后之后,异或输出之前,整个数据是否按位反转,E_TRUE或E_FALSE。
}CrcInfoTypeS;
/* USER CODE END PT */


/**Private variables***********************************************************/
/* USER CODE BEGIN PV */
const static CrcInfoTypeS s_crcInfoTab[CRC_NUM] =
{
    //CRC算法名称			宽度  多项式      初始值      结果异或值  输入反转    输出反转
    {"CRC4_ITU", 			4,     0x03,       0x00,       0x00,       E_TRUE,   E_TRUE},
    {"CRC5_EPC", 			5,     0x09,       0x09,       0x00,       E_FALSE,  E_FALSE},
    {"CRC5_ITU", 			5,     0x15,       0x00,       0x00,       E_TRUE,   E_TRUE},
    {"CRC5_USB", 			5,     0x05,       0x1F,       0x1F,       E_TRUE,   E_TRUE},
    {"CRC6_ITU", 			6,     0x03,       0x00,       0x00,       E_TRUE,   E_TRUE},
    {"CRC7_MMC", 			7,     0x09,       0x00,       0x00,       E_FALSE,  E_FALSE},
    {"CRC8", 				8,     0x07,       0x00,       0x00,       E_FALSE,  E_FALSE},
    {"CRC8_ITU", 			8,     0x07,       0x00,       0x55,       E_FALSE,  E_FALSE},
    {"CRC8_ROHC", 			8,     0x07,       0xFF,       0x00,       E_TRUE,   E_TRUE},
    {"CRC8_MAXIM", 			8,     0x31,       0x00,       0x00,       E_TRUE,   E_TRUE},
    {"CRC16_IBM", 			16,    0x8005,     0x0000,     0x0000,     E_TRUE,   E_TRUE},
    {"CRC16_MAXIM", 		16,    0x8005,     0x0000,     0xFFFF,     E_TRUE,   E_TRUE},
    {"CRC16_USB", 			16,    0x8005,     0xFFFF,     0xFFFF,     E_TRUE,   E_TRUE},
    {"CRC16_MODBUS",		16,    0x8005,     0xFFFF,     0x0000,     E_TRUE,   E_TRUE},
    {"CRC16_CCITT", 		16,    0x1021,     0x0000,     0x0000,     E_TRUE,   E_TRUE},
    {"CRC16_CCITT_FALSE",	16,    0x1021,     0xFFFF,     0x0000,     E_FALSE,  E_FALSE},
    {"CRC16_X25", 			16,    0x1021,     0xFFFF,     0xFFFF,     E_TRUE,   E_TRUE},
    {"CRC16_XMODEM", 		16,    0x1021,     0x0000,     0x0000,     E_FALSE,  E_FALSE},
    {"CRC16_DNP", 			16,    0x3D65,     0x0000,     0xFFFF,     E_TRUE,   E_TRUE},
    {"CRC32", 				32,    0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, E_TRUE,   E_TRUE},
    {"CRC32_MPEG2", 		32,    0x04C11DB7, 0xFFFFFFFF, 0x00000000, E_FALSE,  E_FALSE}
};
/* USER CODE END PV */


/**
 *******************************************************************************
 * @brief   位反转 函数
 * @param   [in] inVal	- 反转前数据
 * @param   [in] bits   - 反转位数
 * @return  反转后数据
 * @note    就是将高位与低位数据顺序反过来。
 *******************************************************************************
 */
unsigned int BitsReverse(unsigned int inVal, unsigned char bits)
{
    unsigned int outVal = 0;
    unsigned char i;
    
    for(i=0; i<bits; i++)
    {  
        if(inVal & (1 << i)) outVal |= 1 << (bits - 1 - i);
    }
    
    return outVal;
}

/**
 *******************************************************************************
 * @brief   获取 CRC(循环冗余校验) 函数
 * @param   [in] type   - CRC 类型
 * @param   [in] *buf   - 数据块指针
 * @param   [in] bufLen - 数据长度
 * @return  校验码
 * @note    
 *******************************************************************************
 */
unsigned int GetCRC(unsigned char type, unsigned char *buf, unsigned int bufLen)
{
    unsigned char width  = s_crcInfoTab[type].Width; //宽度,即CRC比特数。
    unsigned int  poly   = s_crcInfoTab[type].Poly; //生成多项式的简写,以16进制表示。例如:CRC-32即是0x04C11DB7,忽略了最高位的"1",即完整的生成项是0x104C11DB7。
    unsigned int  crc    = s_crcInfoTab[type].CrcInit; //初始值,这是算法开始时寄存器(crc)的初始化预置值,十六进制表示。
    unsigned int  xorout = s_crcInfoTab[type].XorOut; //计算结果与此参数异或后得到最终的CRC值。
    unsigned char refin  = s_crcInfoTab[type].RefIn; //待测数据的每个字节是否按位反转,E_TRUE或E_FALSE。
    unsigned char refout = s_crcInfoTab[type].RefOut; //在计算后之后,异或输出之前,整个数据是否按位反转,E_TRUE或E_FALSE。
    unsigned char n;
    unsigned int  bits;
    unsigned int  data;
    unsigned char i;
    
    n    =  (width<8) ? 0 : (width-8);
    crc  =  (width<8) ? (crc<<(8-width)) : crc; //CRC校验宽度小于8位,CRC初值移到高位计算
    bits =  (width<8) ? 0x80 : (1 << (width-1));
    poly =  (width<8) ? (poly<<(8-width)) : poly; //CRC校验宽度小于8位,CRC多项式移到高位计算
    while(bufLen--)
    {
        data = *(buf++);
        if(refin == E_TRUE) data = BitsReverse(data, 8); //数据输入是否反转
        crc ^= (data << n);
        for(i=0; i<8; i++)
        {
            if(crc & bits)
            {
                crc = (crc << 1) ^ poly;
            }
            else
            {
                crc = crc << 1;
            }
        }
    }
    crc = (width<8) ? (crc>>(8-width)) : crc; //CRC校验宽度小于8位,CRC值计算完成后移到原位
    if(refout == E_TRUE) crc = BitsReverse(crc, width); //CRC输出是否反转
    crc ^= xorout; //CRC结果异或

    return (crc & ((2<<(width-1)) - 1)); //获取有效位(也就是宽度位)
}

/**
 *******************************************************************************
 * @brief   主函数
 * @param   [in] None
 * @return  None
 * @note    验证校验函数
 *******************************************************************************
 */
void main()
{
    char *str = "Hello World!";
    unsigned int i;

    for(i=0; i<CRC_NUM; i++)
    {
        printf("%s (\"%s\") = 0x%0X\n", s_crcInfoTab[i].Name, str, GetCRC(i, (unsigned char*)str, strlen(str)));
    }
	
	while(1);
}

运行结果:

猜你喜欢

转载自blog.csdn.net/weixin_46672094/article/details/117136044