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.