unit unModbus;

本文详细介绍了一种基于Modbus协议的数据交换实现方法,包括读取寄存器、写单个及多个寄存器的功能,并提供了具体的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

unit unModbus;

interface

uses
  Windows, Messages, SysUtils,unGeneral,
  Dialogs;

const

  //参数值
  READ_REG_COUNT           = $08;     //读寄存器命令字节数
  READ_RETURN_BASE_COUNT   = $05;     //读返回字节基本数
  WRITE_REGONE_COUNT       = $08;     //写单个寄存器命令字节数
  WRITE_REGTWO_COUNT       = $0D;     //写两个寄存器命令字节数
  WRITE_REGMUL_BASE_COUNT  = $09;     //写多个寄存器命令字节基本数
  WRITE_RETURN_COUNT       = $08;     //写正确返回命令字节数
  ERR_RETURN_COUNT         = $05;     //错误返回命令字节数

  //功能码
  FUN_READ_REG    = $03;  //读寄存器值
  FUN_WRITE_REG   = $06;  //写单个寄存器
  FUN_WRITE_REGX  = $10;  //写多个寄存器

  //从机接收错误返回信息
  FUN_ERR_READ    = $83;  //读寄存器值从机接收错误
  FUN_ERR_WRITE   = $86;  //写单个寄存器从机接收错误
  FUN_ERR_WRITEX  = $90;  //写多个寄存器从机接收错误
 

type
  TModbusAgree = class
  private
    //字节转换为错误代码
    //function ByteToCode(Abyte:Byte):Byte;
  protected

  public
    constructor Create;
    //打包读寄存器命令
    function PackedRead(Addr:Byte; RegStartAddr, RegCount:Word; var Datas:array of Byte):Integer;
    //解包读寄存器返回数据(函数返回值为数据字节数,返回0时表示错误数据)
    function UnPackRead(const ABytes:array of Byte; const Addr:Byte; const Count:Integer;
        var Datas:array of Byte):Integer;
    //打包写单个寄存器命令
    function PackedWriteOne(Addr:Byte; RegAddr, AData:Word; var Datas:array of Byte):Integer;
    //打包写两个寄存器命令
    function PackedWriteTwo(Addr:Byte; RegStartAddr, AData1, AData2:Word;
        var Datas:array of Byte):Integer;
    //打包写多个寄存器命令
    function PackedWriteMuli(Addr:Byte; RegStartAddr, RegCount:Word;
              AData:array of Word; var Datas:array of Byte):Integer;
    //解包写单个寄存器返回数据
    function UnPackWriteReturnOne(const ABytes:array of Byte; const Count:Integer; const Addr:Byte;
    const RegAddr, AData:Word):Boolean;
    //解包写多个寄存器返回数据
    function UnPackWriteReturnMuli(const ABytes:array of Byte; const Count:Integer; const Addr:Byte;
        const RegStartAddr, RegCount:Word):Boolean;

  end;

var
  ModbusAgree:TModbusAgree;     //MODBUS协议

implementation

uses unCRC16_Modbus;

{ TModbusAgree }

constructor TModbusAgree.Create;
begin
  //
end;

function TModbusAgree.PackedRead(Addr: Byte; RegStartAddr, RegCount: Word;
                                  var Datas:array of Byte):Integer;
var
  crc:Word;
begin
  Datas[0]:=Addr;
  Datas[1]:=FUN_READ_REG;
  Datas[2]:=HiByte(RegStartAddr);
  Datas[3]:=RegStartAddr;
  Datas[4]:=HiByte(RegCount);
  Datas[5]:=RegCount;
  Datas[6]:=0;
  Datas[7]:=0;
  crc:=CalCRC16(Datas,0,5);
  Datas[6]:=crc;
  Datas[7]:=HiByte(crc);
  Result:=READ_REG_COUNT;
end;

function TModbusAgree.UnPackRead(const ABytes:array of Byte; const Addr:Byte; const Count:Integer;
    var Datas:array of Byte): Integer;
var
  i,n:Integer;
  crc,rc:Word;
begin
  Result:=0;
  n:=Count-READ_RETURN_BASE_COUNT;
  {if (Count < READ_RETURN_BASE_COUNT+2) or (n <> ABytes[2]) then
    raise ECommException.Create(ERR_MODBUS_LEN); }

   
  {if (ABytes[1] = FUN_ERR_READ) and (Count = ERR_RETURN_COUNT)  then
  begin
    crc:=CalCRC16(ABytes,0,Count-3);
    if (HiByte(crc) <> ABytes[Count-1]) or (Byte(crc) <> ABytes[Count-2]) then
      raise ECommException.Create(ERR_MODBUS_INV);
    raise ECommException.Create(ABytes[2]);
  end;}
 
  crc:=CalCRC16(ABytes,0,Count-3);
  rc:=MakeWord(ABytes[Count-2],ABytes[Count-1]);
  {if crc <> rc then
    raise ECommException.Create(ERR_MODBUS_CRC)
  else if (ABytes[0] = Addr) and (ABytes[1] = FUN_READ_REG)  and (ABytes[2] = n) then
  begin
    for i := 0 to n-1 do
      Datas[i]:=ABytes[i+3];
    Result:=ABytes[2];
  end
  else
    raise ECommException.Create(ERR_MODBUS_INV); }

  //if (ABytes[0] = Addr) and (ABytes[1] = FUN_READ_REG)  and (ABytes[2] = n) then
  if (ABytes[0] = Addr) and (ABytes[1] = FUN_READ_REG) then
  begin
    for i := 0 to n-1 do
      Datas[i]:=ABytes[i+3];
    Result:=ABytes[2];
  end;
end;

function TModbusAgree.PackedWriteOne(Addr: Byte; RegAddr, AData: Word;
    var Datas:array of Byte):Integer;
var
  crc:Word;
begin
  Datas[0]:=Addr;
  Datas[1]:=FUN_WRITE_REG;
  Datas[2]:=HiByte(RegAddr);
  Datas[3]:=RegAddr;
  Datas[4]:=HiByte(AData);
  Datas[5]:=AData;
  Datas[6]:=0;
  Datas[7]:=0;
  crc:=CalCRC16(Datas,0,5);
  Datas[6]:=crc;
  Datas[7]:=HiByte(crc);
  Result:=WRITE_REGONE_COUNT;
end;

function TModbusAgree.PackedWriteTwo(Addr: Byte; RegStartAddr, AData1, AData2: Word;
    var Datas:array of Byte):Integer;
const
  RegCount = 2;
  ByteCount = 4;
var
  crc:Word;
begin
  Datas[0]:=Addr;
  Datas[1]:=FUN_WRITE_REGX;
  Datas[2]:=HiByte(RegStartAddr);
  Datas[3]:=RegStartAddr;
  Datas[4]:=HiByte(RegCount);
  Datas[5]:=RegCount;
  Datas[6]:=ByteCount;
  Datas[7]:=HiByte(AData1);
  Datas[8]:=AData1;
  Datas[9]:=HiByte(AData2);
  Datas[10]:=AData2;
  crc:=CalCRC16(Datas,0,10);
  Datas[11]:=crc;
  Datas[12]:=HiByte(crc);
  Result:=WRITE_REGTWO_COUNT;
end;

function TModbusAgree.PackedWriteMuli(Addr:Byte; RegStartAddr, RegCount:Word;
          AData:array of Word; var Datas:array of Byte):Integer;
var
  crc:Word;
  i:Integer;
begin
  Datas[0]:=Addr;
  Datas[1]:=FUN_WRITE_REGX;
  Datas[2]:=HiByte(RegStartAddr);
  Datas[3]:=RegStartAddr;
  Datas[4]:=HiByte(RegCount);
  Datas[5]:=RegCount;
  Datas[6]:=RegCount*2;
  for i := 0 to RegCount-1 do
  begin
    Datas[i*2+7]:=HiByte(Adata[i]);
    Datas[i*2+8]:=Adata[i];
  end;
  crc:=CalCRC16(Datas,0,RegCount*2+6);
  Datas[RegCount*2+7]:=crc;
  Datas[RegCount*2+8]:=HiByte(crc);
end;

function TModbusAgree.UnPackWriteReturnOne(const ABytes:array of Byte; const Count:Integer;
    const Addr:Byte; const RegAddr, AData:Word):Boolean;
var
  crc,rg,rd,rc:Word;
begin
  Result:=False;
  {if Count <> WRITE_RETURN_COUNT then
    raise ECommException.Create(ERR_MODBUS_LEN); }
  {if (ABytes[1] = FUN_ERR_WRITE) and (Count = ERR_RETURN_COUNT) then
  begin
    crc:=CalCRC16(ABytes,0,Count-3);
    if (HiByte(crc) <> ABytes[Count-1]) or (Byte(crc) <> ABytes[Count-2]) then
      raise ECommException.Create(ERR_MODBUS_INV);
    raise ECommException.Create(ABytes[2]);
  end;}
 
  crc:=CalCRC16(ABytes,0,Count-3);
  rc:=MakeWord(ABytes[6],ABytes[7]);
 
  {if crc <> rc then
    raise ECommException.Create(ERR_MODBUS_CRC);}

  rg:=MakeWord(ABytes[3],ABytes[2]);
  rd:=MakeWord(ABytes[5],ABytes[4]);

  if (ABytes[0] = Addr) and (ABytes[1] = FUN_WRITE_REG) and (rg = RegAddr) and (rd = AData) then
    Result:=True
  else
    //raise ECommException.Create(ERR_MODBUS_INV);

end;

function TModbusAgree.UnPackWriteReturnMuli(const ABytes: array of Byte; const Count:Integer;
    const Addr: Byte; const RegStartAddr, RegCount: Word):Boolean;
var
  crc,rg,rn,rc:Word;
begin
  Result:=False;
  {if Count <> WRITE_RETURN_COUNT then
    raise ECommException.Create(ERR_MODBUS_LEN);}
  {if (ABytes[1] = FUN_ERR_WRITE) and (Count = WRITE_RETURN_COUNT) then
  begin
    crc:=CalCRC16(ABytes,0,Count-3);
    if (HiByte(crc) <> ABytes[Count-1]) or (Byte(crc) <> ABytes[Count-2]) then
      raise ECommException.Create(ERR_MODBUS_INV);
    raise ECommException.Create(ABytes[2]);
  end;}

  crc:=CalCRC16(ABytes,0,Count-3);
  rc:=MakeWord(ABytes[6],ABytes[7]);
  {if crc <> rc then
    raise ECommException.Create(ERR_MODBUS_CRC);}

  rg:=MakeWord(ABytes[3],ABytes[2]);
  rn:=MakeWord(ABytes[5],ABytes[4]);

  if (ABytes[0]=Addr) and (ABytes[1]=FUN_WRITE_REGX) and (rg=RegStartAddr) and (rn=RegCount) then
    Result:=True
  else
    //raise ECommException.Create(ERR_MODBUS_INV);
end;

end.
//注:只实现了03$,06$和10$命令

下面这个是CRC16
unit unCRC16_Modbus;

interface

function CalCRC16(AData:array of Byte; AStart,AEnd:Integer): Word;

implementation

function CalCRC16(AData:array of Byte; AStart,AEnd:Integer): Word;
const
  GENP = $A001;
 
var
  crc:Word;
  i:Integer;
  tmp:Byte;
 
procedure CalOneByte(AByte:Byte);
var
  j:Integer;
begin
  crc:=crc xor AByte;
  for j := 0 to 7 do
  begin
    tmp:=crc and 1;
    crc:=crc shr 1;
    crc:= crc and $7FFF;
    if tmp = 1 then
      crc:= crc xor GENP;
    crc:=crc and $FFFF;
  end;  
end;

begin
  crc:=$FFFF;
  for i := AStart to AEnd do
    CalOneByte(AData[i]);
  Result:=crc;
end;  

end.
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值