CRC16浅析

article/2025/8/22 13:13:07

CRC即循环冗余校验码(Cyclic Redundancy Check),是数据通信领域中最常用的一种查错校验码。奇偶校验虽然简单,但是漏检率太高,而CRC则要低的多,所以大多数都是使用CRC来校验。CRC也称为多项式码。

任何一个由二进制数位串组成的代码,都可以唯一的与一个只含有0和1两个系数的多项式建立一一对应的关系。如:1010111对应的多项式为X^6 + X^4 + X^2 + X + 1。

CRC码是利用事先约定好的多项式G(x)来得到的。k位要发送的信息位可对应于一个(k - 1)次多项式K(x),r位冗余位则对应于一个(r - 1)次多项式R(x)。k位信息位和r位冗余位组成的码字多项式为T(x) = x^r * K(x) + R(x)。因为补上了r位冗余为,所以K(x)要乘以x^r。

由信息位产生冗余位的编码过程,即已知K(x)求R(x)的过程。通过约定好的r次多项式G(x),最高项X^r的系数恒定为1,即r bit为1。然后用X^r * K(x) 去除以G(x),得到的余式就是R(x)。除法是模2除法,即使用异或运算。当被除数逐位除完时,最后得到的比除数少一位的余数,此余数即为冗余位。将其添加在信息位后面。

这里以K(x) = x^6 + x^4 + x^3 + 1为例(信息位即为1011001),设G(x) = x^4 + x^3 + 1(11001),则r = 4,x^4 * K(x) (即低位补r个0:10110010000)。

模2除法求余式R(x)的过程如下:

即冗余位为1010,R(x) = x^3 + x;

 

常见问题:

0,为什么带CRC的码字的余数为0,即校验成功?

1,多项式公式的代码

例如多项式公式:x16 + x12 + x5 + 1的代码是‭0001 0001 0000 0010 0001‬,即0x11021。次方为第几个bit,0次方等于1。

 

2,为什么多项式的最高项可以不写?

例:CRC-16/xmodem的多项式公式为:x16+x12+x5+1。即多项式为0x11021,但一般记为0x1021。多项式公式最高和最低位要求为1。下面以求0x03的crc为例,手动算出crc。

0x03的多项式为 K(x) = x+1;而G(x) = x16+x12+x5+1,用最高项x16*K(x)=x17+x16,用其除(模2除法)以G(x)求余。

 

 0011 0000  0000  0000  0000
     10 0010  0000  0100  001 
     01 0010  0000  0100  0010
       1 0001  0000  0010  0001 

      0 0011  0000  0110  0011

 

得crc为0x3063。由于多项式的最高位的1总是与被除数的最高位1异或得0,所以只要将信息位左移一位,再与去掉最高项的多项式异或即可(crc = (crc << 1) ^poly;)。

 

3,为什么有的要将多项式0x8005反转,写成0xA001?

由于CRC模型有很多,不只体现在多项式的不同,还有输入值,输出值的操作上,对其进行反转等。例如CRC16_CCITT,就要对其输入,输出值进行反转。如果要对其进行反转,势必会加重其运算效率。所以为了提高效率,可以把多项式反转一下。例如:0x8005 -> 0xA001,然后对信息位从最低位开始右移异或即可。

 

 

 

 

#include<stdio.h>
#include<stdbool.h>typedef unsigned short u16;const u16 crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};unsigned char char_invert(unsigned char ch)
{char i;unsigned char invert = 0;for (i = 0; i < 8; i++) {if (ch & 0x1)invert |= 0x01 << (7-i);ch >>= 1;}return invert;
}u16 short_invert(u16 sh)
{char i;u16 invert = 0;for (i = 0; i < 16; i++) {if (sh & 0x1)invert |= 0x01 << (15-i);sh >>= 1;}return invert;
}u16 crc16(u16 crc, u16 poly, bool input_invert, bool output_invert, u16 output_xor, unsigned char *buf, u16 len)
{unsigned char i;while (len--) {if (input_invert)crc ^= (char_invert((*buf++)) << 8);elsecrc ^= ((*buf++) << 8);for (i = 0; i < 8; i++) {if (crc & 0x8000) crc = (crc << 1) ^poly;elsecrc <<= 1;}}if (output_invert)return short_invert(crc)^output_xor;return crc^output_xor;
}u16 crc16_ibm(unsigned char *buf, u16 len)
{return crc16(0x0, 0x8005, true, true, 0x0,buf, len);
}u16 crc16_modbus(unsigned char *buf, u16 len)
{return crc16(0xffff, 0x8005, true, true, 0x0, buf, len);
}u16 crc16_ccitt(unsigned char *buf, u16 len)
{return crc16(0x0, 0x1021, true, true, 0x0, buf, len);
}u16 crc16_xmodem(unsigned char *buf, u16 len)
{return crc16(0x0, 0x1021, false, false, 0x0, buf, len);
}u16 crc16_x25(unsigned char *buf, u16 len)
{return crc16(0xffff, 0x1021, true, true, 0xffff, buf, len);
}static u16 crc16_byte(u16 crc, const unsigned char data)
{   return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
}u16 crc16_tab(u16 crc, unsigned char const *buffer, size_t len)
{while (len--)crc = crc16_byte(crc, *buffer++);return crc;
}


...

 

 

 

 

 


http://chatgpt.dhexx.cn/article/i7ApdEQx.shtml

相关文章

C语言版CRC-16系列校验算法

一、CRC16校验码的使用 现选择最常用的CRC-16校验&#xff0c;说明它的使用方法。 根据Modbus协议&#xff0c;常规485通讯的信息发送形式如下&#xff1a; 地址 功能码 数据信息 校验码 1byte 1byte nbyte 2byte CRC校验是前面几段数据内容的校验值&#xff0c;为一个16位数据…

LWIP使用经验

LWIP使用经验 一 LWIP内存管理 LWIP的内存管理使用了2种方式&#xff1a;内存池memp和内存堆mem&#xff0c;如图1所示。 内存池的特点是预先开辟多组固定大小的内存块组织成链表&#xff0c;实现简单&#xff0c;分配和回收速度快&#xff0c;不会产生内存碎片&#xff0c;…

Linux线程ID与内核LWP的关系

先给出图&#xff0c;然后我将根据图来讲解二者的关系&#xff1a; 线程ID 在使用线程前&#xff0c;我们需要链接线程的pthread第三方库&#xff0c;在Linux中&#xff0c;第三方库被加载到PCB进程中的虚拟地址空间中的栈区与堆区之间的共享区。 我们所创建线程的控制块即维护…

LWP(Library for WWW in Perl)的基本使用

LWP (“Library for WWW in Perl” 的缩写) 是一个由多个模块组成&#xff0c;用来获取网络数据的的模块组。 和很多 Perl 的模块一样。每一个 LWP 模块都自带详细的文档&#xff0c;做为对这个模块的完整介绍。可是面对 LWP 里的众多模块&#xff0c;有时候即使是完成最简单的…

Linux进程和轻量级进程(LWP)

1.父子进程可以共享含有程序代码的页,但是他们各自有独立的数据拷贝(堆和栈)。 2.进程描述符 3.进程的TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE区别

lwIP 开发指南

目录 lwIP 初探TCP/IP 协议栈是什么TCP/IP 协议栈架构TCP/IP 协议栈的封包和拆包 lwIP 简介lwIP 源码下载lwIP 文件说明 MAC 内核简介(STM32 内置)PHY 芯片介绍(以太网芯片 外置)YT8512C 简介LAN8720A 简介 以太网接入MCU 方案软件TCP/IP 协议栈以太网接入方案硬件TCP/IP 协议栈…

Linux:获取线程的PID(TID、LWP)的几种方式

Linux&#xff1a;获取线程的PID&#xff08;TID、LWP&#xff09;的几种方式 在 Linux C/C 中通常是通过 pthread 库进行线程级别的操作。 在 pthread 库中有函数&#xff1a; pthread_t pthread_self(void);它返回一个 pthread_t 类型的变量&#xff0c;指代的是调用 pthr…

lwip-简介

文章目录 简介层次划分传输过程 简介 lwip的介绍就不累赘了&#xff0c;网上有许多教程。这里推荐野火的<<《[野火]LwIP应用开发实战指南—基于野火STM32》—20210122.pdf>>介绍的非常详细。&#xff08;该文档我已上传到资源了&#xff09;我们记录主要是一些用法…

Linux下的LWP(轻量级进程)、进程 、 线程、用户级线程、内核线程

一、定义 再看正文之前我要先强调一下几点&#xff1a; 1. Linux中没有真正的线程&#xff0c;但windows中确实有线程 2. Linux中没有的线程是由进程来模拟实现的&#xff08;又称作&#xff1a;轻量级进程&#xff09; 3. 所以在Linux中&#xff08;在CPU角度看&#xff0…

LWIP框架

目录 协议栈分层思想 1. 网络接口层 2. 网络层 3. 传输层 4. 应用层 进程模型 单进程模型 协议栈编程接口 1、Raw/Callback API 2、Netconn API 3、Socket API 协议栈分层思想 TCP/IP协议完整的包含了一系列构成互联网基础的网络协议&#xff0c;TCP/IP协议的开发出…

lwip协议

LWIP版本号 野火LwIP应用开发实战指南&#xff1a;基于STM32 lwIP 2.1.0 Lightweight IP stack lwip的版本号在哪个文件里能看到&#xff1f; 关于LWIP几篇不错的文章分享 LwIP应用开发实战指南 LwIP多TCP连接问题 lwIP TCP/IP 协议栈笔记之十&#xff1a; LwIP 数据流框架 wir…

LWP 与 WEB 的基本使用

简介 LWP (“Library for WWW in Perl” 的缩写) 是一个由多个模块组成&#xff0c;用来获取网络数据的的模块组。 和很多 Perl 的模块一样。每一个 LWP 模块都自带详细的文档&#xff0c;做为对这个模块的完整介绍。可是面对 LWP 里的众多模块&#xff0c;有时候即使是完成最简…

LWIP协议与TCP/IP

1. 学习一个东西&#xff0c;先了解这个东西是干什么用的&#xff0c;哪些场景会用到它&#xff0c;与自己已经掌握的其他知识的联系 a. 例如&#xff1a;LWIP这个东西是干什么用的&#xff1a;他就是一个裁剪后保持大部分TCP/IP功能的协议。用少量的资源消耗实现一个较为完整的…

[Linux]线程概念_线程控制(线程与进程的区别与联系 | 线程创建 | 线程等待 | 线程终止 | 线程分离 | LWP)

文章目录 线程概念进程和线程的关系线程的优点线程的缺点 线程控制Linux线程和接口关系的认识线程创建线程ID及进程地址空间布局线程等待线程终止线程终止状态线程分离LWP和pthread_t 线程概念 线程是在进程内部运行的一个执行分支&#xff08;执行流&#xff09;&#xff0c;…

实现线程的三种方式KLT/ULT/LWP

大家好&#xff0c;我是神韵&#xff0c;是一个技术&生活博主。关于文章都是定位为基础&#xff0c;我不敢讲的太深入&#xff0c;因为我怕自己没时间。欢迎来点赞打卡&#xff0c;你们的行动将是我无限的动力。 今日主题是&#xff1a;实现线程的三种方式KLT/LWP/ULT 三种…

Linux——一文彻底了解进程id和线程id的关系(什么是pid、tgid、lwp、pthread_t)

目录 一.内核层面&#xff1a;pid & tgid 二.函数调用层面&#xff1a;getpid & gettid & pthread_self 三.用户层面&#xff1a;PID & LWP&#xff08;TID&#xff09; 四.总结 一.内核层面&#xff1a;pid & tgid 首先&#xff0c;我们要清楚&#…

LWIP简介

1介绍 lwIP 是 TCP/IP 协议套件的小型独立实现。lwIP TCP/IP 实现的重点是减少 RAM 使用同时仍然有一个完整的 TCP。这使得 lwIP 适合使用在具有数十 KB 可用 RAM 和空间的嵌入式系统中大约 40 KB 的代码 ROM。 lwIP 最初由计算机和网络的 Adam Dunkels 开发瑞典计算机科学研究…

Linux进程、线程模型,LWP,pthread_self()

一&#xff0e;定义 关于进程、轻量级进程、线程、用户线程、内核线程的定义&#xff0c;这个很容易找到&#xff0c;但是看完之后你可以说你懂了&#xff0c;但实际上你真的明白了么&#xff1f; 在现代操作系统中&#xff0c;进程支持多线程。进程是资源管理的最小单元&…

Linux下线程(LWP)的相关概念

一.概念 线程&#xff1a;一个进程内部的控制序列。或者说在一个程序里的一个执行路线 首先明确一个概念&#xff0c;在Linux下是没有进程的控制块的&#xff0c;使用进程模拟的线程。一个进程中至少有一个线程。所以进程跟线程的数量是一对&#xff08;一&#xff09;多的。…

Linux-线程(LWP)

文章目录 线程线程概念进程今天的进程 vs之前的进程私有和共享资源实验验证线程的优点&#xff1a;线程的缺点&#xff1a;线程异常线程的用途&#xff1a; 线程控制创建线程(1)先创建两个线程&#xff1a;链接时要引入第三方库。(2)创建多个线程&#xff1a;(3)线程的健壮性不…