当前位置:首页 » 《我的小黑屋》 » 正文

C语言CRC校验代码(CRC16)(CRC函数、CRC校验函数解析、循环冗余校验、在线CRC校验、CRC在线、CRC网站)

5 人参与  2024年09月11日 16:41  分类 : 《我的小黑屋》  评论

点击全文阅读


文章目录

直接上代码函数调用在线CRC校验网站 CRC16校验函数解析初始化CRC值主循环XOR操作(异或操作)内部循环:位处理条件判断与多项式运算疑问为什么是crc & 0x0001而不是crc & 0x1、crc & 0x01或者crc & 0x00000001? 多项式`0xA001`总结 实际计算示例初始化变量第一轮计算(对数据 `0x03`)1. XOR操作结果 2. 进行8次循环,每次检查 `crc` 的最低位,并根据该位是0还是1进行操作 第二轮计算(对数据 `0x04`)示例演示处理第一个字节(0x03)处理第二个字节(0x04)

直接上代码

函数

uint16_t Calculate_CRC16(const uint8_t *data, uint16_t length){    uint16_t crc = 0xFFFF;    for (uint16_t i = 0; i < length; i++)    {        crc ^= data[i]; // 直接 XOR 低字节,因为多项式是反转的        for (uint8_t j = 0; j < 8; j++)        {            if (crc & 0x0001)              // 检查最低位                crc = (crc >> 1) ^ 0xA001; // 右移并应用多项式            else                crc >>= 1; // 只右移        }    }    return crc;}

调用

int VendorB_PowerStart(serial_t *serial, char **errorMsg){    // 后面可能有web设置报警器喇叭静音功能,到时提供一个全局变量和设置接口,本函数就只被算法触发的时候调,本函数会读全局变量    uint8_t index = 0;    uint8_t tbuf[128]; // 局部请求缓冲区    uint8_t rbuf[128]; // Response buffer    // 构建请求数据包    tbuf[index++] = ClientInfor.devaddr; // 地址(设备地址)    tbuf[index++] = 0x10;                // 功能码(对于此声光报警器控制板:把设置的数值写入指定的连续寄存器)    tbuf[index++] = 0x04;                // 寄存器地址(高位)    tbuf[index++] = 0x0E;                // 寄存器地址(低位)    tbuf[index++] = 0x00;                // 寄存器个数(高位)    tbuf[index++] = 0x05;                // 寄存器个数(低位)    tbuf[index++] = 0x0A;                // 寄存器字节数(一个寄存器——高位+低位,有四个16进制数,等于两个字节)    tbuf[index++] = 0x00;                // 修改数据(高位)—— 报警器状态    tbuf[index++] = 0x03;                // 修改数据(低位)    tbuf[index++] = 0x00;                // 修改数据(高位)—— 报警器音量    tbuf[index++] = 0x1E;                // 修改数据(低位)    tbuf[index++] = 0x01;                // 修改数据(高位)—— 报警器音调    tbuf[index++] = 0x01;                // 修改数据(低位)    tbuf[index++] = 0x00;                // 修改数据(高位)—— 报警器播放模式    tbuf[index++] = 0x01;                // 修改数据(低位)    tbuf[index++] = 0x01;                // 修改数据(高位)—— 警示灯闪烁模式(调试发现只支持默认模式,即 0x01 0x01)    tbuf[index++] = 0x01;                // 修改数据(低位)    // 默认出厂配置    // 0x040E  00H, 03H 报警器和警示灯都打开    // 0x040F  00H, 1EH 音量为 30 级;    // 0x0410  01H, 01H 第1个文件夹的第1个语音;    // 0x0411  00H, 01H 单曲循环播放;    // 0x0412  01H, 01H 警示灯不与报警器同步,模式为爆闪模式;    // Calculate CRC16,注意CRC16先发低位再发高位    uint16_t crc = Calculate_CRC16(tbuf, index);    tbuf[index++] = crc & 0xFF;        // CRC16(低位)    tbuf[index++] = (crc >> 8) & 0xFF; // CRC16(高位)    if (MODBUS_SendReceive(serial, tbuf, index, rbuf, sizeof(rbuf), errorMsg) != 0)    {        return -1;    }    // 这里可以添加对响应数据的验证逻辑    // 例如验证响应的正确性或解析错误代码等    return 0;}

在这里插入图片描述
在这里插入图片描述

在线CRC校验网站

可用在线crc16校验网站来检验:CRC在线计算

01 10 04 0E 00 05 0A 00 03 00 1E 01 01 00 01 01 01

将发送数据(CRC校验码之前的数据)粘贴进去计算,结果与我们代码计算结果一致:

在这里插入图片描述

CRC16校验函数解析

uint16_t Calculate_CRC16(const uint8_t *data, uint16_t length){    uint16_t crc = 0xFFFF;    for (uint16_t i = 0; i < length; i++)    {        crc ^= data[i]; // 直接 XOR 低字节,因为多项式是反转的        for (uint8_t j = 0; j < 8; j++)        {            if (crc & 0x0001)              // 检查最低位                crc = (crc >> 1) ^ 0xA001; // 右移并应用多项式            else                crc >>= 1; // 只右移        }    }    return crc;}

这段C语言代码是实现了CRC-16(循环冗余校验)的一个常见版本。CRC-16是一种检测数据错误的方法,广泛用于通信和存储系统中。让我们逐步解释这段代码的关键部分:

初始化CRC值

uint16_t crc = 0xFFFF;

这行代码初始化了CRC的值为0xFFFF。这是开始计算前的标准初始值,用于确保CRC的高位在开始计算前就已经设置好了。

主循环

for (uint16_t i = 0; i < length; i++)

这个循环负责遍历输入的数据数组data,其中length是数组中数据的个数。每次循环处理一个字节。

XOR操作(异或操作)

crc ^= data[i];

在这一行中,当前的CRC值与数据的当前字节进行异或运算(XOR)。异或运算是一种基础的位操作,用于CRC计算中以整合当前字节的数据。

内部循环:位处理

for (uint8_t j = 0; j < 8; j++)

此循环对CRC值的每一位进行处理,总共循环8次,因为每个字节有8位。

条件判断与多项式运算

if (crc & 0x0001)    crc = (crc >> 1) ^ 0xA001;else    crc >>= 1;

这里是CRC计算的核心部分。首先检查CRC的最低位(通过与0x0001进行AND运算)。如果最低位是1(表示当前的CRC是奇数),则CRC值先向右移一位,然后与多项式0xA001进行异或运算。如果最低位是0,则CRC值只向右移一位。

疑问
为什么是crc & 0x0001而不是crc & 0x1、crc & 0x01或者crc & 0x00000001?

在C语言中,表达式 crc & 0x0001crc & 0x1crc & 0x01、和 crc & 0x00000001 都是用来检查变量 crc 的最低位是否为 1。这三个表达式的功能是完全相同的,因为它们都会与 crc 的最低位进行位与(AND)操作。差别只在于数值表示的方式,但这对操作结果没有影响。

具体来说:

crc & 0x0001:这里 0x0001 是一个16位的常量,表示的是最低位为1,其余位为0。使用这种格式有助于清晰地显示我们关注的是16位整数的最低位。crc & 0x01:与上面的表达式功能相同,只是以更简洁的形式表示。crc & 0x00000001:这是一个32位的常量,但因为 crcuint16_t(即16位无符号整数),所以额外的高位零没有任何作用。

通常,在编写代码时选择哪种形式取决于编程风格和可读性的考虑。例如,在处理16位数据时(我们函数中定义的crc变量是uint16_t类型的,16位),使用 0x0001 可以明确地表示这是一个16位操作,这对于代码的可读性和维护性是有益的。

多项式0xA001

多项式0xA001是反转多项式,它来自标准的CRC-16多项式0x8005。在这种情况下,因为CRC的计算从低位开始,所以使用了反转的多项式。

总结

通过这种方式,CRC算法可以检测数据中的小错误,如单个位的翻转,或者两个位的错误。每处理完一个数据字节后,CRC值会更新,最终的crc值就是数据的CRC-16校验码。这个校验码可以用来检查数据在传输或存储过程中是否被更改过。

实际计算示例

在这里插入图片描述

让我们通过一个简单的示例来详细展示 Calculate_CRC16 函数的工作流程。假设我们有一个简单的输入数据,该数据只包含两个字节:0x030x04。我们将一步步计算这个数据的 CRC-16。

初始化变量

crc = 0xFFFF:初始化CRC值为全1,即16位都是1。data = [0x03, 0x04]:输入数据。length = 2:数据长度为2个字节。

第一轮计算(对数据 0x03

1. XOR操作

crc 初始值为 0xFFFF 和数据 0x03 进行 XOR 操作。我们首先将这些数转换为二进制形式:

crc 初始值: 0xFFFF1111 1111 1111 1111数据: 0x030000 0000 0000 0011

进行 XOR 操作,即每一位相同则结果为 0,不同则结果为 1:

对比最右边的两位:11 XOR 11 = 00其余位因为 0x03 的高位都是0,所以 1 XOR 0 保持不变,即为 1
结果
1111 1111 1111 1111XOR 0000 0000 0000 0011----------------------1111 1111 1111 1100 → 十六进制为 0xFFFC
2. 进行8次循环,每次检查 crc 的最低位,并根据该位是0还是1进行操作
第1次循环: 初始 crc = 0xFFFC(二进制:1111 1111 1111 1100crc & 0x0001 => 结果为 0(最低位为0)右移 crc => crc = 0x7FFE(二进制:0111 1111 1111 1110第2次循环crc = 0x7FFEcrc & 0x0001 => 结果为 0右移 crc => crc = 0x3FFF(二进制:0011 1111 1111 1111第3次循环crc = 0x3FFFcrc & 0x0001 => 结果为 1右移并异或多项式 crc = (crc >> 1) ^ 0xA001 => crc = 0xD000(二进制:1101 0000 0000 0000) 继续上述循环至第8次,根据是否与1相与来决定是否应用多项式。 处理结束后的 crc(假设完成上述所有循环后的值)。

第二轮计算(对数据 0x04

进行类似的处理,从XOR开始,直至八次循环。

这个过程会根据数据和每次循环的情况而改变。每一步都检查最低位,并根据这个最低位决定是否应用多项式 0xA001,或者只是简单地右移。

示例演示

接下来,我将编写一个Python程序来模拟这个计算过程,并显示每步的详细结果。这样你就可以清晰地看到每一步的变化。

def calculate_crc16_demo(data):    crc = 0xFFFF    steps = []        for byte in data:        crc ^= byte        steps.append(f"After XOR with {byte:02X}: {crc:04X}")                for _ in range(8):            if crc & 0x0001:                crc = (crc >> 1) ^ 0xA001            else:                crc >>= 1            steps.append(f"Step after shift/XOR: {crc:04X}")        return crc, steps# Example data: [0x03, 0x04]data_example = [0x03, 0x04]final_crc, calculation_steps = calculate_crc16_demo(data_example)final_crc, calculation_steps

运行结果:

结果(33536, ['After XOR with 03: FFFC',  'Step after shift/XOR: 7FFE',  'Step after shift/XOR: 3FFF',  'Step after shift/XOR: BFFE',  'Step after shift/XOR: 5FFF',  'Step after shift/XOR: 8FFE',  'Step after shift/XOR: 47FF',  'Step after shift/XOR: 83FE',  'Step after shift/XOR: 41FF',  'After XOR with 04: 41FB',  'Step after shift/XOR: 80FC',  'Step after shift/XOR: 407E',  'Step after shift/XOR: 203F',  'Step after shift/XOR: B01E',  'Step after shift/XOR: 580F',  'Step after shift/XOR: 8C06',  'Step after shift/XOR: 4603',  'Step after shift/XOR: 8300'])

我们使用了两个字节的数据(0x030x04)进行CRC-16计算,最终的CRC结果是 0x8300(十六进制)。以下是详细的计算步骤:

初始CRC: 0xFFFF
处理第一个字节(0x03)
XOR操作后0xFFFC循环操作0x7FFE(右移)0x3FFF(右移)0xBFFE(右移后应用多项式 0xA0010x5FFF(右移后应用多项式 0xA0010x8FFE(右移后应用多项式 0xA0010x47FF(右移后应用多项式 0xA0010x83FE(右移后应用多项式 0xA0010x41FF(右移后应用多项式 0xA001
处理第二个字节(0x04)
XOR操作后0x41FB循环操作0x80FC(右移后应用多项式 0xA0010x407E(右移)0x203F(右移)0xB01E(右移后应用多项式 0xA0010x580F(右移后应用多项式 0xA0010x8C06(右移后应用多项式 0xA0010x4603(右移后应用多项式 0xA0010x8300(右移后应用多项式 0xA001

每一步我们都进行了检查 crc 的最低位,然后决定是否右移或右移后与多项式 0xA001 进行异或操作。这些步骤一起构成了CRC计算的完整过程。


点击全文阅读


本文链接:http://zhangshiyu.com/post/158352.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1