在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);
发送效果很好,但不会在字符串中返回任何内容。
有任何建议吗?
非常感谢!
此致 劳伦
答案 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
调用将结果等传达给主线程。