CRC校验——以SHT4xA温湿度传感器为例

一、简介

循环冗余校验码(CRC)的基本原理是:在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码又叫(N,K)码。对于一个给定的(N,K)码,可以证明存在一个最高次幂为N-K=R的多项式G(x)。根据G(x)可以生成K位信息的校验码,而G(x)叫做这个CRC码的生成多项式。

在这里插入图片描述

二、计算方法

具体参考CRC校验——以SHT30温湿度传感器为例(内附SHT30的驱动代码)

(一)步骤

(1) 预置一个值为0xFFFF的16位寄存器,此寄存器为CRC寄存器
(2) 把第一个8位二进制数与16位的CRC寄存器相异或,异或的结果存在CRC寄存器中
(3) CRC寄存器的内容左移一位,用0填补最低位,并检测移出位是0还是1
(4) 如果移出位是0,则重复步骤3,如果移出位是1,则与多项式进行异或
(5) 重复步骤3、4,直到左移8位,这样整个8位数据都进行了处理
(6) 重复步骤2~5,进行下一个字节的处理
(7)最后得到的CRC寄存器的内容即为CRC校验码

在这里插入图片描述

(二)参考代码(C语言)

#include <stdio.h>
typedef unsigned char uint8_t;

/*
 * crc8校验函数,data为要校验的数据,len为要校验的数据的字节数
 */
uint8_t crc8(const uint8_t *data, int len)
{
     const uint8_t POLYNOMIAL = 0x31;
     uint8_t crc = 0xFF;
     int i, j;
   
	 for (i=0; i<len; ++i) 
	 {
     	crc ^= *data++;
 
     	for (j=0; j<8; ++j) 
		{
        	crc = ( crc & 0x80 )? (crc << 1) ^ POLYNOMIAL: (crc << 1);
       	}
  	 }
	
	 return crc;
}

int main(int argc, const char *argv[])
{
	unsigned char data1[2] = {0x61, 0x04};
	unsigned char data2[2] = {0xBE, 0xEF};
	printf("0x%02X\n", crc8(data1, 2));
	printf("0x%02X\n", crc8(data2, 2));

	return 0;
}
/* 输出结果
linux@linux-VirtualBox:~$ ./a.out 
0xE4
0x92
*/


(三)检验:CRC(0xBEEF)= 0x92

多项式:0x31 ( x 8 x^8 x8+ x 5 x^5 x5+ x 4 x^4 x4+1)换成二进制为 9‘b1_0011_0001
检验:CRC(0xBEEF)= 0x92

1.第一个8位数据0xBE:CRC寄存器值为8'hFF,两者异或

          1   0   1   1     1   1  1   0
XOR     1   1   1   1     1   1  1   1
————————————————————
          0   1   0   0     0   0  0   1

1)左移一位,移出0,CRC值变成

           1   0   0   0     0   0   1   0

2)左移一位,移出1,与多项式0x31异或

           0   0   0   0     0   1   0   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            0   0   1   1     0   1   0   1

3)左移一位,移出0,CRC值变成

            0   1   1   0     1   0   1   0

4)左移一位,移出0,CRC值变成

            1   1   0   1     0   1   0   0

5)左移一位,移出1,与多项式0x31异或

           1   0   1   0     1   0   0   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            1   0   0   1     1   0   0   1

6)左移一位,移出1,与多项式0x31异或

           0   0   1   1     0   0   1   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            0   0   0   0     0   0   1   1

7)左移一位,移出0,CRC值变成

            0   0   0   0     0   1   1   0

8)左移一位,移出0,CRC值变成

            0   0   0   0     1   1   0   0


2.第二个8位数据0xEF,CRC寄存器值0x0C,两者异或:
          1   1   1   0    1   1   1   1
XOR    0   0   0   0    1   1   0   0
————————————————————
          1   1   1   0     0   0   1   1

1)左移一位,移出1,与多项式0x31异或

           1   1   0   0     0   1   1   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            1   1   1   1     0   1   1   1

2)左移一位,移出1,与多项式0x31异或

           1   1   1   0     1   1   1   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            1   1   0   1     1   1   1   1

3)左移一位,移出1,与多项式0x31异或

           1   0   1   1     1   1   1   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            1   0   0   0     1   1   1   1

4)左移一位,移出1,与多项式0x31异或

           0   0   0   1     1   1   1   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            0   0   1   0     1   1   1   1

5)左移一位,移出0,CRC值变成

            0   1   0   1     1   1   1   0

6)左移一位,移出0,CRC值变成

            1   0   1   1    1   1   0   0

7)左移一位,移出1,与多项式0x31异或

           0   1   1   1     1   0   0   0
XOR       0   0   1   1     0   0   0   1
————————————————————
            0   1   0   0     1   0   0   1

8)左移一位,移出0,CRC值变成

            1   0   0   1     0   0   1   0

(3)可以看出CRC值最终变成了0x92.


三、Verilog检验CRC

(一)实现

    module crc(
        input                   clk     ,//时钟信号
        input                   rst_n   ,//复位信号
        input       [23:0]      din     ,
        input                   din_vld ,
        output reg  [15:0]      dout    ,
        output reg              dout_vld,
        output reg              right    //判断正确错误                
    );
    //参数定义
        localparam  CNT_SHIFT = 8       ,
                    DATA_NUM  = 2       ;
        localparam  CRC_INIT  = 8'hFF   ,//初始值
                    POLY      = 8'h31   ;//多项式
        localparam  IDLE      = 5'b00001,
                    START     = 5'b00010,
                    SHIFT     = 5'b00100,
                    CHANGE    = 5'b01000,
                    DONE      = 5'b10000;   
                     

    //信号定义
        reg   [4:0]    state_c ;
        reg   [4:0]    state_n ;
        reg   [23:0]   din_latch;
        reg   [7:0]    data     ;
        // reg            msb      ;//高有效位
        reg   [7:0]    crc      ;//crc寄存器
        reg   [2:0]    shift_cnt;
        reg            data_cnt ;
        reg            data_done;

    //din_latch
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               din_latch<= 0;
            else if(din_vld)
               din_latch<= din;
        end
    //状态机设计
        always @(posedge clk or negedge rst_n) begin 
            if (rst_n==0) 
                state_c <= IDLE ;
            else 
                state_c <= state_n;
        end 
    //状态跳转  
        always @(*) begin 
            case(state_c)  
                IDLE  :begin
                        if(din_vld)
                            state_n = START ;
                        else 
                            state_n = state_c;
                       end  
                START :state_n = SHIFT ;
                SHIFT :begin
                        if(shift_cnt== CNT_SHIFT-1)
                            state_n = CHANGE;
                        else 
                            state_n = state_c;
                       end 
                CHANGE:begin
                        if(!data_done)
                            state_n = START  ;
                        else if(data_done)
                            state_n = DONE  ;
                        else 
                            state_n = state_c;
                       end 
                DONE  :state_n = IDLE ;
                default : state_n = IDLE ; 
            endcase
        end

     //shift_cnt
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
                shift_cnt <= 0;
            else if(shift_cnt== CNT_SHIFT-1) 
                shift_cnt <= 0;
            else if(state_c == SHIFT)
                shift_cnt <= shift_cnt + 1'b1;
        end
    //data_cnt
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
                data_cnt <= 0;
            else if((data_cnt== DATA_NUM-1)&(shift_cnt== CNT_SHIFT-1)) 
                data_cnt <= 0;
            else if(shift_cnt== CNT_SHIFT-1)
                data_cnt <=  data_cnt + 1'b1;
        end
    //data
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               data<= 0;
            else if(din_vld)
               data<= din[23:16];
            else if((shift_cnt== CNT_SHIFT-2)&(data_cnt== DATA_NUM-2))
               data<= din_latch[15:8];
        end
    //crc
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               crc<= CRC_INIT;
            else if(din_vld)
               crc<= CRC_INIT;
            else if(state_c == START)
               crc= crc^data;
            else if(crc[7]&(state_c == SHIFT))
               crc<= (crc<<1)^POLY;
            else if(!crc[7]&((state_c == SHIFT)))
               crc<= (crc<<1);
            
        end
    //data_done
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               data_done<= 0;
            else if((data_cnt== DATA_NUM-1)&(shift_cnt== CNT_SHIFT-1))
               data_done<= 1'b1;
            else
               data_done<= 1'b0;
        end
    //right
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               right <= 0;
            else if(din_vld)
               right <= 1'b0;
            else if((crc==din_latch[7:0])&data_done)
               right <= 1'b1;
            else if((crc!=din_latch[7:0])&data_done)
               right <= 1'b0;
        end
    //dout
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               dout<= 0;
            else if(data_done)
               dout<= din_latch[23:8];
        end
    //dout_vld
        always@(posedge clk or negedge rst_n)begin
            if(!rst_n)
               dout_vld<= 0;
            else if(state_n ==DONE)
               dout_vld<= 1'b1;
            else
               dout_vld<= 1'b0;
        end
    endmodule

(二)仿真

`timescale 1 ns / 1 ps
    module tb;
        reg               clk     ;
        reg               rst_n   ;
        reg   [23:0]      din     ;
        reg               din_vld ;
        wire  [15:0]      dout    ;
        wire              dout_vld;
        wire              right   ;

        always #20 clk =~clk;
    //初始化
      initial begin
         clk = 1'b1;
         rst_n =1'b1;
         #40 ;
         rst_n = 1'b0;
         #80
         rst_n = 1'b1;
         #100000;
         $stop;
      end
    integer i = 0,j = 0;
    //初始化
      initial begin
        din     = 0;
        din_vld = 0;
        #200;
        for(i=0;i<30;i=i+1)begin
          if((i==1)|(i==10)|(i==20))
            din     = 24'hBEEF92;
          else
            din     = {$random};
          @(posedge clk) ;
          for(j=0;j<50;j=j+1)begin
            if(j==1)
              din_vld = 1'b1;
            else
              din_vld =1'b0;
            @(posedge clk) ;
          end
        end
          
      end
    
    //模块例化
      crc tb_crc(
        /* input             */.clk      (clk     ),//时钟信号
        /* input             */.rst_n    (rst_n   ),//复位信号
        /* input   [23:0]    */.din      (din     ),
        /* input             */.din_vld  (din_vld ),
        /* output  [15:0]    */.dout     (dout    ),
        /* output            */.dout_vld (dout_vld),
        /* output            */.right    (right   ) //判断正确错误                
      );
endmodule



(三)波形

1.检验CRC(0xBEEF)= 0x92?
可以看到输入数据din=24’hBEEF92(数据16'hBEEF + CRC8'h92),输出结果为right(right==1),此时CRC校验结果是正确的。
在这里插入图片描述

2.检验CRC(0x895E)= 0x81?
可以看到输入数据din=24’895E81(数据16'h895E + CRC8'h81),输出结果为wrong(right==0),此时CRC校验结果是错误的。正确的CRC应该是8'h00,非8’h81

在这里插入图片描述

参考

模2除法(CRC)循环冗余校验码在线计算器
CRC校验——以SHT30温湿度传感器为例(内附SHT30的驱动代码)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值