matlab实现CRC

适用于任何小于等于32位的CRC校验

function crcout = crcX(datain,tempstr,initdata,refin,refout,xorout)
%cala crc
%author ******
%parameter: initdata --xor the fisrt reg; refin --- the datain need reflect
%refout: out crc need reflect; xorout--- crc reg xor xorout
mcrctemp = uint32(bin2dec(tempstr));
len = length(tempstr);
size = length(datain);
for i = 0:(fix(len/8)+1)
    datain(end+1) = 0;
end
if refin ~= 0
    datain = reflect(datain , 8);
end
%andvalue = num2str(uint8(zeros(1,len+1)));
%andvalue(len+1) = '1';
andvalue = bitshift(1,len);
midout = uint32(0);
for i = 0:(((size*8)-1)+len)
    midout = bitshift(midout,1);  
    if bitand(datain((fix(i/8)+1)),bitshift(1,7-rem(i,8))) ~= 0
        midout = bitor(midout,1);
    end
    if i == len-1
        midout = bitxor(midout,initdata);
    end
    if bitand(midout,andvalue)~=0 
        midout = bitxor(midout,mcrctemp);
    end
%    dec2bin(midout)
end
if refout ~= 0
    midout = reflect(midout,len);
end
midout = bitxor(midout,xorout);
andvalue = bitshift(4294967295,len-32);
crcout = bitand(midout,andvalue);
end   
function outdata=reflect(data,bitnum)
%reflect this data
%paramter explain: bitnum reflect bit number
size=length(data);
outdata = zeros(1,size);
mvalue = bitshift(4294967295,bitnum);
for i = 1:size
    outdata(i) = data(i);
    outdata(i) = bitand(outdata(i),mvalue);
    for j=0:(bitnum-1)
        if bitand(data(i),bitshift(1,j)) ~= 0
            outdata(i) = bitor(outdata(i),bitshift(1,bitnum -1 - j));
        end
    end
end
end
<think>我们被要求使用MATLAB实现CRC16校验算法。CRC(循环冗余校验)是一种常用的错误检测算法,广泛应用于数据通信和存储中。CRC16是其中一种,生成16位的校验码。 实现CRC16的关键点: 1. 选择CRC16的多项式(例如:CRC-16-CCITT, CRC-16-IBM等)。不同的多项式会得到不同的校验结果。 2. 初始化寄存器(通常为0xFFFF或0x0000)。 3. 对每个输入字节进行处理:与寄存器的高8位异或,然后移位,根据多项式的位决定是否异或。 4. 最后,寄存器中的值即为CRC16校验码。 这里我们以常见的CRC-16-CCITT(多项式为0x1021)为例。注意,有些实现会使用初始值0xFFFF,并且输入输出不进行反转。 步骤: 1. 初始化一个16位的寄存器(crc)为0xFFFF(即全1)。 2. 将数据按字节处理(如果数据是多个字节,则按顺序处理每个字节): a. 将寄存器的高8位与当前字节进行异或(XOR),结果存回寄存器的高8位(注意:寄存器是16位的,所以我们可以将整个寄存器左移8位,然后与当前字节异或,但标准做法是取高8位与字节异或,然后左移8位,再与根据该字节异或后的高8位决定是否异或多项式)。 b. 将寄存器左移8位(移出最高位),然后根据移出的位(即原来的高8位)来决定是否与多项式进行异或。但实际上,我们通常用查表法来提高效率,但这里我们先实现直接计算法(逐位处理)。 但是,为了效率,我们也可以逐字节处理(而不是逐位),即使用一个查找表(LUT)来加速。不过,用户要求的是示例,我们可以先给出一个直接计算(逐位)的方法,然后再给出一个使用查表法(逐字节)的方法。 然而,注意:CRC16有多种变体,包括初始值、输入输出是否反转、异或输出值等。这里我们按照CRC-16-CCITT的常见标准: - 多项式:0x1021(即二进制1000000100001,去掉最高位,相当于16位中的低16位,所以是0x1021) - 初始值:0xFFFF - 输入反转:无(即直接处理) - 输出反转:无 - 输出异或值:0(即不异或) 另一种常见的是CRC-16-IBM(多项式0x8005),但这里我们按用户要求,先实现CRC-16-CCITT。 直接计算(逐位)的方法效率较低,但易于理解。我们也可以实现逐字节的方法(需要预计算一个256个元素的查找表)。 我们先给出一个逐位计算的示例(用于理解),然后给出一个更实用的逐字节(查表法)的示例。 注意:MATLAB中,我们可以使用位操作函数(如bitand, bitshift, bitxor等)。 方法1:逐位计算(效率低,仅用于理解) 伪代码: crc = 0xFFFF; for each byte in data for i=1 to 8 % 处理每个bit crc = crc << 1; % 左移一位 if the bit (from the byte) is 1 (or 0?) 注意:实际上,我们是将当前字节的最高位与crc的最高位异或,然后决定是否异或多项式。 但通常做法是:将当前字节的最高位与crc的最高位异或,然后决定是否异或多项式。然后左移,并将当前字节左移一位(或者右移,取决于处理顺序)。 由于我们处理的是字节,通常有两种方式:按位处理时,可以处理每个字节的每一位(从高位到低位或低位到高位)。这里我们按高位到低位。 具体步骤(逐位): for each byte in data b = current byte for j=1:8 % 从最高位开始 bit = (b & 0x80) ~= 0 % 取出最高位 b = b << 1; % 左移一位,准备下一个位 msb = (crc & 0x8000) ~= 0; % 取出crc的最高位 crc = bitshift(crc, 1); % 左移一位(注意:crc是16位,左移后最低位补0) if (msb ~= bit) % 如果不同,则相当于异或1,那么需要异或多项式 crc = bitxor(crc, 0x1021); % 多项式0x1021(注意:多项式是17位,但最高位总是1,所以用16位表示时是0x1021,异或时相当于对crc的低16位异或0x1021) end end 但是,上面的方法中,我们实际上在比较当前位和crc最高位是否相同,然后决定是否异或多项式。另一种常见的做法是:将当前字节与crc的高8位异或,然后作为新的高8位,然后左移8位,再根据这个高8位(即移出的8位)来查表(或者逐位处理)决定异或的值。但为了简单,我们采用逐位的方法。 然而,上述方法效率较低,我们更希望使用逐字节的方法。 方法2:逐字节计算(使用查找表) 首先,我们需要一个256个元素的查找表,这个表可以通过上述逐位方法对0~255的每个字节计算其CRC(但只计算一个字节,初始crc为0,然后得到的结果作为该字节对应的值,但在实际计算中,我们使用的是当前crc的高8位与当前字节异或后的值作为索引,然后查表得到该字节对应的值,再与crc的低8位左移8位后的值异或,得到新的crc)。 具体步骤(查表法): 1. 预计算一个256个元素的查找表(table),每个元素是一个16位的数,对应一个字节的CRC值(计算时以初始crc=0,但实际计算中,我们使用一个中间状态,即只计算一个字节的CRC,不考虑初始值)。 2. 初始化crc为0xFFFF。 3. 对每个字节: index = bitxor( bitshift(crc, -8), byte); % 当前crc的高8位与当前字节异或,得到索引 crc = bitxor( bitshift(crc, 8), table(index+1)); % 注意:MATLAB索引从1开始 % 其中,bitshift(crc,8) 相当于将crc左移8位(低8位补0),然后与查表得到的值异或。 注意:这个查表法需要预计算一个表,这个表的计算方法是: for i=0:255 crc = i << 8; % 将i放在16位的高8位,低8位为0 for j=1:8 if (crc & 0x8000) % 最高位为1 crc = bitshift(crc,1); crc = bitxor(crc, 0x1021); else crc = bitshift(crc,1); end end table(i+1) = crc; % 注意:crc是16位,存入表中 但是,注意:这个表计算时,初始crc为0(实际上,我们只处理了一个字节,并且这个字节被放在高8位,低8位为0,然后经过8次移位和异或操作,得到这个字节的CRC值,这个值就是表中对应位置的值)。 然后,在计算整个数据时,我们使用初始crc=0xFFFF,然后对每个字节: temp = (crc >> 8) ^ byte; % 高8位与当前字节异或 crc = (crc << 8) ^ table(temp+1); % 左移8位,然后与查表结果异或 注意:在MATLAB中,我们需要使用位操作函数,并确保使用16位无符号整数(uint16)来避免符号扩展和溢出问题。 下面我们分别实现这两种方法(先实现查表法,因为更实用)。 由于用户没有指定具体多项式,我们就用CRC-16-CCITT(多项式0x1021)为例。 步骤: 1. 定义多项式 poly = 0x1021 (即hex2dec('1021')) 2. 预计算查找表(256个元素) 3. 初始化crc = 0xFFFF 4. 遍历数据的每个字节,更新crc 5. 最终crc即为校验值 注意:数据输入可以是向量(uint8类型)。 我们编写一个函数,输入是数据向量(uint8),输出是CRC16值(uint16)。 方法1:查表法(推荐) 方法2:逐位计算(仅用于理解,效率低) 我们先实现查表法: 示例代码(查表法): ``` function crc = crc16_ccitt(data) % CRC16-CCITT implementation with lookup table % polynomial: 0x1021 % initial value: 0xFFFF % data: uint8 array % Precompute the lookup table (if not persistent, we can compute once) poly = uint16(hex2dec('1021')); crcTable = uint16(zeros(256,1)); % 计算查找表 for i = 0:255 crc = uint16(0); byte = uint16(i) * 256; % 相当于将i放在高8位,低8位为0 for j = 1:8 if bitand(byte, 32768) % 32768 = 0x8000 (即最高位) byte = bitshift(byte, 1); byte = bitxor(byte, poly); else byte = bitshift(byte, 1); end end crcTable(i+1) = byte; end % 初始化crc crc = uint16(hex2dec('FFFF')); % 初始值 % 处理每个字节 for k = 1:length(data) % 当前字节 byte = uint16(data(k)); % 计算索引:crc的高8位与当前字节异或 index = bitxor(bitshift(crc, -8), byte); % 注意:在MATLAB中,bitshift(crc, -8) 相当于右移8位(得到高8位) % 然后,新的crc = (crc左移8位)异或查表值 crc = bitxor(bitshift(crc, 8), crcTable(bitand(index,255)+1)); % 注意:index是uint16,但范围在0-255,所以用bitand(index,255)确保索引在0-255,然后+1(因为MATLAB索引从1开始) end % 输出crc(即校验码) end ``` 但是,上面的查表法在每次调用函数时都会重新计算查找表,这会影响效率。我们可以将查找表定义为持久变量(persistent)或者全局变量,这样只需要计算一次。我们使用persistent: 修改为: ``` function crc = crc16_ccitt(data) % CRC16-CCITT implementation with lookup table persistent crcTable if isempty(crcTable) % 计算查找表 poly = uint16(hex2dec('1021')); crcTable = uint16(zeros(256,1)); for i = 0:255 crc = uint16(0); byte = uint16(i) * 256; % 左移8位,相当于高8位是i,低8位0 for j = 1:8 if bitand(byte, 32768) % 0x8000 byte = bitshift(byte, 1); byte = bitxor(byte, poly); else byte = bitshift(byte, 1); end end crcTable(i+1) = byte; end end % 初始化crc crc = uint16(hex2dec('FFFF')); % 初始值 % 处理每个字节 for k = 1:length(data) byte = uint16(data(k)); index = bitxor(bitshift(crc, -8), byte); % 确保index在0-255范围内 index = bitand(index, 255); % 实际上,由于byte是8位,crc高8位移下来也是8位,所以异或后还是8位,这一步可以省略,但为了安全 crc = bitxor(bitshift(crc, 8), crcTable(index+1)); end end ``` 注意:上面的查找表计算中,我们是将i乘以256(即左移8位)来模拟一个字节在高8位,然后进行8次移位(每次处理一个位)。这与标准方法一致。 但是,有一种更常见的计算查找表的方法:直接计算每个字节(作为索引)的CRC值,但初始crc为0,然后只处理这个字节(8位),得到的结果作为表项。我们上面的方法实际上也是等效的。 然而,我们注意到,在计算表时,我们并没有使用初始值(初始crc=0),这是因为在查表法中,我们使用的方式是:将当前crc的高8位与当前字节异或,然后查表,然后与crc的低8位(左移8位后)异或。所以表项的计算与初始值无关,只与多项式有关。 测试:我们可以用一些已知的测试向量来验证。例如,字符串'123456789'的CRC16-CCITT(初始0xFFFF)应该是0x29B1。 测试代码: data = uint8('123456789'); crc = crc16_ccitt(data); % 应该得到0x29B1 如果得到0x29B1,则正确。 但是,我们也可以实现逐位计算的方法(虽然效率低,但用于验证查表法): 方法2:逐位计算 ``` function crc = crc16_ccitt_bitwise(data) % CRC16-CCITT (bitwise implementation) poly = uint16(hex2dec('1021')); crc = uint16(hex2dec('FFFF')); % 初始值 % 遍历每个字节 for i = 1:length(data) byte = uint16(data(i)); % 处理每个字节的8位(从高位到低位) for j = 1:8 % 取出当前字节的最高位 bit = bitand(byte, 128); % 128=0x80, 取出最高位(非0即128) % 取出crc的最高位 msb = bitand(crc, 32768); % 32768=0x8000 % 左移crc一位(包括当前字节) crc = bitshift(crc, 1); byte = bitshift(byte, 1); % 左移一位,丢弃最高位,最低位补0 % 如果当前字节的最高位与crc原来的最高位不同(即异或结果为1),则需要异或多项式 if (bit ~= 0) % 如果当前位是1 if (msb == 0) % 原来crc的最高位是0,那么异或后为1,所以需要异或多项式 crc = bitxor(crc, poly); end else if (msb ~= 0) % 当前位是0,而crc原来的最高位是1,那么异或后也是1,需要异或多项式 crc = bitxor(crc, poly); end end % 上面的判断可以简化为:如果(bit ~=0) XOR (msb ~=0) 为真,即两者不同,则异或多项式 % 但为了清晰,我们写成上面的条件。 end end end ``` 但是,上面的逐位计算方法中,我们同时处理了当前字节和crc的移位。注意,在移位过程中,当前字节的最高位被移出,而crc左移一位后,最低位补0,然后根据条件决定是否异或多项式。 然而,上面的代码在测试'123456789'时可能得不到0x29B1,因为常见的CRC-CCITT实现中,输入数据通常不被反转,但这里我们按照标准方式处理(高位先处理)。 实际上,更常见的逐位计算方法是: for each byte: b = current byte crc = crc XOR (b << 8) % 将当前字节移到高8位 for i=1:8 if (crc & 0x8000) crc = (crc << 1) XOR poly else crc = crc << 1 end end 我们按照这种方法重新实现: 方法3:另一种逐位计算(更常见) ``` function crc = crc16_ccitt_bitwise2(data) % CRC16-CCITT (another bitwise implementation) poly = uint16(hex2dec('1021')); crc = uint16(hex2dec('FFFF')); for i = 1:length(data) % 将当前字节移到高8位,然后与crc异或 crc = bitxor(crc, bitshift(uint16(data(i)),8)); for j = 1:8 if bitand(crc, 32768) % 检查最高位是否为1 crc = bitxor(bitshift(crc,1), poly); else crc = bitshift(crc,1); end end end % 最终crc就是结果 end ``` 测试:用'123456789'测试,应该得到0x29B1。 注意:在MATLAB中,bitshift左移时,如果移出位,则丢失。所以我们需要确保使用16位整数(uint16)来避免符号扩展。 我们推荐使用查表法,因为效率高。而逐位计算法(方法3)虽然简单,但效率较低。 现在,我们给出完整的示例,使用查表法(带持久表)作为主要实现。 注意:不同的CRC16变体可能需要不同的多项式、初始值、异或输出等。用户如果需要其他变体,可以调整这些参数。 测试代码: data = uint8('123456789'); crc = crc16_ccitt(data); % 将crc以16进制字符串输出 dec2hex(crc) 应该输出'29B1'。 因此,我们提供查表法的函数作为实现。 另外,如果用户需要其他多项式,可以修改函数,但这里我们固定为CRC-16-CCITT。 最后,我们生成相关问题。</think>### MATLAB CRC16校验算法实现 CRC16校验是一种常用的数据校验方法,用于检测数据传输或存储中的错误。下面我将提供两种MATLAB实现方法:查表法(高效)和直接计算法(易于理解)。 #### 1. 查表法实现(推荐) ```matlab function crc = crc16_ccitt(data) % CRC16-CCITT 查表法实现 (多项式 0x1021) % 输入: data - uint8类型数组 % 输出: crc - 16位校验值 % 预计算CRC表(持久变量提高效率) persistent crcTable if isempty(crcTable) poly = uint16(hex2dec('1021')); crcTable = zeros(1, 256, 'uint16'); for i = 0:255 crc = bitshift(uint16(i), 8); for j = 1:8 if bitand(crc, 32768) % 检查最高位 crc = bitxor(bitshift(crc, 1), poly); else crc = bitshift(crc, 1); end end crcTable(i+1) = crc; end end % 初始化CRC寄存器 crc = uint16(hex2dec('FFFF')); % 处理每个字节 for i = 1:length(data) byte = uint16(data(i)); index = bitxor(bitshift(crc, -8), byte) + 1; % 计算查表索引 crc = bitxor(bitshift(crc, 8), crcTable(index)); end end ``` #### 2. 直接计算法实现 ```matlab function crc = crc16_direct(data) % CRC16-CCITT 直接计算法 (多项式 0x1021) % 输入: data - uint8类型数组 % 输出: crc - 16位校验值 poly = uint16(hex2dec('1021')); % 多项式 crc = uint16(hex2dec('FFFF')); % 初始值 % 处理每个字节 for i = 1:length(data) crc = bitxor(crc, bitshift(uint16(data(i)), 8)); for j = 1:8 if bitand(crc, 32768) % 检查最高位 crc = bitxor(bitshift(crc, 1), poly); else crc = bitshift(crc, 1); end end end end ``` #### 使用示例 ```matlab % 测试数据 (ASCII字符串 '123456789') testData = uint8('123456789'); % 计算CRC16 (正确结果应为 0x29B1) crcTable = crc16_ccitt(testData); crcDirect = crc16_direct(testData); % 显示结果 (16进制) fprintf('查表法结果: 0x%s\n', dec2hex(crcTable, 4)); fprintf('直接法结果: 0x%s\n', dec2hex(crcDirect, 4)); ``` #### 实现说明 1. **多项式选择**:使用CRC16-CCITT标准多项式 $x^{16} + x^{12} + x^5 + 1$ (十六进制 0x1021) 2. **初始值**:0xFFFF(标准初始值) 3. **输出处理**:直接输出计算结果(不进行额外异或或反转) 4. **输入要求**:数据需为uint8数组 5. **性能比较**: - 查表法:处理速度更快($O(n)$),适合大数据量 - 直接法:代码更简单,适合理解算法原理 #### 验证测试 输入字符串 `'123456789'` 的正确CRC16值应为 **0x29B1**(十进制 10673),这是CRC16-CCITT的标准测试向量[^1]。 #### 常见CRC16变体 如需其他CRC16变体(如CRC16-MODBUS),只需修改多项式和初始值: ```matlab % CRC16-MODBUS 参数 poly = uint16(hex2dec('8005')); % 多项式 init = uint16(hex2dec('FFFF')); % 初始值 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值