RE: Using 16550 UART FIFO in polled mode (CE5.0)
- From: Michael Schwab <MichaelSchwab@xxxxxxxxxxxxxxxxxxxxxxxxx>
- Date: Fri, 28 Sep 2007 06:08:01 -0700
Well, I have answered some of my own questions, but not all
I have printed out and studied the 16550 UART manual, and I can see some of
my answers there. In the interrupt enable logic you can set a “trigger
level” on the FIFO, to set how full the FIFO must get before interrupting the
processor. The possible values are 1, 4, 8, or 14. So, obviously the serial
port driver uses the 14 level, and does not even get an interrupt from the
UART until there are 14 bytes in the FIFO.
But, there still must be some “gap detection” logic, for the UART to decide
to interrupt the processor anyway for less than 14 bytes, when data stops
coming in. Reading farther, the answer is: 4 character times. If no data
has been received for 4 character times, and there is at least 1 byte in the
FIFO, the UART interrupts the processor. 4 character times at 38400 baud is
1.04 milliseconds. So, since the Microsoft serial port driver is obviously
interrupt based, now the 14-byte-at-a-time behaviour makes sense.
I don’t know how much I can directly adjust these parameters without
interfering with the driver code. Reading through the 16550 manual, it seems
to me that it would be not too hard to control the UART directly ourselves
(from the 2ms HPT), and not use the Microsoft driver at all. Is this
possible? Would I need to remove the COM1/COM2 registries in PlatBuilder?
"Michael Schwab" wrote:
My application is a heavy GUI front-end to a bunch of embedded Hitachi.
processors, and I have to communicate with them through RS-232 (legacy stuff,
I know), at a high baud rate (currently using 38400 baud). Most important is
not to miss any bytes, and I have to be able to respond to a message from the
Hitachi every 120 milliseconds. I was afraid/lazy to try to use the serial
port interrupt-based methods, because they might not be able to keep up with
these requirements without losing data, and I wanted to keep my code
"simple", not write drivers, etc. I am also using Rhapsody UML for code
generation, and I have no idea how to implement ISRs in that environment.
I happen to have a very high priority (91) thread (HPT) that runs every 2
milliseconds (and needs to complete its activities within 1ms). So, with the
16-byte FIFO in the UART I figured I could read the UART (from this HPT)
every 2ms, and get about 7-8 bytes each time (at 38400 baud). I only use
CreateFile, ReadFile, and WriteFile. I have the timeouts (see code below)
set to return immediately if there are no bytes in the buffer (according to
one MSDN note on the subject).
But the pattern I'm getting (from ReadFile) is not the 7-8 bytes per 2ms
"tick" that I expected. Instead, while data is still coming in, I get
exactly 14 bytes one tick, then nothing the next, then 14, then nothing, etc.
Occasionally I get 14 bytes on 2 ticks in a row, other times I can get 2
ticks in a row with no bytes, then data starts again. (I guess it averages
out to about the 7.7 bytes/tick I would expect). Then once data stops coming
in from the Hitachi, the next ReadFile returns the remaining bytes from the
end of the message.
(My typical call: iReadResult = ReadFile(m_hComm, cData, 180, &dwByteCount,
0);
My problems are:
- This delays me an extra 2ms to notice and respond to bytes in the first
part of the packet.
- I have code of my own that tries to detect "gaps" in communication to
detect end-of-message, and when the UART can return 1-2 2ms ticks with no
data in the middle of a message, I start to think that it's a real gap.
- It's just plain weird, and not what I expected! :)
My questions are:
- Can I change the 14 byte level that the driver uses to "delay" giving me
data, to something like 7 or 8, so I can get data every 2ms?
- How does the UART (or driver) "know" that more data is coming in, so that
it waits until next time to give me the 14 bytes? Does it look for some gap
in bytes coming in? If so, how long is the gap? Or is there a bit it
detects that shows that another byte is actively coming in (i.e start bit
seen)?
- Speaking of bits, is there a way to directly read the status register of
the UART to know if an overrun or framing error has been detected in the
incoming bytes? And, if 7-14 bytes have come in since the overrun/framing
error, would the bit still be set?
(Note: I need to do this for all 4 comm ports!)
------------------
Here is my initialization code for the serial port:
COMMTIMEOUTS noblock;
DCB dcb;
m_hComm = CreateFile(TEXT("COM2:"), GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
SetupComm(m_hComm, 2048, 2048);
PurgeComm(m_hComm, PURGE_TXABORT | PURGE_RXABORT |
PURGE_TXCLEAR | PURGE_RXCLEAR);
if (m_hComm == INVALID_HANDLE_VALUE) {
messageBoxWithAbort(NULL, TEXT("Serial port could not be opened. Wrong port
number?"),
TEXT("serialPort"), 0);
m_hComm = NULL;
return; // error opening com port
}
dcb.DCBlength = sizeof(DCB);
// set communication timeout
GetCommTimeouts(m_hComm, &noblock); // get communication timeouts
// This is the setup recommended by MSDN to have ReadFile return immediately
// if there are no bytes in the buffer:
noblock.ReadIntervalTimeout = MAXDWORD;
noblock.ReadTotalTimeoutConstant = 0; // no milliseconds timeout
noblock.ReadTotalTimeoutMultiplier = 0;
noblock.WriteTotalTimeoutMultiplier = 10;
noblock.WriteTotalTimeoutConstant = 1000;
if (SetCommTimeouts(m_hComm, &noblock) == 0) // set communication timeouts
{
messageBoxWithAbort(NULL,
TEXT("Serial port could not be initialized. Error while setting
communication timeouts."),
TEXT("serialPort"), 0);
CloseHandle(m_hComm);
m_hComm = NULL;
return; // error initialing com port
}
GetCommState(m_hComm, &dcb);
dcb.BaudRate = 38400;
dcb.fBinary = TRUE;
dcb.fParity = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fTXContinueOnXoff = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = FALSE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
if (SetCommState(m_hComm, &dcb) == 0) {
messageBoxWithAbort(NULL,
TEXT("Serial port could not be initialized. Error while setting
communication state."),
TEXT("serialPort"), 0);
CloseHandle(m_hComm);
m_hComm = NULL;
return; // error opening com port
}
- Follow-Ups:
- Re: Using 16550 UART FIFO in polled mode (CE5.0)
- From: <ctacke/>
- Re: Using 16550 UART FIFO in polled mode (CE5.0)
- References:
- Using 16550 UART FIFO in polled mode (CE5.0)
- From: Michael Schwab
- Using 16550 UART FIFO in polled mode (CE5.0)
- Prev by Date: i'm trying to make a list of RAS connections on a pocket pc 2003 d
- Next by Date: Re: Using 16550 UART FIFO in polled mode (CE5.0)
- Previous by thread: Using 16550 UART FIFO in polled mode (CE5.0)
- Next by thread: Re: Using 16550 UART FIFO in polled mode (CE5.0)
- Index(es):
Relevant Pages
|