http://www.stmcu.org/chudonganjin/blog/12-08/230184_515e6.html

1、循环校验码(CRC码):

是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。

2、生成CRC码的基本原理:

任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。

标准CRC生成多项式如下表:

名称          生成多项式              简记式*   标准引用

CRC-4         x4+x+1                  3         ITU G.704

CRC-8         x8+x5+x4+1              0x31

CRC-8         x8+x2+x1+1              0x07

CRC-8         x8+x6+x4+x3+x2+x1       0x5E

CRC-12        x12+x11+x3+x+1          80F

CRC-16        x16+x15+x2+1            8005      IBM SDLC

CRC16-CCITT  x16+x12+x5+1   1021   ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS

CRC-32      x32+x26+x23+...+x2+x+1 04C11DB7 ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCS

CRC-32c     x32+x28+x27+...+x8+x6+1 1EDC6F41     SCTP

3、CRC-16校验码的使用:

    现选择最常用的CRC-16校验,说明它的使用方法。

根据Modbus协议,常规485通讯的信息发送形式如下:

地址  功能码   数据信息  校验码

1byte   1byte   nbyte    2byte

CRC校验是前面几段数据内容的校验值,为一个16位数据,发送时,低8位在前,高8为最后。

例如:信息字段代码为: 1011001,校验字段为:1010。

发送方:发出的传输字段为:  1 0 1 1 0 0 1 1 0 10

                          信息字段       校验字段

接收方:使用相同的计算方法计算出信息字段的校验码,对比接收到的实际校验码,如果相等及信息正确,不相等则信息错误;或者将接受到的所有信息除多项式,如果能够除尽,则信息正确。

4、CRC-16校验码计算方法:

常用查表法和计算法。计算方法一般都是:
(1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器;
(2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低
       8位相异或,把结果放于CRC寄存器,高八位数据不变;
(3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位;
(4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多

项式A001(1010 0000 0000 0001)进行异或;
(5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
(6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
(7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低
       字节进行交换;
(8)、最后得到的CRC寄存器内容即为:CRC码。

以上计算步骤中的多项式A001是8005按位颠倒后的结果。

查表法是将移位异或的计算结果做成了一个表,就是将0~256放入一个长度为16位的寄存器中的低八位,高八位填充0,然后将该寄存器与多项式0XA001按照上述3、4步骤,直到八位全部移出,最后寄存器中的值就是表格中的数据,高八位、低八位分别单独一个表。

5、提供两个经典的程序示例(皆验证通过)

(1)     C查表法版本:

特点:速度快,语句少,但表格占用一定的程序空间。

*pucFrame 为待校验数据首地址,usLen为待校验数据长度。返回值为校验结果。

USHORT usMBCRC16( UCHAR * pucFrame, USHORT usLen )

{

UCHAR ucCRCHi = 0xFF;

UCHAR ucCRCLo = 0xFF;

int iIndex;

while( usLen-- )

{

iIndex = ucCRCLo ^ *( pucFrame++ );

ucCRCLo = ( UCHAR )( ucCRCHi ^ aucCRCHi[iIndex] );

ucCRCHi = aucCRCLo[iIndex];

}

return ( USHORT )( ucCRCHi << 8 | ucCRCLo );

}

static const UCHAR aucCRCHi[] = {

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40

};

static const UCHAR aucCRCLo[] = {

0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,

0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,

0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,

0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,

0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,

0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,

0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,

0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,

0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,

0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,

0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,

0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,

0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,

0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,

0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,

0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,

0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,

0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,

0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,

0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,

0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,

0x41, 0x81, 0x80, 0x40

};

(2)     汇编计算法版本:

    特点:需要计算n*8次(n为信息字节数),运行速度慢,占用程序时间,但节省空间资源。

TEMP             EQU    40H
CHKSUMBYL   EQU    46H                  ;校验和低字节
CHKSUMBYH   EQU    47H                  ;校验和高字节
DATALENGTH  EQU    4FH                  ;待校验的数据串长度

ORG   0000H
 MOV   TEMP,#1EH
 MOV   TEMP+1,#6
 MOV   TEMP+2,#20H
 MOV   TEMP+3,#0
 MOV   TEMP+4,#0
 MOV   TEMP+5,#2
 LCALL MAKE_CHKSUM
 SJMP  $
 ;--------------------------------------------------------------------------
;运行:  1E 06 20 00 00 02 01 A4  ,16进制,设备地址,命令,存储器地址高,存储器地址低,参数高,参数低,校验低,校验高。
;---------------------------------------------------------------------------
MAKE_CHKSUM:         ;RTU 模式,CRC - 16 校验,用软件模拟仿真检查无误
          MOV   R0,#TEMP
        MOV   CHKSUMBYL,#0FFH  ;1.预置 16 位寄存器为十六进制 FFFF(即全为 1),低字节
        MOV   CHKSUMBYH,#0FFH  ;  预置 16 位寄存器为十六进制 FFFF(即全为 1),高字节
        MOV   DATALENGTH,#6         ;待校验的数据串长度
CHKSUM_LP1:
        MOV   A,@R0           ;2.把第一个 8 位数据与 16 位 CRC 寄存器的低位相异或,
        XRL   A,CHKSUMBYL
        MOV   CHKSUMBYL,A           ;并把结果放于CRC 寄存器
        MOV   R7,#8
CHKSUM_LP2:
         MOV   A,CHKSUMBYH
         CLR   C
         RRC   A                   ;把寄存器的内容右移一位(朝低位),先移动高字节
         MOV   CHKSUMBYH,A
         MOV   A,CHKSUMBYL
         RRC   A                     ;再移动低字节
         MOV   CHKSUMBYL,A
         JNC   CHKSUM_JP  ;4.检查最低位(移出位),如果最低位为 0 ,重复第 3 步(再次移位)
         MOV   A,CHKSUMBYL
         XRL   A,#01H             ;如果最低位为 1,CRC 寄存器与多项式 A001 进行异或
         MOV   CHKSUMBYL,A
         MOV   A,CHKSUMBYH
         XRL   A,#0A0H
         MOV   CHKSUMBYH,A
CHKSUM_JP:
         DJNZ  R7,CHKSUM_LP2   ;重复步骤 3、4,右移 8 次,8 位数据全部进行了处理
         INC   R0
         DJNZ  DATALENGTH,CHKSUM_LP1 ;重复步骤2-5,进行下一个 8 位数据的处理
         RET
           
         END

首先介绍一个不错的CRC校验的网站,http://www.easics.com/webtools/crctool  现在估计所有的工程应用均来自该网站生成的代码。使用方便。

但是该网站的代码不易于CRC的学习和研究,但是保证是对的,工程实践证明。现在将我的研究成果和大家分享一下:用于任意CRC的校验。

网站上的校验方式最大提供CRC32 和任意数据位(最大511)的校验。当然一般的情况下应该是够用了。我所做的设计可以扩展到任意数据的校验,当然是并行数据的校验,串行数据的校验应用可以参照网上的一些资料。很简单,不再赘述。以CRC32为例

首先建立函数,=====设计的的关键

//--------------------------------------------------------------------------
function [31:0] next_c32;

input [31:0] crc;     
input B;        
begin
    next_c32 = {crc[30:0],1'b0} ^ ({32{(crc[31] ^ B)}} &32'h04c11db7);//下划线的部分为本征多项式
end

endfunction

/*这是校验和左移一位求校验和的计算公式*/

相同的如果CRC8

//--------------------------------------------------------------------------
function [7:0] next_c8;

input [7:0] crc;     
input B;        
begin
    next_c8 = {crc[6:0],1'b0} ^ ({8{(crc[7] ^ B)}} & 8'h03);//下划线的部分为本征多项式
end

endfunction

其他的是一样的。

其次 如果我们要求CRC32_D(M)M  >= 32

function [31:0] next_c32_ge; //M+1 is the data maximum with
input [M:0] data;
input [31:0] crc;
integer  i;
begin
 next_c32_ge = crc;
 for(i=0; i<=M; i="i"+1) begin
      next_c32_ge = next_c32(next_c32_ge,data[M-i]);
 end
end
endfunction

假设我们求CRC32_D64  那么M=63

function [31:0] next_c32_D64; //M+1 is the data maximum with
input [63:0] data;
input [31:0] crc;
integer  i;
begin
 next_c32_D64 = crc;
 for(i=0; i<=63; i="i"+1) begin
      next_c32_D64 = next_c32(next_c32_D64,data[63-i]);
 end
end
endfunction

假设我们求CRC32_D128  那么M=127

function [31:0] next_c32_D128;

input [127:0] data;
input [31:0] crc;
integer  i;
begin
 next_c32_D128 = crc;
 for(i=0; i<=127; i="i"+1) begin
      next_c32_D128= next_c32(next_c32_D128,data[127-i]);
 end
end
endfunction

再次如果我们要求CRC32_D(M) M<=32

function [31:0] next_c32_le;
input [31:0] data;
input [31:0] inp;
input [4:0] be;
integer  i;
begin
 next_c32_le = data;
 for(i=0; i<=31-be; i="i"+1) begin
      next_c32_le = next_c32(next_c32_le,inp[31-be-i]);
 end
end
endfunction

我们首先校验完毕所有的有效数据位下面的函数是对CRC的空闲位的修正。

function [K-1:0] next_cK_1_any_LEK_1;
input [N-1:0] data;
input [K-1:0] crc;
begin
     next_cK_1_any_LEK_1 = next_c32_le({data,{(K-N){1'b0}}},{crc[K-1:N],{(K-N){1'b0}}},(K-N))^{crc<<br /> end 
endfunction

//以CRC32D16  K =32   N =16 这个函数就变成

function [31:0] next_C32_D16;
input [15:0] data;
input [31:0] crc;
begin
 next_C32_D16 = next_c32_le({data,{16{1'b0}}},{crc[31:16],{16{1'b0}}},16)^{crc<<16}; 
end 
endfunction

经过modelsim和Qii软件仿真无误。本来想做成动态数据长度校验的函数,本人也作了一些尝试,在CRC--N           N = 2^m时都是没有问题的 比如CRC8  CRC16 CRC32 CRC64 等等,但是若是不是这些数值比如CRC12 CRC10的Qii会抱错(因为部分函数的输入部分必须为常数),但是Modelsim不会抱错而且仿真和实际的结果一致。可以用来做验证。 这边仅仅举了CRC32 的例子,其他的也都类似。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity CODE_74_NEW is
Port (     clk : in  STD_LOGIC;
           data_in: in  STD_LOGIC_VECTOR (3 downto 0);
     --cnt_out: out std_logic_vector (2 downto 0);
     --dtemp_out: out std_logic_vector (3 downto 0);
           data_crc : out  STD_LOGIC);
     
end CODE_74_NEW;
architecture Behavioral of CODE_74_NEW is
  constant multi_coef:std_logic_vector (3 downto 0):="1101";--生成多项式系数,MSB一定为1,g(x)=x^3+x^2+1
  
begin
process(clk)
  variable crcvar,dtemp,sdata:std_logic_vector(3 downto 0);--除法运算被除数变量
  variable cnt:std_logic_vector (2 downto 0):="000";--运算次数控制
  
begin
if clk'event and clk='1' then
cnt:=cnt+1;
--cnt_out<=cnt;
--dtemp_out<=dtemp;
   
    if cnt<=4 then --前四个时钟,串行输出四位信息码
    if  cnt=1 then --初始化操作
      dtemp:=data_in;--装载原数据,用于运算校验码
      sdata:=data_in;--装载原数据,保存
    end if;  
   
          data_crc<=sdata(3);--当计数器小于4时,每来一个时钟串行输出一位信息码
          sdata:=sdata(2 downto 0) & '0'; --左移   
   
    --以下为校验码运算
    if dtemp(3)='1' then  --当前运算的四位码,如果最高位为1则可进行模二除法
     crcvar:=dtemp(3 downto 0) xor multi_coef;--异或运算模二除法
     dtemp:=crcvar(2 downto 0) & '0';--运算后补零
     else dtemp:=dtemp(2 downto 0) & '0';--当前运算的四位码,如果最高位为0则只进行移位补零
    end if;
   
  elsif  cnt>4  then --后三个时钟串行输?位校验码
      data_crc<=dtemp(3);--输出,移位
    dtemp:=dtemp(2 downto 0) & '0';
      if cnt=7 then  --第7个时钟清零
       cnt:=(others=>'0');
    end if;
  end if;
end if;
end process;
end Behavioral;

最新文章

  1. distribution 中一直在运行 waitfor delay @strdelaytime 语句
  2. Android复习笔记--架构与版本
  3. C#变量类型
  4. Go to the first line OR the last line of the file
  5. 利用分布类防止EF更新模型丢失验证信息
  6. 网站生产app的一些网址
  7. CentOS6.5 下Nginx 的安装与配置
  8. php页面zend加密乱码的解决办法
  9. windows 7 netsh wlan命令连接wifi
  10. day5 liaoxuefeng---实战篇
  11. UICollectionView请求网络数据显示(Text)
  12. Windows PowerShell 入門(7)-関数編2
  13. 100-days: fifteen
  14. 安卓APP应用在各大应用市场上架方法整理
  15. 阿里P6大牛给予Java初学者的学习路线建议
  16. Nginx如何设置禁止IP访问网站
  17. Redis随笔(六)RESP的协议规范
  18. .NetCore Cap 结合 RabbitMQ 实现消息订阅
  19. Java下载https文件上传到阿里云oss服务器
  20. 如何更方便的查看Linux内核代码的更新记录【转】

热门文章

  1. java基础知识精华
  2. 开发自己的One Page Scroll插件(二)
  3. 【PAT】1028. List Sorting (25)
  4. Nginx搭建flv mp4流媒体服务器[转]
  5. 算法笔记_147:有向图欧拉回路判断应用(Java)
  6. 清除tomcat的缓存
  7. vue的全家桶
  8. CentOS 6.3下Samba服务器的安装与配置(转)
  9. DirectShow多媒体流捕获播放组件
  10. Zmodem transfer canceled by remote side问题的解决办法!