Delphi (ComPortLib) で azbil SDC/Rシリーズ RS-485 通信

azbil SDC/R シリーズの RS-485 (CPL / Modbus) 通信をやってみました。



使用した USB-RS485 変換ケーブルは、UTS-485TB-V2 (株式会社 ネットメカニズム http://www.cabling-ol.net/cabledirect/UTS-485TB-V2.php)
Windows7、8.1 では、USBポートに接続すると、自動でドライバーがインストールされます。
明示的に送信/受信を切り替える機能は無く、送信をするタイミング以外では、常に受信待機となります。
このため、RTS、DTSのコントロールは不要になり、プログラムはとても簡単なものになります。

※azbil C15、25/26、35/36 では終端抵抗を付けないので、本体内部 J1 の開放が必要です。


CPL の恩恵として、azbil ローダー SPL-C35 から 「CMC10L経由」を選択すると、接続機器との通信が可能になります。




unit azbilCPLUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, CPort, CPortCtl;

type
  TForm3 = class(TForm)
    ComPort1: TComPort;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    ComLed1: TComLed;
    Label1: TLabel;
    Edit3: TEdit;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

//******************************
// CPL
// SetUp C64 = 0
//******************************
procedure TForm3.Button1Click(Sender: TObject);
const
  STX  = #$02;
  EXT  = #$03;
  CRLF = #$0D#$0A;
var
  cmd : string;
  s : string;
  i : integer;
  sum : integer;
  chksum : string;
  res : string;
begin
  Edit1.Text := '';
  Edit1.Update;

  ComPort1.Timeouts.ReadInterval := 20;
  ComPort1.Timeouts.ReadTotalMultiplier := 0;
  ComPort1.Timeouts.ReadTotalConstant := 500;
  ComPort1.Timeouts.WriteTotalMultiplier := 0;
  ComPort1.Timeouts.WriteTotalConstant := 500;

  ComPort1.Port := 'COM7';
  ComPort1.BaudRate := br19200;
  ComPort1.Parity.Bits := prEven;


  ComPort1.StopBits := sbOneStopBit;
  ComPort1.DataBits := dbEight;

  ComPort1.FlowControl.ControlDTR := dtrDisable;
  ComPort1.FlowControl.ControlRTS := rtsDisable;

  // セットアップダイアログを表示
  // ComPort1.ShowSetupDialog;
  try
    // オープン
    ComPort1.Open;
    if ComPort1.Connected then begin

      // 送信コマンド (9101 = PV読み出し)
      cmd := 'RS,9101W,1';

      s := STX; // 電文先頭
      s := s + '01'; // 機器アドレス
      s := s + '00'; // サブアドレス(未使用)
      s := s + 'X' ; // デバイス区別コード 'X' または、'x'
      s := s + cmd ; // コマンド
      s := s + EXT ; // アプリケーション層の終了位置

      // チェックサムの作成
      // STX ~ ETX までを1バイトずつ加算
      sum := 0;
      for i := 1 to s.Length do
        sum := sum + Ord(s[i]);

      // 加算結果の下位1バイト
      sum := sum and $000000FF;

      // 2の補数
      sum := (- sum and $000000FF);

      // それを2バイトの ASCII コードに変換
      chksum := IntToHex(sum, 2);

      s := s + chksum; // チェックサム
      s := s + CRLF;   // 電文の最後

      // 送信文字列
      Edit1.Text := s;
      Edit1.Update;

      // 送信
      ComPort1.WriteStr(s);
      // 受信
      ComPort1.ReadStr(res, 256);

      // 受信文字列
      Edit2.Text := res;

      // PV
      Edit3.Text := '';
      if Copy(res, 7, 2) = '00' then begin
        res := Copy(res, 10, res.Length - Pos(EXT, res) -1);
        Edit3.Text := res;
      end;
      // クローズ
      ComPort1.Close;
    end
    else begin
      ShowMessage('ComPort 接続 失敗');
    end;
  except
      ShowMessage('ComPort オープン エラー');
  end;
end;

//******************************
// Modbus ASCII
// SetUp C64 = 1
//******************************
procedure TForm3.Button2Click(Sender: TObject);
const
  CRLF = #$0D#$0A;
var
  head : string;
  s : string;
  i : integer;
  sum : integer;
  chksum : string;
  res : string;
begin
  Edit1.Text := '';
  Edit1.Update;

  ComPort1.Timeouts.ReadInterval := 20;
  ComPort1.Timeouts.ReadTotalMultiplier := 0;
  ComPort1.Timeouts.ReadTotalConstant := 500;
  ComPort1.Timeouts.WriteTotalMultiplier := 0;
  ComPort1.Timeouts.WriteTotalConstant := 500;

  ComPort1.Port := 'COM7';
  ComPort1.BaudRate := br19200;
  ComPort1.Parity.Bits := prEven;


  ComPort1.StopBits := sbOneStopBit;
  ComPort1.DataBits := dbEight;

  ComPort1.FlowControl.ControlDTR := dtrDisable;
  ComPort1.FlowControl.ControlRTS := rtsDisable;

  try
    // オープン
    ComPort1.Open;

    if ComPort1.Connected then begin
      head := ':';     // 電文先頭
      s := '01';       // 機器アドレス(2桁)
      s := s + '03';   // 読み出しコマンド
      s := s + '238D';     //読み出し先頭アドレス(16進4桁)
      s := s + '0001'; //読み出し数(4桁)

      // チェックサム(LRC)の作成
      // 機器アドレスの先頭からチェックサムの直前まで
      sum := 0;
      // 16進2文字ずつを数値に変換し、加算する
      for i := 1 to s.Length div 2 do
        sum := sum + StrToInt('$' + Copy(s, i * 2 - 1, 2));

      // 加算結果の下位1バイト
      sum := sum and $000000FF;

      // 2の補数
      sum := (- sum and $000000FF);

      // それを2バイトの ASCII コードに変換
      chksum := IntToHex(sum, 2);

      s := s + chksum; // チェックサム
      s := s + CRLF;   // 電文の最後

      // 送信文字列
      Edit1.Text := head + s;
      Edit1.Update;

      // 送信
      ComPort1.WriteStr(head + s);
      // 受信
      ComPort1.ReadStr(res, 256);

      // 受信文字列
      Edit2.Text := res;

      // PV表示
      Edit3.Text := '';
      // 送信した読み出しコマンド '03' を比較
      if Copy(res, 4, 2) = '03' then begin
        // 読み出しデータ数は、2倍されているので注意
        // 1つ目の読み出しデータ(16進)
        res := Copy(res, 8, 4);
        // 16進を10進に変換
        Edit3.Text := IntToStr(StrToInt('$' + res));
      end;
      // クローズ
      ComPort1.Close;
    end
    else begin
      ShowMessage('ComPort 接続 失敗');
    end;
  except
      ShowMessage('ComPort オープン エラー');
  end;

end;

//******************************
// Modbus RTU
// SetUp C64 = 2
//******************************
// azbil マニュアルのCRC計算を Delphi に変換
function CRC16(buf : array of Byte; BufLen: integer): word;
var
  i, j : integer;
  carry : word;
  crc : word;
  crcl : Byte;
begin
  crc := $FFFF;
  for i := 0 to BufLen - 1 do begin
    crc := word(crc xor Ord(Buf[i]));
    for j := 0 to 7 do begin
      carry := crc and $0001;
      crc := crc shr 1;
      if carry = 1 then
        crc := crc xor $A001;
    end;
  end;
  crcl := (crc and $FF00) shr 8;
  crc := crc shl 8;
  crc := crc or crcl;
  result := crc;
end;

procedure TForm3.Button3Click(Sender: TObject);
const
  CRLF = #$0D#$0A;
var
  s : string;
  i : integer;
  n : integer;
  TxBuf : array [0..255] of Byte;

  TxBufLen : integer;
  crc : word;
  RxBuf : array [0..255] of Byte;
  res : string;
begin
  Edit1.Text := '';
  Edit1.Update;

  ComPort1.Timeouts.ReadInterval := 20;
  ComPort1.Timeouts.ReadTotalMultiplier := 0;
  ComPort1.Timeouts.ReadTotalConstant := 500;
  ComPort1.Timeouts.WriteTotalMultiplier := 0;
  ComPort1.Timeouts.WriteTotalConstant := 500;

  ComPort1.Port := 'COM7';
  ComPort1.BaudRate := br19200;
  ComPort1.Parity.Bits := prEven;


  ComPort1.StopBits := sbOneStopBit;
  ComPort1.DataBits := dbEight;

  ComPort1.FlowControl.ControlDTR := dtrDisable;
  ComPort1.FlowControl.ControlRTS := rtsDisable;

  //ComPort1.DiscardNull := True;

  try
    // オープン
    ComPort1.Open;

    if ComPort1.Connected then begin
      s := '01';       // 機器アドレス(2桁)
      s := s + '03';   // 読み出しコマンド
      s := s + '238D';     //読み出し先頭アドレス(16進4桁)
      s := s + '0001'; //読み出し数(4桁)

      TxBufLen := s.Length div 2;
      for i := 1 to TxBufLen do
        TxBuf[i-1] := StrToInt('$' + s[i * 2 - 1] + s[i * 2]);

      // チェックサム(CRC)計算
      crc := CRC16(TxBuf, TxBufLen);
      Edit1.Text := s;

      s := IntToHex(crc, 4);
      // 文字列として表示
      Edit1.Text := Edit1.Text + s;

      // 送信データに CRC を追加
      TxBuf[TxBufLen ]    := StrToInt('$'+Copy(s, 1, 2));
      TxBuf[TxBufLen + 1] := StrToInt('$'+Copy(s, 3, 2));
      TxBufLen := TxBufLen + 2;

      // 送信
      // C80(LEDモニタ) = 2 (受信時点滅)にて確認
      ComPort1.Write(TxBuf, TxBufLen);

      // 受信
      // C80(LEDモニタ) = 1 (送信時点滅)にて確認
      n := ComPort1.Read(RxBuf, 256);

      Edit2.Text := '';
      Edit3.Text := '';
      res := '';
      if n > 0 then begin
        // 受信データを文字列にする
        for i := 0 to n - 1 do
          res := res + IntToHex(RxBuf[i], 2);
        // 受信文字列
        Edit2.Text := res;
        // PV表示
        res := Copy(res, 7, 4);
        // 16進を10進に変換
        Edit3.Text := IntToStr(StrToInt('$' + res));
      end;
      // クローズ
      ComPort1.Close;
    end
    else begin
      ShowMessage('ComPort 接続 失敗');
    end;
  except
      ShowMessage('ComPort オープン エラー');
  end;

end;

end.