Article View: borland.public.cppbuilder.nativeapi
Article #6256Problem with serial communications
From: Martin Nijhoff
Date: Tue, 31 May 2005 09:38
Date: Tue, 31 May 2005 09:38
198 lines
5696 bytes
5696 bytes
Some time ago I wrote a serial port component using overlapped I/O. The
component creates a thread to monitor the serial port an trigger some event
handlers.
The component works fine on both Win98 and WinXP, that is, most of the time.
Sometimes an error occurs while writing to the serial port, in which case
the WriteFile() fails and GetLastError() returns ERROR_OPERATION_ABORTED
(995).
Here is the code for the thread:
#define EV_ALL 0x01FF
class TCOMStatThread : public TThread
{
typedef TThread inherited;
private:
TCustomCOMPort *Port;
OVERLAPPED Overlapped;
HANDLE TerminateEvent;
DWORD dwParam;
void __fastcall HandleCommEvent (DWORD EventMask);
void __fastcall DoOnDataAvailable ();
void __fastcall DoOnError ();
protected:
void __fastcall Execute ();
public:
__fastcall TCOMStatThread (TCustomCOMPort *APort, bool Suspended);
__fastcall ~TCOMStatThread ();
void __fastcall Terminate ();
};
__fastcall TCOMStatThread::TCOMStatThread (TCustomCOMPort *APort, bool
Suspended) : TThread(Suspended)
{
Port = APort;
FreeOnTerminate = true;
Overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
TerminateEvent = CreateEvent(NULL, true, false, NULL);
}
__fastcall TCOMStatThread::~TCOMStatThread ()
{
CloseHandle(Overlapped.hEvent);
CloseHandle(TerminateEvent);
}
void __fastcall TCOMStatThread::Execute ()
{
HANDLE EventHandles[2] = {Overlapped.hEvent, TerminateEvent};
DWORD EventMask, ByteCount;
SetCommMask(Port->Handle, EV_ALL);
while (!Terminated)
{
if (WaitCommEvent(Port->Handle, &EventMask, &Overlapped))
HandleCommEvent(EventMask);
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
switch (WaitForMultipleObjects(2, EventHandles, false, INFINITE))
{
case WAIT_OBJECT_0: // Overlapped I/O completed.
if (GetOverlappedResult(Port->Handle, &Overlapped, &ByteCount,
false))
HandleCommEvent(EventMask);
break;
case WAIT_OBJECT_0 + 1: // Termination signalled.
break;
}
}
}
}
}
void __fastcall TCOMStatThread::HandleCommEvent (DWORD EventMask)
{
if (EventMask & (EV_TXEMPTY | EV_RXCHAR | EV_ERR))
{
COMSTAT Status;
DWORD Errors;
if (!ClearCommError(Port->Handle, &Errors, &Status))
return;
if (Errors != 0 && Port->OnError != NULL)
{
dwParam = Errors;
Synchronize(DoOnError);
}
if (Status.cbInQue > 0 && Port->OnDataAvailable != NULL)
{
dwParam = Status.cbInQue;
Synchronize(DoOnDataAvailable);
}
}
if (EventMask & EV_RXFLAG)
{
...
}
if (EventMask & (EV_BREAK | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
{
...
}
}
void __fastcall TCOMStatThread::DoOnDataAvailable ()
{
Port->OnDataAvailable(Port, dwParam);
}
void __fastcall TCOMStatThread::DoOnError ()
{
Port->OnError(Port, dwParam);
}
void __fastcall TCOMStatThread::Terminate ()
{
inherited::Terminate();
SetEvent(TerminateEvent);
}
Here is the code for reading from and writing to the serial port:
DWORD __fastcall TCustomCOMPort::Read (void *Buffer, DWORD Size)
{
DWORD BytesRead;
if (FHandle == INVALID_HANDLE_VALUE)
throw Exception(SPortClosed);
if (!ReadFile(FHandle, Buffer, Size, &BytesRead, &osRead))
{
if (GetLastError() == ERROR_IO_PENDING)
WaitForAsync(osRead, BytesRead);
}
return(BytesRead);
}
DWORD __fastcall TCustomCOMPort::Write (void *Buffer, DWORD Size)
{
DWORD BytesWritten;
if (FHandle == INVALID_HANDLE_VALUE)
throw Exception(SPortClosed);
if (!WriteFile(FHandle, Buffer, Size, &BytesWritten, &osWrite))
{
if (GetLastError() == ERROR_IO_PENDING)
WaitForAsync(osWrite, BytesWritten);
}
return(BytesWritten);
}
void __fastcall TCustomCOMPort::WaitForAsync (OVERLAPPED &Overlapped, DWORD
&ByteCount)
{
switch (WaitForSingleObject(Overlapped.hEvent, INFINITE))
{
case WAIT_FAILED:
ByteCount = 0;
break;
default:
case WAIT_OBJECT_0:
if (!GetOverlappedResult(FHandle, &Overlapped, &ByteCount, false))
ByteCount = 0;
}
}
I read somewhere that WaitCommEvent() doesn't report all errors. For EV_ERR,
only line-status errors are reported (CE_FRAME, CE_OVERRUN and CE_RXPARITY).
Other errors, like CE_RXOVER and CE_TXFULL, go unnoticed. I think this is
part of the problem, but how do I fix it?
Should I forget about WaitCommEvent() and simply call ClearCommError() in
the thread's main loop? This would cause a big load on the processor, right?
Any help would be greatly appreciated.
Martin
Technical Details
Message-ID:
<429c30e1$1@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!feeder.enertel.nl!nntpfeed-01.ops.asmr-01.energis-idc.net!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mailShow Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,ab642e2ba4263114,start X-Google-Attributes: gid10cc67,public X-Google-Language: ENGLISH,ASCII-7-bit From: "Martin Nijhoff" <dont@mail.me> Newsgroups: borland.public.cppbuilder.nativeapi Subject: Problem with serial communications Date: Tue, 31 May 2005 11:38:55 +0200 Lines: 200 X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2800.1409 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1409 NNTP-Posting-Host: 80.100.129.156 Message-ID: <429c30e1$1@newsgroups.borland.com> X-Trace: newsgroups.borland.com 1117532385 80.100.129.156 (31 May 2005 02:39:45 -0700) Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!feeder.enertel.nl!nntpfeed-01.ops.asmr-01.energis-idc.net!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mail