0

Delphi XE2 を使用しており、シリアル ポート経由でデバイスと通信しようとしています。コミュニケーションは簡単なはずですが、いくつか問題があります。デバイスの通信プロトコルは次のとおりです。I (マスター) は、":" で始まり、CRLF で終わるフレームを送信します。デバイス (スレーブ) は、同じ形式 (「:」で始まり、CRLF で終わる) で応答を送信します。

WinAPI と重複しない IO を使用しています。私が抱えている問題は、デバイスからの応答として #0 文字を受け取ることが非常に多いことです。デバイスプロバイダーのアプリケーションを使用できるので、問題は私の側にあると確信しており、通信が正常に行われていることがわかります。

COMポートをセットアップする方法は次のとおりです。

Result := False;
FFileHandle := CreateFile('COM3', GENERIC_READ OR GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);
if FFileHandle = INVALID_HANDLE_VALUE then
  Exit;

if not GetCommState(FFileHandle, DCB) then
  Exit;
DCB.BaudRate := ASettings.BaudRate;
DCB.Flags := 1 OR // BINARY
             (DTR_CONTROL_ENABLE shl 4) OR
             (RTS_CONTROL_ENABLE shl 12);

DCB.XonLim := 100;           // transmit XON threshold
DCB.XoffLim := 100;          // transmit XOFF threshold
DCB.ByteSize := 8;           // number of bits/byte, 4-8
DCB.Parity := 0;             // 0-4=no,odd,even,mark,space
DCB.StopBits := ONESTOPBIT;  // 0,1,2 = 1, 1.5, 2
DCB.XonChar := #1;           // Tx and Rx XON character
DCB.XoffChar := #2;          // Tx and Rx XOFF character
DCB.ErrorChar := #$FF;       // error replacement character
DCB.EofChar := #$0A;         // end of input character
DCB.EvtChar := #$0A;         // received event character
if not SetCommState(FFileHandle, DCB) then
  Exit;
if not SetCommMask(FFileHandle, EV_RXCHAR OR EV_TXEMPTY OR EV_RXFLAG) then
  Exit;
Timeouts.ReadIntervalTimeout := 1200;
Timeouts.ReadTotalTimeoutMultiplier := 1;
Timeouts.ReadTotalTimeoutConstant := 1200;
Timeouts.WriteTotalTimeoutMultiplier := 0;
Timeouts.WriteTotalTimeoutConstant := 0;
if not SetCommTimeouts(FFileHandle, Timeouts) then
  Exit;
if not PurgeComm(FFileHandle, PURGE_TXABORT OR PURGE_RXABORT OR PURGE_TXCLEAR OR PURGE_RXCLEAR) then
  Exit;
if not ClearCommError(FFileHandle, Errors, @ComStat) then
  Exit;
if not SetupComm(FFileHandle, 1024, 1024) then
  Exit;
Result := True;

これが私が書く方法です:

function TCOMPortWrapper.Write(const AFrame: AnsiString): TComPortWriteRes;
var
  Written: Cardinal;
  Err: Cardinal;
  Stat: TComStat;
  Mask: Cardinal;
begin
  Result := CPW_ERROR;
  ClearCommError(FFileHandle, Err, @Stat);
  if not IsOpened then
    Exit;
  if not WriteFile(FFileHandle, AFrame[1], Length(AFrame), Written, nil) then
    Exit;
  Mask := EV_TXEMPTY;
  if not WaitCommEvent(FFileHandle, Mask, nil) then
    Exit;
  ClearCommError(FFileHandle, Err, @Stat);
  Result := CPW_OK;
end;

そして最後に、私が読む方法は次のとおりです。

function TCOMPortWrapper.Read(out Frame: AnsiString): TComPortReadRes;
var
  S: AnsiString;
  BytesRead: Cardinal;
  Mask: Cardinal;
begin
  Result := CPR_ERROR;
  if not IsOpened then
    Exit;
  SetLength(S, 4096);
  Mask := EV_RXFLAG;
  if not WaitCommEvent(FFileHandle, Mask, nil) then
    Exit;
  if not ReadFile(FFileHandle, S[1], Length(S), BytesRead, nil) then
    Exit;
  SetLength(S, BytesRead);
  Frame := S;
  Result := CPR_OK;
end;

上で述べたように、実際のフレームを取得する代わりに読み取りで #0 文字の文字列を取得します。私はシリアル通信に非常に慣れていないので、私の間違いは WaitCommEvent API 呼び出しにあると思います。

手伝ってくれてありがとう!

4

1 に答える 1