Re: Why is the serial port so slow in Linux?
- From: floyd@xxxxxxxxxx (Floyd L. Davidson)
- Date: Mon, 05 Feb 2007 06:17:52 -0900
Richard <rkm@xxxxxxxxxx> wrote:
Floyd L. Davidson wrote:
Do you really want hardware flow control turned off?
Yes, because ultimately I will connect a hardware device
to the serial port which does not support hardware flow
control.
Okay. That's something you can't avoid. You might want to turn
it on for starters. Then when you have something that will
rattle off more than 4 kb of characters, turn it off and figure
out how to deal with the results. Whatever, you'll never be
able to test for overruns with a loopback.
{
printw("Warning - byte received not the same as byte sent\n");
refresh();
};
Heh heh, how many times do you get the warning message?
I never get the warning message. I am doing a loopback test,
so the byte received is always the same as the byte sent,
unless it is not receiving correctly.
Given that you were using non-blocking i/o, it was entirely
possible to get the read out of sync with the write.
Please re-write this using the above comments as a guide,
I made a simplified program, loopback.c, which only does
the loopback test. It runs, there are 10000 bytes sent
and 10000 bytes received. However on Linux it takes 40
seconds to execute, on DOS about 1 second. I still
don't understand why Linux is so slow. Do you still
see problems with the code? The source code follows:
Get over the idea that Linux is 40 times slower than DOS. That
is just silly. Linux easily can run 230k bps serial port
connections at full speed with hardware flow control...
....
fd_setup = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
You do want to open using the O_NONBLOCK flag (otherwise the
open may never return), and then clear the flag after the port
is open.
if (fd_setup == -1)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open serial port ");
return fd_setup;
};
tcgetattr(fd_setup, &options);
memset(&options, 0, sizeof options);
/*
* Enable the receiver and set local mode...
*/
/* no parity (8N1) */
/* no hardware flow control */
/* no software flow control */
options.c_cflag |= (CLOCAL | CREAD | CS8);
You might want to set this too,
options.c_iflag = IGNBRK | IGNPAR;
/*
* Set the baud rate...
*/
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cc[VTIME] = 10; /* 1 second timeout */
options.c_cc[VMIN] = 1; /* read 1 byte */
The VTIME and VMIN elements are tricky, and what you have
is not what the above comments say it is.
If both VTIME and VMIN are greater than 0, the timer is an
_inter-byte_ timer that is only active after the first byte is
received. Since VMIN is 1, the timer in this case does nothing,
since VMIN is satisfied when that first byte is received.
The above can block forever if no data is received, and will
always return at least 1 byte unless interupted by a signal.
The actual value of VTIME will not change the results as long as
VMIN is 1.
Hence, while it doesn't do what the comments suggest, what it
does probably *is* what you want done!
If VMIN were set to 0, a read would not block. The timer would
be a receive timer that activates when the read call is made.
Either at least one byte being available, VTIME expiring, or a
signal will cause a return, and 0 bytes of data might have been
received.
IF VMIN and VTIME are both 0, a read call will return
immediately with whatever data is available, which might be zero
bytes.
/*
* Set the new options for the port...
*/
tcsetattr(fd_setup, TCSANOW, &options);
/* serial port is now configured */
return fd_setup;
};
/* ---------------------------------------------------------------- */
int main()
{
int fd; /* File descriptor for the serial port */
int byte_sent,byte_received;
unsigned char char_sent,char_received;
int ns;
fd = setup_serial_port();
if(fd<0)
{
printf("Error opening serial port\n");
printf("Program will be terminated\n");
exit(1);
};
byte_sent = 0x42;
char_sent = 0x42;
num_sent_loopback = 0;
num_loopback = 10000;
do
{ /* loopback loop */
ns = write(fd,&char_sent,1);
You probably should increment char_sent through a range of at
least 10 or so values. Otherwise it is pretty hard to tell if
any data is being dropped.
byte_sent = char_sent;
if (++char_sent > 0x52) char_sent = 0x42;
if(ns <= 0)
{
printf("Error - byte not sent successfully\n");
printf("Program will be terminated\n");
close(fd);
exit(1);
};
num_sent_loopback++;
ns = read(fd,&char_received,1);
One way to make your receiver re-sync with the sender is to let
the read grab more than one byte at a time, if it is available.
You don't really need to look at all the data, only the last byte.
char ch[10];
ns = read(fd, &ch, 10);
if (ns > 0 && ns < 11) {
char_received = ch[ns - 1];
}
if(ns>0)
{
byte_received = char_received;
if(byte_received != byte_sent)
{
printf("Warning - byte received not the same as byte sent\n");
};
num_received_loopback++;
However, I don't see a clear reason that your code would be
slow... except that you are looping back a serial port and
doing single byte i/o on a multitasking system.
Try writing 1000 bytes at a time, and then putting read() into a
loop that reads 1000 bytes at a time.
What kernel are you running this on, and what is the HZ value
set to? If you have HZ set at the traditional 60, and each
read and write causes your process to give up its time slice,
this is going to be very very slow...
You simply cannot get true asynchronous i/o on a single cpu
system, and by doing this 1 byte at a time you are probably
seeing nothing other than how fast your scheduler is giving your
program time slices.
There might be various ways around that. But I would *not*
suggest that you bother trying to determine what is happening
because no matter how you slice it any code developed using a
serial port loopback is asking for trouble. Either use two
different machines, or do something like I commonly do and have
the program talk to a modem that has at least one really verbose
command. Something you can query with a command that triggers a
page or two of text. Then alternate between sending it commands
where it merely says "OK\r\n" and one where it tells you every
thing including what time of day it was made and the name of the
QA guy that signed it off... :-)
That is exactly what the terminal programs on the website that
I gave you last time were written to do.
--
Floyd L. Davidson <http://www.apaflo.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@xxxxxxxxxx
.
- Follow-Ups:
- Re: Why is the serial port so slow in Linux?
- From: Richard
- Re: Why is the serial port so slow in Linux?
- References:
- Re: Why is the serial port so slow in Linux?
- From: Richard
- Re: Why is the serial port so slow in Linux?
- Prev by Date: Re: A7M266-D mismatched processors?
- Next by Date: Re: Install linux directly through a internet connection?
- Previous by thread: Re: Why is the serial port so slow in Linux?
- Next by thread: Re: Why is the serial port so slow in Linux?
- Index(es):
Relevant Pages
|