如何发送字符串(串口),等待(超时)并捕获回复字符串?

时间:2012-12-13 21:33:19

标签: multithreading delphi function serial-port communication

在stackoverflow上阅读这个非常有趣的主题后 - > How to wait for COM port receive event before sending more data in a loop

我遇到了很多问题,我尝试了很多解决方案......不幸的是,没有什么能正常运作!

许多串口库是事件驱动的,我很难理解它们。

我尝试过使用Asyncpro,Synaser和TComport。

是否可以拥有这样的功能:

SerialSendandReply(tx string here,timeout)返回rx字符串,如果超时发送错误字符串

来自设备的响应有几毫秒的阻塞方式会更好吗?

像这样:

Dosomething here
showmessage(SerialSendandReply('test',100 )); //100 ms timeout
dosomething else

使用此代码

TForm1 = class(TForm)
 ...
private
    IOEvent           : THandle; // used for IO events
    IORx              : string;
    Comport           : TapdComport;
...


procedure TForm1.ComportTriggerAvail(CP: TObject; Count: Word);

var i       : integer;

begin
 for i:=1 to Count do
  IORx:=IORx+Comport.GetChar;
 SetEvent(IOEvent);
end;

function TForm1.SerialSAWR(tx : string; TimeOut : integer) : boolean;
begin
 Result := False;
 try
  IORx := ''; // your global var
  ResetEvent(IOEvent);
  Comport.PutString(tx);
  Result := WaitForSingleObject(IOEvent, TimeOut) = WAIT_OBJECT_0;
 except
  on E : Exception do
   // dosomething with exception
 end;
end;

// constructor part
IOEvent := CreateEvent(nil, True, False, nil);
// destructor part
 if IOEvent <> 0 then
  CloseHandle(IOEvent);

然后我试着调用这个函数:

if SerialSAWR('test'; 5000) then showmessage(IORx);

发送效果很好,但不会在字符串中返回任何内容。

有任何建议吗?

非常感谢!

此致 劳伦

3 个答案:

答案 0 :(得分:1)

我使用TComPort并创建了以下例程来执行您的要求。 TComPort监视其监视线程中收到的字符,我的例程在不调用Application.ProcessMessages的情况下等待字符。它可能不是最优雅的代码,但它工作正常。

function TArtTComPort.SerialPort_AwaitChars(AMinLength: integer;
      ATerminator: char; AQtyAfterTerm: integer; ARaise: boolean): string;
    var
      fDueBy : TDateTime;

      function IsEndOfReplyOrTimeout( var AStr : string ) : boolean;
      var
       I : integer;
      begin
        Result := False;
        If ATerminator <> #0 then
          begin
          I := Length( AStr ) - AQtyAfterTerm;
          If I > 0 then
            Result := AStr[I] = ATerminator;
          end;
        If not Result then
          Result := Length(AStr) >= AMinLength;


        // Un-comment this next line to disable the timeout.
        //Exit;

        If not Result then
          begin
          Result := Now > fDueBy;
          If Result then
            If ARaise then
              raise EArtTComPort.Create( 'Serial port reply timeout' )
            else
              AStr := '';
          end;
      end;

    var
      Events : TComEvents;
      iCount : integer;
      S : string;
    begin
      Assert( AMinLength > 0, 'Invalid minimum length' );

      If not FComPort.Connected then
        begin
        Result := '';
        Exit;
        end;

      fDueBy := Now + (FTimeoutMS * TDMSec );

      Result := '';


      Repeat

          // Setup events to wait for:
          Events := [evRxChar, evTxEmpty, evRxFlag, evRing, evBreak,
                   evCTS, evDSR, evError, evRLSD, evRx80Full];

          // Wait until at least one event happens.
          FComPort.WaitForEvent(
            Events,
            FStopEvent.Handle,
            FTimeOutMS);

          If Events = [] then // timeout
            begin
            If ARaise then
              raise EArtTComPort.Create( 'Serial port reply timeout' )
            end
           else
            begin
            If evRxChar in Events then
              begin
              iCount := FComport.InputCount;
              FComPort.ReadStr( S, iCount );
              Result := Result + S;
              end;
            end;

      until IsEndOfReplyOrTimeout( Result );


    end;

答案 1 :(得分:1)

我换了nrComm Lib(v9.31)......使用非常简单并得到很好的支持。

唯一的缺点是不是免费和开源......但它是值得的!

它也是线程安全的,也是好的:)。

非常感谢大家的回复!

答案 2 :(得分:0)

您正尝试从主线程执行异步I / O.这将永远不会与GUI一起使用。

执行复杂的异步I / O更适合单独的线程。我有一个阻塞的串行通信包(我认为Synaser也有阻塞模式)和这样的函数:

function TransmitReceive(const msg: AnsiString; var reply: AnsiString; 
  timeOut: Integer): Boolean;

将复杂的代码逻辑放在thread.execute内,并用事件信号触发逻辑的开头。

例如,通过PostMessage调用将结果等传达给主线程。