Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Highly responsive serial port

210 views
Skip to first unread message

Arkej

unread,
Aug 4, 2008, 9:19:59 AM8/4/08
to
Hello,
before I dive into the world of kernel mode drivers I'd like to know:
My problem consists of reading from a serial COM port as fluent as
possible(real-time like). It looks like it is not doable in user mode. If
due to scheduling of threads my reader thread doesn't get enough time I have
a delay of read on the input. Let's say I have to process data coming every
30ms and then reply in 20ms. If my reader only reads the input buffer after
50ms I'm too late to reply but I don't know it.
1. Would a driver(in kernel mode) have enough priority to process such
requests in time?
2. Is there any other way in user mode to assure constant read of serial
port?

Thanks
Robert


David Craig

unread,
Aug 4, 2008, 11:49:10 AM8/4/08
to
If the PC can be configured by someone other than yourself, forget it.
Windows is NOT a real time operating system. If you can choose the
hardware, installed software, and keep it locked down it is possible to
obtain your timings but it will require a lot of testing. Run the thread in
the real time class which will also help. At that priority you will be very
close to as fast as any driver can be.

"Arkej" <NOSPAMro...@REPLACESINETsiol.si> wrote in message
news:%23ZBhNTj...@TK2MSFTNGP02.phx.gbl...

Jochen Kalmbach [MVP]

unread,
Aug 4, 2008, 12:10:10 PM8/4/08
to
Hi David!

> If the PC can be configured by someone other than yourself, forget it.
> Windows is NOT a real time operating system. If you can choose the
> hardware, installed software, and keep it locked down it is possible to
> obtain your timings but it will require a lot of testing. Run the thread in
> the real time class which will also help. At that priority you will be very
> close to as fast as any driver can be.

And you must call "timeBeginPeriod(1)".

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/

Pavel A.

unread,
Aug 4, 2008, 2:42:25 PM8/4/08
to
In addition to other replies:
Note that read request to the serial driver can be completed
by reading specified number of bytes, or by timeout.
In it's turn, the resolution of these timeouts is same as the
system clock resolution.
So, you either don't want to rely on timeouts (design your protocol
so that request can be completed by reading exact number of bytes)
or set the minimum possible timer resolution (which may be ~ 2ms)
and use short timeouts.
Giving your process realtime priority is pointless
without this, because what holds your process is waiting for
i/o completion, not other processes.

Also, for real time serial protocols I'd warmly recommend
a cheap programmable microcontroller, connected to PC
over USB, ethernet or even serial port (not in real-time, of course).

Regards,
--PA

Arkej

unread,
Aug 5, 2008, 2:56:25 AM8/5/08
to
David, Jochen,

thanks for the tips. I'm gonna try to read the comm with timeouts,
completion ports etc... What I tried was a loop in a thread with a
Sleep(1)(I guess this one does not come back in time when load on the system
increments). In my test I log what and when I've read and it's interesting
that when there is a delay it also logs all the bytes accumulated in the
buffer. I supposed the driver is in fact reading from the device and filling
the buffer. My approach was maybe wrong because using ClearCommError to get
num of bytes in the buffer and then start an asynch readfile on it in a loop
that is sleeped looks like no good for fluency.

As much as I've had time to look in the WDF it is all new to me and it would
take a lot of time to learn and get experience.
Thanks again.
Robert

"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message
news:%23LrgVyk...@TK2MSFTNGP05.phx.gbl...

Jochen Kalmbach [MVP]

unread,
Aug 5, 2008, 3:04:19 AM8/5/08
to
Hi Arkej!

> What I tried was a loop in a thread with a
> Sleep(1)

This implies a wait of about 12-15 ms!
Set "timeBeginPersion(1)", this will decrese the delay to 2 ms...

> I supposed the driver is in fact reading from the device and filling
> the buffer.

Yes.

Arkej

unread,
Aug 5, 2008, 3:52:39 AM8/5/08
to
Pavel,

thank you for your hints. What I did in my test with timeouts was to set
ReadIntervalTimout = MAXDWORD to make a call to Readfile function return
immediately. That's all I do with timeouts.
As described in reply above this thread I then read in a loop using
ClearCommError to get the status of the device and read asynchronously the
bytes. Realtime priority gave some results but still sometimes there were
delays(I guess sleep(1) is the most delaying line in my code but also
without it there are still delays and the system gets nonresponsive). I'm
already looking for a hw/sw solution but I need a quick one now until the
hw(microcontroller) is built.

Regards,
Robert

"Pavel A." <pav...@NOfastmailNO.fm> wrote in message
news:%23osAvHm...@TK2MSFTNGP02.phx.gbl...

Jochen Kalmbach [MVP]

unread,
Aug 5, 2008, 4:13:50 AM8/5/08
to
Hi Arkej!

> delays(I guess sleep(1) is the most delaying line in my code but also
> without it there are still delays and the system gets nonresponsive). I'm

You need at least *two* core-CPUs to use "realtime-priority" with an
endless loop! If you have just one core, then the UI will never respond...

Arkej

unread,
Aug 5, 2008, 6:17:26 AM8/5/08
to
Would WaitCommEvent for EV_RXCHAR be a better approach than sleep(1)? Will
it trigger on time?
How does it affect when baudrate is elevated?

Regards
Robert

"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message

news:emTn8ls9...@TK2MSFTNGP06.phx.gbl...

Pavel A.

unread,
Aug 5, 2008, 7:54:08 AM8/5/08
to
Jochen Kalmbach [MVP] wrote:
> Hi Arkej!
>
>> delays(I guess sleep(1) is the most delaying line in my code but also
>> without it there are still delays and the system gets nonresponsive). I'm
>
> You need at least *two* core-CPUs to use "realtime-priority" with an
> endless loop! If you have just one core, then the UI will never respond...

Again, spinning in the app is absolutely pointless.
The app won't see the i/o completed before the driver completes it.
And the driver will complete it *immediately* when the preset
condition occurs, this isn't bound by timer resolution.

Regards,
--PA

Pavel A.

unread,
Aug 5, 2008, 8:00:25 AM8/5/08
to
Yes, sleep(1) will make you wait for too long.
Maybe you need here ReadFileEx with asynchronous callbacks, or
overlapped ReadFile & WaitForSingleObject with timeout

On this page http://www.lvr.com/serport.htm
you can find more info on serial ports under Windows.

Regards,
--PA

Arkej

unread,
Aug 5, 2008, 8:14:36 AM8/5/08
to
Thanks for the link.
I forgot to mention I'm using overlaped readfile with the
waitforsingleobject... Here is the Reader function(the one that gets called
in the loop):

int CCOMreader::Read()
{
DWORD dwError;
DWORD dwRes;
COMSTAT pStat;
bool bResult;
if (bReadWaiting)
{//if previous read hasn't finish return
if ((dwRes = WaitForSingleObjectEx( hReadCompleted, 0, FALSE)) !=
WAIT_OBJECT_0)
{
return 0;//read still in progress
};
if (!GetOverlappedResult(hStream, &olRead, &nBytesRead, TRUE))
{
char cstrTemp[80];
sprintf(cstrTemp, "Read GetOverlappedResult error: %d", GetLastError());
Output(cstrTemp);
};
bReadWaiting = false;//reading finished
ResetEvent(olRead.hEvent);
};
//dump the buffer into log
if (nBytesRead > 0)
{
Output("iC:" + DumpData((BYTE *)bufMsg, nBytesRead));
ZeroMemory(bufMsg,sizeof(bufMsg));
nBytesRead = 0;
};
//if anything on the device call ReadFile asynchronously
ClearCommError( hStream, &dwError, &pStat);
if( dwError )
{
char strE[80];
ZeroMemory(strE,sizeof(strE));
sprintf( strE, "ERR: dwError on comm port check == 0x%08x", dwError);
Output(strE);
}
if (pStat.cbInQue > 0) //read if anything
{
bResult = ReadFile( hStream,
bufMsg,
pStat.cbInQue,
&nBytesRead,
&olRead
);
if (!bResult)
{
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
char strE[100];
ZeroMemory(strE,sizeof(strE));
sprintf( strE, "ERR: FATAL I/O Error %ld I/O Context %lx.%lx\n",
dwError, &(olRead), olRead.hEvent);
Output(strE);
}
else
{
bReadWaiting = true;//overlapped read in process
};

};

};

}


"Pavel A." <pav...@NOfastmailNO.fm> wrote in message

news:uLJUxLv9...@TK2MSFTNGP02.phx.gbl...

Louis

unread,
Aug 6, 2008, 2:15:15 AM8/6/08
to
Arkej wrote:

That is what hardware flow control is used for.

There are two sides to this which I can generalized as follows:

- Receive as fast as you can with high buffers
- Transmit as fast as you can with lower buffers.

This "bucket brigade" concept is simple:

The sender will never know how big or how fast the receiver can handle
the sender's pumping of data. If you used high transmit buffer, the
odds are very good you will get flow control signals. Its not
efficient. In a bucket brigade, one node can not go any faster than
the next and you can't fill it any more than bucket can hold. So an
efficient brigade is one that is near continuous without stopping.

On the other hand, the receiver is fully aware of what it can handle
so it should not be designed to slow down its reception of data -
"Give me what you got" attitude, "I can handle whatever you can send."
It should be a draining concept, don't read a few bytes at time. Get
it all during that one signal instance. But you need to do this
asynchronously and not ask for too much because that can put pressure
on the system to fulfill your request.

Most high end com packages traditionally used a three threads per PORT
approach:

- a receiver thread using a circular buffer, i.e., 16K

- a sender thread using a circular buffer, i.e, 4K

- a UI thread (passive, main or otherwise), that
can read events from the receiver thread or transmit
thread, or it can be done procedurally.

The receiver/transmit thread might run in a lightly higher priority.

Also, the protocols you might use, i.e, transfer a file with ZMODEM,
XMODEM, YMODEM, KERMIT, etc, will help design some of the settings
too. For example, ZMODEM can start with 16K windows that can be
reduced during the protocol transmission if it finds too many retries.
XMODEM on the other hand is a fixed 128 BYTE block concept - very
inefficient.

Overall, the key is to make sure you get your flow control right. You
need to prepare the port with the right set of hardware flow control
flags with the right sizes for buffers and also your timeouts to
minimize the number of cycles.

Finally, a general rule of thumb in thread synchronization
(especially in a non-RTOS) is to stay away from depending on "sleep"
and loop designs in a vain attempt to synchronize I/O. You might get
it to work in one environment, but inevitably its going to bite you
:-) There are scores of books discussions on the subject. Google it.

Keep in mind these designs pre-dated I/O Comp ports ideas, that may
help reduced threads and scalability issues. In my exploration done a
few years ago, I found there some efficiency issues - never mind the
more complex designs especially with our virtual com I/O model, i.e, I
found issues with using the same IOCP model used for sockets, for
RS232. Theoretically should work the same but it doesn't. :)

I do want to give it another shot one day, but we have yet to find a
good reason to break what isn't broke. :-)

--

Ben Voigt [C++ MVP]

unread,
Aug 6, 2008, 3:59:43 PM8/6/08
to
Arkej wrote:
> Would WaitCommEvent for EV_RXCHAR be a better approach than
> sleep(1)? Will it trigger on time?

Yes to the first question, "no guarantees" to the second.

Arkej

unread,
Aug 7, 2008, 6:16:54 AM8/7/08
to
Hello with some news...

Until now the WaitCommEvent solution with readfile after that gave me the
best results. I'm trying now to achieve the same without WaitCommEvent but
only by setting the commtimeouts. As for now the best is RIT = MAXDWORD,
RTTM = MAXDWORD, RTTC = 30. And no more sleeps in there! Timeperiod set to
1! Pretty promising on a dual core machine.

Thanks again for all the tips to all!
Robert

"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:%23rRHJ5$9IHA...@TK2MSFTNGP05.phx.gbl...

0 new messages