• Delphi Modbus RTU CRC16校验码的生成方式


    在很多的串口通讯中,会使用到CRC16校验。在TIdHashCRC16中,给我们提供了一种CRC校验码的生成方式:

    方式一:使用TIdHashCRC16

    TIdHashCRC16继承于TIdHash16这个类,其中覆盖实现了两个方法:

    1. { TIdHashCRC16 }
    2. procedure TIdHashCRC16.HashStart(var VRunningHash: UInt16);
    3. begin
    4. VRunningHash := 0;
    5. end;
    6. procedure TIdHashCRC16.HashByte(var VRunningHash: UInt16; const AByte: Byte);
    7. begin
    8. VRunningHash := (VRunningHash shr 8) xor CRC16Table[AByte xor (VRunningHash and $FF)];
    9. end;

    然而,当我们直接使用TIdHashCRC16类生成校验码的时候,经常是与设备的校验不匹配的,其原因在于VRunningHash初始化时为0,而我们需要的初始化值应该为0xFFFF。所以,我们需要对这个类进行改造,其中最简单的办法就是找到IdHashCRC.pas文件,把VRunningHash:=0;这个修改为VRunningHash:=$FFFF;,然后把这个修改后的文件直接放到项目文件的根目录下就行了。

    但这样的修改并不是最好的方式,所以,我们可以新建一个类,让他继承于TIdHashCRC16,并重写HashStart方法:

    1. uses
    2. IdGlobal, IdHash, IdHashCRC, System.SysUtils, System.Classes;
    3. Type
    4. TCRC16 = class(TIdHashCRC16)
    5. public
    6. procedure HashStart(var VRunningHash: UInt16); override;
    7. function GetCRC(vCRCBytes: TBytes): TBytes;
    8. end;
    9. implementation
    10. { TCRC16 }
    11. function TCRC16.GetCRC(vCRCBytes: TBytes): TBytes;
    12. var
    13. LStream: TStream;
    14. SS: TIdBytes;
    15. begin
    16. LStream := TMemoryStream.Create;
    17. try
    18. LStream.WriteBuffer(vCRCBytes, Length(vCRCBytes));
    19. LStream.Position := 0;
    20. SS := HashStream(LStream);
    21. move(SS[0], Result[0], 2);
    22. finally
    23. FreeAndNil(LStream);
    24. end;
    25. end;
    26. procedure TCRC16.HashStart(var VRunningHash: UInt16);
    27. begin
    28. VRunningHash := $FFFF;
    29. end;

    上面代码中我们在重写HashStart方法的时候还增加了一个获取CRC值的函数,方便直接使用。

    使用方法也比较简单:

    1. CRC16 := TCRC16.Create;
    2. vCRC16 := CRC16.GetCRC(vTmp);
    3. FreeAndNil(CRC16);

    方式二:代码直接计算生成

    此外,我们还有一种生成CRC校验码的方式:

    1. function CalCRC16(AData: TBytes): TBytes;
    2. const
    3. GENP=$A001; //多项式公式X16+X15+X2+1(1100 0000 0000 0101)
    4. var
    5. vCRC:Word;
    6. i:Integer;
    7. vTmpByte:Byte;
    8. procedure CalOneByte(AByte:Byte); //计算1个字节的校验码
    9. var
    10. j:Integer;
    11. begin
    12. vCRC:=vCRC xor AByte; //将数据与CRC寄存器的低8位进行异或
    13. for j:=0 to 7 do //对每一位进行校验
    14. begin
    15. vTmpByte:=vCRC and 1; //取出最低位
    16. vCRC:=vCRC shr 1; //寄存器向右移一位
    17. vCRC:=vCRC and $7FFF; //将最高位置0
    18. if vTmpByte=1 then //检测移出的位,如果为1,那么与多项式异或
    19. vCRC:=vCRC xor GENP;
    20. vCRC:=vCRC and $FFFF;
    21. end;
    22. end;
    23. begin
    24. vCRC:=$FFFF; //将余数设定为FFFF
    25. for i:=0 to Length(AData)-1 do //对每一个字节进行校验
    26. CalOneByte(AData[i]);
    27. SetLength(Result,2);
    28. Result[0]:= trunc(vCRC mod 256);
    29. Result[1]:= trunc(vCRC div 256);
    30. end;

    方法三的使用也非常简单,在需要计算CRC校验码的时候,直接调用函数就可以生成CRC校验码了。

    这两种校验码的生成方式不同,结果是一样的。两种方法的使用,都是以TBytes的变量传递计算与返回值。

  • 相关阅读:
    【FileZila】实现windows与Linux系统文件互传
    访问者模式:对象结构的元素处理
    【数据结构】图基本概念
    Excel VSTO开发1-VSTO简介
    聊聊我常用的5款动态数据可视化工具
    计算机网络第四章——网络层(中)
    从零开始的C语言学习第二十课:数据在内存中的存储
    哪款物业管理软件又便宜又好用?
    Word2Vec词向量训练、使用及可视化操作
    Android studio:错误: 需要常量表达式
  • 原文地址:https://blog.csdn.net/tanqth/article/details/125508691