首页 > 其他分享 >ARM32开发--CRC循环冗余校验

ARM32开发--CRC循环冗余校验

时间:2024-06-22 22:57:27浏览次数:21  
标签:crc -- 0x00 ARM32 校验码 CRC data uint32

CRC循环冗余校验

循环冗余校验码是一种用在数字网络和存储设备上的差错校验码,可以校验原始数据的偶然差

错。

CRC 计算单元使用固定多项式计算 32 位 CRC 校验码。

1. 硬件CRC

在单片机中,芯片具有专用的CRC计算单元,它是按照32位数据长度进行计算。 它相当于是我们的MCU有个小老弟,专门是干CRC计算的。

主要特征

1.1. 在C代码计算CRC

下面我们在C代码中去使用CRC循环冗余校验码:

  1. 在工程中导入gd32f4xx_crc.c文件
  2. 参考下面的示例代码,对数据进行计算
void example26_crc_test(){
  // 开启外设时钟
  rcu_periph_clock_enable(RCU_CRC);
  // 复位
  crc_deinit();
  // 数据重置
  crc_data_register_reset();
  // 要发送的数据
  uint32_t data[] = {0x00000001,0x00000002,0x00000003,0x00000004};
  // CRC校验码  
  uint32_t crc_data = crc_block_data_calculate((uint32_t*)data,4);
  
  printf("0x%X\r\n",crc_data);
}

1.2. 在线计算CRC

CRC(循环冗余校验)在线计算_ip33.com

1.3. 比对二者结果

通过下图,我们可以看到两端所计算的结果是相同的,说明数据在通讯的过程中,数据是正确的。

如果通讯的过程中,数据传错了,哪怕是错一位,最终计算出来的结果都是不一致的。

请问下图,我错在哪?

2. 软件CRC

在上面的案例中,我们是将数据传输给硬件计算单元去计算,但是芯片默认只支持32位的结果输出。但是在日常开发中,我们使用16位或者8位的情况非常多,所以我们无法直接使用硬件CRC,这个时候,咱们就得使用软件CRC自己来计算。

好的,我来简单地描述一下如何在软件中实现CRC(循环冗余校验)的步骤:

选择多项式生成器: 首先需要确定使用哪种CRC多项式作为生成器。常见的有CRC-16、CRC-32等多种选择,需要根据实际需求选择合适的生成器。

例如在我们硬件CRC,芯片内部默认使用的多项式生成器是 0x4C11DB7

对每个输入数据位执行以下步骤:

a. 取一个字节数据,将字节左移到最高处

b. 将左移之后的数据和初值进行异或处理,将结果作为新初值

c. 处理新初值每一位, 判断最高位是否是1:

  1. 如果是1,则新初值左移1位后和多项式进行异或,将结果设置为新初值
  2. 如果是0,仅左移。

重复步骤3,直到所有输入数据位都处理完毕。

输出结果: 此时出书的就是最终的CRC校验码。

通常,我们进行数据的传输都是使用字节进行传输的,所以在以下的案例中,我们的数据都是按照1字节的方式进行计算

2.1. 32位CRC

uint32_t calculate_crc32(uint8_t *data, uint32_t length) {
    uint32_t crc  = 0xFFFFFFFF;
    uint32_t poly = 0x04C11DB7;  
  
    for(int i=0; i< length;i++){
        // 1. 取出1个字节
        uint32_t d = data[i];
        // 2. 将数据进行左移,最高处是32位,传入的数据是8位的,所以左移24
        crc = crc ^ (d << 24);
        // 3. 处理新初值每一位
        for (int j = 0; j < 8; j++) {
            if (crc & 0x80000000) { // 若最高位是1,则左移1位与多项式进行异或
                crc = (crc << 1) ^ poly;
            } else {
                crc = crc << 1;   //若最高位不是1, 则右移一位
            }
        }
    }
    return crc;
}

void example27_crc_soft_test(){
  
  uint8_t data[] = {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04};
  // CRC校验码  
  uint32_t crc_data = calculate_crc32(data,16);
  
  printf("0x%X\r\n",crc_data);
}

2.2. 16位CRC

uint32_t calculate_crc16(uint8_t *data, uint32_t length) {
    uint32_t crc  = 0xFFFF;
    uint32_t poly = 0x8005;  
  
    for(int i=0; i< length;i++){
        // 1. 取出1个字节
        uint32_t d = data[i];
        // 2. 将数据进行左移,最高处是16位,传入的数据是8位的,所以左移8
        crc = crc ^ (d << 8);
        // 3. 处理新初值每一位
        for (int j = 0; j < 8; j++) {
            if (crc & 0x8000) { // 若最高位是1,则左移1位与多项式进行异或
                crc = (crc << 1) ^ poly;
            } else {
                crc = crc << 1;   //若最高位不是1, 则右移一位
            }
        }
    }
    return crc;
}

测试代码

  uint8_t data16[] = {0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04};
  uint16_t crc_data = calculate_crc16(data16,8);
  
  printf("0x%X\r\n",crc_data);

执行代码,查看代码运行结果是否和网页计算一致

2.3. 8位CRC

uint32_t calculate_crc8(uint8_t *data, uint32_t length) {
    uint32_t crc  = 0xFF;
    uint32_t poly = 0x80;  
  
    for(int i=0; i< length;i++){
        // 1. 取出1个字节
        uint8_t d = data[i];
        // 2. 将数据进行左移,最高处是8位,传入的数据是8位的,所以左移0
        crc = crc ^ (d << 0);
        // 3. 处理新初值每一位
        for (int j = 0; j < 8; j++) {
            if (crc & 0x80) { // 若最高位是1,则左移1位与多项式进行异或
                crc = (crc << 1) ^ poly;
            } else {
                crc = crc << 1;   //若最高位不是1, 则右移一位
            }
        }
    }
    return crc;
}
uint8_t data8[] = {0x01,0x02,0x03,0x04};
uint8_t crc_data8 = calculate_crc8(data8,4);
printf("0x%X\r\n",crc_data8);

在网页中查看执行结果,比对与程序执行输出的结果是否一致

3. 模拟数据传输

在该案例中,我们使用单片机作为发送端,使用python客户端作为接收端。

  1. 在单片机端, 我们先对数据计算一个CRC校验码
  2. 在接收端,我们重新对数据计算一个CRC校验码
  3. 比对收到的校验码和自己计算出来的校验码
    1. 若相同,则数据校验成功
    2. 若不同,则数据校验失败

3.1. 在python中实现CRC8

按照前面的步骤,我们将CRC算法在python中实现一遍


def crc8(data, init_crc):
    poly = 0x80
    
    for d in data:
        init_crc = init_crc^d # 1.
        for i in range(8):
            if init_crc&0x80:
                init_crc = (init_crc<<1)^poly
            else:
                init_crc = init_crc << 1
    
    return init_crc&0xff  # 这里的0xff只是为了去取出8位数据

3.2. 单片发出数据

在单片机中,我们循环的去发送数据


  while(1){
    // 假设我们要发送的数据是前4个字节
    uint8_t data8[5] = {0x01,0x02,0x03,0x04,0};
    // 计算数据的CRC校验码
    uint8_t crc_data8 = calculate_crc8(data8,4);
    // 将CRC校验码拼接到要发送数据的末尾
    data8[4] = crc_data8;
    
    usart0_dma_send_data(data8,5);
    
    delay_1ms(2000);
  }

3.3. python接收数据

在python客户端中,我们要干如下几件事:

  1. 接收到客户端数据
  2. 将数据转成字节数组
  3. 从字节数组中拆分出数据部分和校验码部分
  4. 使用crc校验算法,对数据部分重新计算校验码
  5. 比对计算出来的校验码和接收到的校验码
    1. 若相同,则校验成功
    2. 若不同,则校验失败,该条数据就是辣鸡
ser = serial.Serial("COM58", 115200, timeout=3000)
print("ser is open:",ser.isOpen())

while True:
    # print(ser)
    count = ser.in_waiting
    # 先找到头
    if count >= 5:
        data = ser.read(count)
        print(f"count={count},data={data}");
       
        if count == 5:
            result = struct.unpack('<BBBBB',data )
            print(f"result={result}")
            # 将数据部分取出来,计算校验码
            code = crc8(result[0:4],0xff)
            print("tx_code=0x{:02X}, rx_code=0x{:02X}".format(result[4],code)) 

    time.sleep(0.001)
    
ser.close()

标签:crc,--,0x00,ARM32,校验码,CRC,data,uint32
From: https://blog.csdn.net/xuewenyu_/article/details/139819769

相关文章

  • 探索图神经网络(GNN):使用Python实现你的GNN模型
    一、引言图神经网络(GraphNeuralNetwork,GNN)作为近年来机器学习和深度学习领域的热门话题,正逐渐吸引越来越多的研究者和开发者的关注。GNN能够处理图结构数据,在社交网络分析、推荐系统、化学分子结构预测等领域有着广泛的应用。本文将带你一步一步使用Python实现一个基本的......
  • 一千题,No.0093(延迟的回文数)
    给定一个 k+1 位的正整数 N,写成 ak​⋯a1​a0​ 的形式,其中对所有 i 有 0≤ai​<10 且 ak​>0。N 被称为一个回文数,当且仅当对所有 i 有 ai​=ak−i​。零也被定义为一个回文数。非回文数也可以通过一系列操作变出回文数。首先将该数字逆转,再将逆转数与该数相加......
  • openfly:基于nginx的4层代理管理平台
    简介作者:京城郭少基于nginx的4层代理管理平台支持的功能:被动健康检查白名单include导入文件哈希backup冗余互备weight权重注释......部署openfly部署nginx:目标:部署一个支持stream模块的nginx。步骤仅供参考,可自行发挥。systemctlstopfirewalldsystemct......
  • 【C#进阶】单元测试_2024-06-22
    单元测试什么是单元测试?想象一下,你在做一道大菜,每种食材的准备就是一个个小任务。单元测试就像是在烹饪前检查每样食材是否新鲜、切割是否恰当。在编程中,一个“单元”通常指的是代码中的最小可测试部分,比如一个方法。单元测试就是编写一小段代码,专门用来检查这个方法是否按预期......
  • 域渗透之ATT&CK实战系列——红队实战(一)
    目录前言环境搭建外围打点信息收集phpmyadmin全局日志getshell内网信息收集msf上线mimikatz抓取明文密码&hash域信息收集横向移动msf+proxychains搭建socks5隧道MS08-067漏洞利用MS17-010漏洞利用获取域控服务器总结前言这个靶场是红日安全团队推出的红队实战系列第一个靶场,其中......
  • 离线安装 VS Code Server
    离线安装VSCodeServerVSCode提供了两种连接服务器的方法,分别使用Remote-SSH和Remote-Tunnels插件。本文介绍使用Remote-SSH连接服务器。VSCode连接服务器安装Remote-SSH插件点击左侧的扩展按钮(或用Ctrl+Shift+X),搜索插件Remote-SSH进行安装离线安装VSCo......
  • 300部幼儿园儿歌舞蹈视频大合集,家有萌娃必备
    亲爱的家长朋友们,您是否正在寻找适合您孩子的有趣活动,既能激发他们的艺术天赋,又能让他们在快乐中成长?我们为您精心准备了一个包含300部作品的幼儿园儿歌舞蹈视频大合集,让您的萌娃在家也能享受幼儿园的乐趣!《你好你好》教会孩子们见面时的礼貌用语,通过活泼的舞蹈动作,让孩子们学......
  • 设计模式之-代理模式
    代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对其他对象的访问。代理模式主要解决的问题是在某些情况下,直接访问一个对象可能会带来一些问题,比如对象的创建和销毁需要额外的开销,或者需要对对象的访问进行控制和限制。需要使用代理模式的时候,通常有以下情况:对......
  • 将socks5列表转换成sing-box格式的json
    将socks5列表转换成sing-box格式的jsonfunctiongetRowMap($tag,$server,$port,$user,$pass){$mRow=[];$mRow['tag']=(string)$tag;$mRow['type']='socks';$mRow['version']='5';$mRow[......
  • 用TP5编写上传多张图片的功能
    这篇文章给大家分享的是用TP5怎样编写上传多张图片的功能。小编觉得挺实用的,因此分享给大家做个参考,实现效果及代码如下,文中示例代码介绍的非常详细,感兴趣的朋友接下来一起跟随小编看看吧。    1、效果图(每点击一次‘添加选项',就会有一个新的file框来添加新的图片)   ......