Re: Why is the serial port so slow in Linux?
- From: floyd@xxxxxxxxxx (Floyd L. Davidson)
- Date: Sun, 04 Feb 2007 06:40:38 -0900
Richard <rkm@xxxxxxxxxx> wrote:
Floyd L. Davidson wrote:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Post the code you are using to open, to configure, to write and
to read the serial port. The smallest possible program that
will demonstrate how each functionality is coded, while still
If you had made an attempt to reduce the size and complexity of
the program, as requested, it would have provided an opportunity
to see your own mistakes. That was part of the reason for requesting
"the smallest possible program", rather than something like this.
....
To compile serialcomm.c, the ncurses library must be linked to it.
Here is one possible compile command:
gcc -lncurses -g -o serial_comm serial_comm.c
That command line will search the ncurses library *before* it compiles
the source code file.
gcc -g -o serial_comm serial_comm.c -lncurses
The order in which files are specified on a command line for gcc is
significant.
....
/*
The following procedure reads to the end of the current line. It stops
It is a function, not a proceedure.
void to_next_line(FILE *input_file)
{
char ascii_read_nl;
if(!feof(input_file))
{
do
{
fscanf(input_file,"%c",&ascii_read_nl);
}
while(!( (ascii_read_nl=='\n') || (feof(input_file)) ) );
}
The above function is simply non usable. The feof() function indicates
when an attempt has been made to read past the end of file, it does not
indicate that the *next* read will do that. It is not appropriate as
the control mechanism in a read loop, and in the above example will read
the last character twice.
....
int setup_serial_port(int icom, long int baud)
{
int _fd;
struct termios options;
/*
* Open serial port
*/
if(icom==1)
{
_fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
Identifiers beginning with a single underscore are in the name
space reserved for the compiler implementation and should not be
used by a program.
}
else
{
_fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
};
if (_fd == -1)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open serial port ");
return _fd;
}
else
fcntl(_fd, F_SETFL, 0);
See the man page for fnctl(). The above will *clear* three
flags: O_APPEND, O_NONBLOCK and O_ASYNC. Note that on Linux
O_NONBLOCK is the same as O_NDELAY and FNDELAY, but that may not
be true on other systems. (You should use O_NONBLOCK because it
will always be the same, while the other two may have slightly
different definitions on different systems.)
/* to make the read function return immediately (non-blocking mode): */
fcntl(_fd, F_SETFL, FNDELAY);
You just cleared this flag, now you set it. Why do you want
non-blocking mode? That is almost certainly going to be a
problem.
All you need to do is set the c_cc[VTIME] and c_cc[VMIN] values
appropriately in the termios struct.
/*....
* Get the current options for the port...
*/
tcgetattr(_fd, &options);
switch (baud)....
{
case 0: /* note: case 0 disconnects the line */
cfsetispeed(&options, B0);
cfsetospeed(&options, B0);
break;
options.c_cflag |= (CLOCAL | CREAD);
/* no parity (8N1): */
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
/* disable hardware flow control: */
options.c_cflag &= ~CRTSCTS;
Do you really want hardware flow control turned off?
Regardless, the above is an error prone method for setting
configuation options. You have no idea what non-POSIX elements
any given OS has added to the termios structure, and are leaving
anything you do no explicitely set at whatever it was.
It works much better to clear the struct termios, and then set
only those that you need.
memset(&options, 0, sizeof options);
options.c_cflag = CLOCAL | CREAD | CS8;
cfsetispeed(&options, ...);
cfsetospeed(&options, ...);
void set_noncanonical_input_mode (void)....
{
atexit (reset_canonical_input_mode);
There is no advantage to doing that.
/* Set noncanonical, nonecho mode: */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 0; /* so that read exits immediately */
tattr.c_cc[VTIME] = 0; /* so that read exits immediately */
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}; /* end set_noncanonical_input_mode */
That is almost guaranteed to be a problem. You've set
non-blocking and raw mode. This program is going to be
a cpu hog, and is also very timing critical.
....
int main()
....
if( (input_file=fopen(&file_name[0],"r")) == NULL)
if ((input_file=fopen(file_name, "r") == NULL)
....
to_next_line(input_file); /* skip first line of comments */
io_result = fscanf(input_file,"%d",&icom);
I'm not going to bother trying to debug your use of scanf family
functions. That is why this should be reduced to the smallest
working program that demonstrates what you are doing. As is,
you might well have more problems with using scanf (which would
be typical) than with using termios! You need to modularize so
that debugging can isolate problems.
initscr(); /* start curses mode */
atexit(exit_curses); /* so that we exit curses mode at exit */
clear();
nocbreak();
Using ncurses is the same. It's just obfuscation at this point.
I don't have the time to debug your ncurses programming
either... ;-)
do
{ /* loopback loop */
ns = write(fd,&char_sent,1);
....
ns = read(fd,&char_received,1);
nd = getch();
}
while(!( (ns>0) || (nd>0) || (i_read_loopback >= 100000) ) );
if(ns>0)
{
byte_received = char_received;
if(byte_received != byte_sent)
{
printw("Warning - byte received not the same as byte sent\n");
refresh();
};
Heh heh, how many times do you get the warning message? I would
guess that under the right circumstances it would be repeated at
almost every iteration of the loop, as there is virtually no
guarantee in your code that the read() function is going to
find, on a one-to-one basis, the character just sent. One
iteration that doesn't, and your loop can *never* recover the
correct timing relationship.
Please re-write this using the above comments as a guide, and
repost it. Do *not* include ncurses or use scanf(), and do
*not* add all the abstractions. Just the open a serial port,
configure it, and use a loop to write to it and read from it.
The read/write can be a single line of text from a static
buffer.
If you would like some programming examples, here are several ways
to write test programs for Linux serial ports:
http://www.apaflo.com/floyd_davidson/code/terminal/
--
Floyd L. Davidson <http://www.apaflo.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@xxxxxxxxxx
.
- References:
- Why is the serial port so slow in Linux?
- From: Richard
- Re: Why is the serial port so slow in Linux?
- From: Floyd L. Davidson
- Re: Why is the serial port so slow in Linux?
- From: Richard
- Why is the serial port so slow in Linux?
- Prev by Date: Re: Why is the serial port so slow in Linux?
- Next by Date: Re: Linux solution wanted
- 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
|