当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]CRC校验源码学习

CRC 根据”权”(即多项表达式)的不同而相应的源代码也有稍许不同。以下是各种常用的权。

  CRC8=X8+X5+X4+1

  CRC-CCITT=X16+X12+X5+1

  CRC16=X16+X15+X5+1

  CRC12=X12+X11+X3+X2+1

  CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1

  以下的源程序全部以 CCITT 为例。其实本质都是一样,搞明白一种,其他的都是小菜。

  图 1,图 2 说明了 CRC 校验中 CRC 值是如何计算出来的,体现的多项式正是 X16+X12+X5+1。 Serial Data 即是需要校验的数据。从把数据移位开始计算,将数据位(从最低的数据位开始)逐位移入反向耦合移位寄存器(这个名词我也不懂,觉得蛮酷的,就这样写了,嘿)。当所有数据位都这样操作后,计算结束。此时,16 位移位寄存器中的内容就是 CRC 码。

  


 

  

 

  图中进行 XOR 运算的位与多项式的表达相对应。

  X5 代表 Bit5,X12 代表 Bit12,1 自然是代表 Bit0,X16 比较特别,是指移位寄存器移出的数据,即图中的DATA OUT。可以这样理解,与数据位做XOR运算的是上次 CRC值的 Bit15。

  根据以上说明,可以依葫芦画瓢的写出以下程序。(程序都是在 keil C 7.10 下调试的)

  typedef unsigned char uchar;

  typedef unsigned int uint;

  code uchar crcbuff [] = { 0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3};

  uint crc; // CRC 码

  void main(void)

  {

  uchar *ptr;

  crc = 0; // CRC 初值

  ptr = crcbuff; // 指向第一个 Byte 数据

  crc = crc16l(ptr,8);

  while(1);

  }

  uint crc16l(uchar *ptr,uchar len) // ptr 为数据指针,len 为数据长度

  {

  uchar i;

  while(len--)

  {

  for(i=0x80; i!=0; i>>=1)

  {

  if((crc&0x8000)!=0) {crc<<=1; crc^=0x1021;} 1-1

  else crc<<=1; 1-2

  if((*ptr&i)!=0) crc^=0x1021; 1-3

  }

  ptr++;

  }

  return(crc);

  }

  执行结果 crc = 0xdbc0;

  程序 1-1,1-2,1-3 可以理解成移位前 crc 的 Bit15 与数据对应的 Bit(*ptr&i)做 XOR运算,根据此结果来决定是否执行 crc^=0x1021。只要明白两次异或运算与原值相同,就不难理解这个程序。

  很多资料上都写了查表法来计算,当时是怎么也没想通。其实蛮简单的。假设通过移位处理了 8 个 bit 的数据,相当于把之前的 CRC 码的高字节(8bit)全部移出,与一个 byte 的数据做XOR 运算,根据运算结果来选择一个值(称为余式),与原来的 CRC 码再做一次 XOR 运算,就可以得到新的 CRC 码。

  不难看出,余式有 256 种可能的值,实际上就是 0~255 以 X16+X12+X5+1 为权得到的 CRC码,可以通过函数 crc16l来计算。以1 为例。

  code test[]={0x01};

  crc = 0;

  ptr = test;

  crc = crc16l(ptr,1);

  执行结果 crc = 1021,这就是1 对应的余式。

  进一步修改函数,我这里就懒得写了,可得到 X16+X12+X5+1 的余式表。

  code uint crc_ta[256]={ // X16+X12+X5+1 余式表

  0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,

  0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,

  0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,

  0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,

  0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,

  0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,

  0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,

  0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,

  0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,

  0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,

  0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,

  0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,

  0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,

  0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,

  0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,

  0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,

  0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,

  0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,

  0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,

  0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,

  0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,

  0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,[!--empirenews.page--]

  0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,

  0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,

  0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,

  0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,

  0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,

  0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,

  0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,

  0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,

  0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,

  0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0

  };

  根据这个思路,可以写出以下程序:

  uint table_crc(uchar *ptr,uchar len) // 字节查表法求 CRC

  {

  uchar da;

  while(len--!=0)

  {

  da=(uchar) (crc/256); // 以 8 位二进制数暂存 CRC 的高 8 位

  crc<<=8; // 左移 8 位

  crc^=crc_ta[da^*ptr]; // 高字节和当前数据 XOR 再查表

  ptr++;

  }

  return(crc);

  }

  本质上 CRC 计算的就是移位和异或。所以一次处理移动几位都没有关系,只要做相应的处理就好了。

  下面给出半字节查表的处理程序。其实和全字节是一回事。

  code uint crc_ba[16]={

  0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,

  0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,

  };

  uint ban_crc(uchar *ptr,uchar len)

  {

  uchar da;

  while(len--!=0)

  {

  da = ((uchar)(crc/256))/16;

  crc <<= 4;

  crc ^=crc_ba[da^(*ptr/16)];

  da = ((uchar)(crc/256)/16);

  crc <<= 4;

  crc ^=crc_ba[da^(*ptr&0x0f)];

  ptr++;

  }

  return(crc);

  }

  crc_ba[16]和crc_ta[256]的前 16 个余式是一样的。

  其实讲到这里,就已经差不多了。反正当时我以为自己是懂了。结果去看别人的源代码的时候,也是说采用 CCITT,但是是反相的。如图 3

  

 

  反过来,一切都那么陌生,faint.吐血,吐血。

  仔细分析一下,也可以很容易写出按位异或的程序。只不过由左移变成右移。

  uint crc16r(unsigned char *ptr, unsigned char len)

  {

  unsigned char i;

  while(len--!=0)

  {

  for(i=0x01;i!=0;i <<= 1)

  {

  if((crc&0x0001)!=0) {crc >>= 1; crc ^= 0x8408;}

  else crc >>= 1;

  if((*ptr&i)!=0) crc ^= 0x8408;[!--empirenews.page--]

  }

  ptr++;

  }

  return(crc);

  }

  0x8408 就是 CCITT 的反转多项式。

  套用别人资料上的话

  “反转多项式是指在数据通讯时,信息字节先传送或接收低位字节,如重新排位影响 CRC计算速度,故设反转多项式。”

  如

  code uchar crcbuff [] = { 0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3};

  反过来就是

  code uchar crcbuff_fan[] = {0xe3,0xd2,0x0d,0x06,0x00,0x00,0x00,0x00};

  crc = 0;

  ptr = crcbuff_fan;

  crc = crc16r(ptr,8);

  执行结果 crc = 0x5f1d;

  如想验证是否正确,可改

  code uchar crcbuff_fan_result[] = {0xe3,0xd2,0x0d,0x06,0x00,0x00,0x00,0x00,0x1d,0x5f};

  ptr = crcbuff_fan_result;

  crc = crc16r(ptr,10);

  执行结果 crc = 0; 符合 CRC 校验的原理。

  请注意 0x5f1d 在数组中的排列中低位在前,正是反相运算的特点。不过当时是把我搞的晕头转向。

  在用半字节查表法进行反相运算要特别注意一点,因为是右移,所以 CRC 移出的 4Bit与数据 XOR 的操作是在 CRC 的高位端。因此余式表的产生是要以下列数组通过修改函数crc16r 产生。

  code uchar ban_fan[]=

 {0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,

0xa0,0xb0,0xc0,0xd0,0xe0,0xf0};

  得出余式表

  code uint fan_yushi[16]={

  0x0000, 0x1081, 0x2102, 0x3183,

  0x4204, 0x5285, 0x6306, 0x7387,

  0x8408, 0x9489, 0xa50a, 0xb58b,

  0xc60c, 0xd68d, 0xe70e, 0xf78f

  };

  uint ban_fan_crc(uchar *ptr,uchar len)

  {

  uchar da;

  while(len--!=0)

  {

  da = (uchar)(crc&0x000f);

  crc >>= 4;

  crc ^= fan_yushi [da^(*ptr&0x0f)];

  da = (uchar)(crc&0x000f);

  crc >>= 4;

  crc ^= fan_yushi [da^(*ptr/16)];

  ptr++;

  }

  return(crc);

  }

  主程序中

  crc = 0;

  ptr = crcbuff_fan;

  crc = ban_fan_crc(ptr,8);

  执行结果 crc = 0x5f1d;

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭