张志峰的博客

水滴石川,积少成多。

导航

如何使用Delphi编写Modbus RTU CRC16的校验码

Posted on 2016-06-12 15:11  ╰★张志峰★╮  阅读(2098)  评论(0编辑  收藏  举报

在工业控制中,Modbus RTU CRC16的校验码用的比较广泛,包括本人富士产品中,PC与伺服电机以及PC与VP系列的变频器的Modbus RTU通讯中都使用到了CRC16.

    而对CRC16的计算的方式基本上有2种:第一种,使用双循环依照CRC的计算方法进行计算,第二种,采用查表的方式。本人愚钝无比,从网络上搜来的查表法都与实际的正确CRC16的结果有所差异,因此编写了一个小程序供自己使用。
   软件的界面很简单,输入诸如“010303020014”的值,然后每2个字符作为一个字节,填入字节数,然后就可以计算出校验码,校验码的多项式为:X16+X15+X2+1.
 
 
实现的源代码如下:
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Edit2: TEdit;
    Edit3: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Memo1: TMemo;
    Label4: TLabel;
  function CalCRC16(AData:array of Byte;AStart,AEnd:Integer):Word;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
//××××××××××××××××××××××××××
// CalCRC16用于计算Modbus RTU的CRC16
// 多项式公式为X16+X15+X2+1
//××××××××××××××××××××××××××
function TForm1.CalCRC16(AData:array of Byte;AStart,AEnd:Integer):Word;
const
  GENP=$A001;  //多项式公式X16+X15+X2+1(1100 0000 0000 0101)
var
  crc:Word;
  i:Integer;
  tmp:Byte;
procedure CalOneByte(AByte:Byte);  //计算1个字节的校验码
var
j:Integer;
begin
  crc:=crc xor AByte;   //将数据与CRC寄存器的低8位进行异或
  for j:=0 to 7 do      //对每一位进行校验
  begin
    tmp:=crc and 1;        //取出最低位
    crc:=crc shr 1;        //寄存器向右移一位
    crc:=crc and $7FFF;    //将最高位置0
    if tmp=1 then         //检测移出的位,如果为1,那么与多项式异或
      crc:=crc xor GENP;
      crc:=crc and $FFFF;
  end;
end;
begin
  crc:=$FFFF;             //将余数设定为FFFF
  for i:=AStart to AEnd do   //对每一个字节进行校验
    CalOneByte(AData[i]);
  Result:=crc;
end;
 
 
 
procedure TForm1.Button1Click(Sender: TObject);
var
  Data:array[0..255] of Byte;
  i,j,Count:Integer;
  Res:Word;
  szData:string;
begin
  szData:=Form1.Edit2.Text;    //读入欲校验的字符串
  Count:=StrToInt(form1.Edit3.Text); //读入需要计算的字符串长度
  i:=1;
  j:=0;
  for j:=0 to Count-1 do
  begin
    if (i mod 2)=0 then    //每2个字符放入一个字节中
         i:=i+1;
    if i>=Length(szData) then
        exit;
    Data[j]:=StrToInt('$'+copy(szData,i,2)); //取出字符并转换为16进制数
    i:=i+1;
  end;
 
  Res:=CalCRC16(Data,Low(Data),Count-1);
  form1.Edit1.Text:=IntToHex(Res,4);
end;
 
end.
 
原贴:
http://blog.163.com/shcima@yeah/blog/static/107821988201062105441133/
 
 

你这个程序有问题,有空联系我,不对。我的QQ是992725706

2011-08-21 14:31
 cvlsam 回复 dfdfdfdf
已经用于实际项目中了……
2015-05-07 11:09
15751010184 回复 dfdfdfdf
不要怀疑这个程序的正确性。。。。不过modbus上使用可能是需要把高低对调一下,别的我就不知道了!
  Res:=CalCRC16(Data,Low(Data),Count-1);
  crc:=IntToHex(Res,4);
  crcL:=crc[3]+crc[4] ;
  crcH:=crc[1]+crc[2] ;
  memo1.Text:=crcL+crcH;
2015-05-07 11:04
不过在modbus通讯字符串里,那个得到的校验码确实需要把高位和低位对调一下。。。但是任然万分感激楼主把我从一个星期的蛋疼中解救出来。。。。楼主我能加你qq么?以后多请教一下你。。。。
2015-05-07 11:00
真是感激啊。。。。。。位校验程序网上很多,关键就在需要校验的字符怎么处理再给予校验,各种坑啊。。。。楼主贴出了字符到校验的处理全过程。。。。。业界良心啊!!!!!!!!
2014-02-20 23:03
1
您太厉害了 好佩服
2013-07-21 17:41
patchhome

好像计算出来的高2位和低2位应该对调才和设备的对上。

2011-05-16 08:40
2010-09-19 10:50
恩,我也才刚冲学校走出来, 单位要求学习delphi,先在编城中有关通讯这块遇到问题,主要用modbus协议CRC的算法,有空向您多请教