serial port question.

serial port question.

Post by Chris.Re.. » Thu, 01 Jun 2006 04:56:08



Hello.

I am working on a project in c++ that uses the serial port.
I have read  "The Serial Programming Guide for POSIX Operating System"
  http://www.easysw.com/~mike/serial/serial.html
And I was successfully able to send and recieve an unsigned character
array of 255 chars.  (from 00 to FF)  However, I was only able to do so
by sending and receiving one byte at a time.  I have been unable to
accurately send and receive more then 6 - 7 bytes at one time, although
my software requirements state that they would like me to be able to
receieve up to 512 unsigned chars at one time.  Has anyone been able to
do this with any degree of accuracy?  Or is this a limitation on the
serial port?

Thanks.

Chris.

 
 
 

serial port question.

Post by Henrik Carlqvis » Thu, 01 Jun 2006 06:14:06



> And I was successfully able to send and recieve an unsigned character
> array of 255 chars.  (from 00 to FF)  However, I was only able to do so
> by sending and receiving one byte at a time.  I have been unable to
> accurately send and receive more then 6 - 7 bytes at one time,

It sounds as if you haven't detected and dealt with short reads and short
writes. If your program does something like this:

ret = write(serial, "Hello World", 11);

You should look at the return value to see if all data was sent. If ret is
only 7 you have only sent the first 7 bytes and should then do a new call
with something like:

ret = write(serial, "orld", 4);

Of course the best thing for you is to write wrapper functions for read
and write that handles short reads and writes.

It was a long time since I wrote code for serial ports, but if I remember
right it was possible to avoid this behavior by turning off O_NONBLOCK
with an fcntl.

regards Henrik
--
The address in the header is only to prevent spam. My real address is:
hc8(at)uthyres.com Examples of addresses which go to spammers:


 
 
 

serial port question.

Post by chris dewber » Thu, 01 Jun 2006 06:40:30


Quote:> And I was successfully able to send and recieve an unsigned character
> array of 255 chars.  (from 00 to FF)  However, I was only able to do so
> by sending and receiving one byte at a time.  I have been unable to
> accurately send and receive more then 6 - 7 bytes at one time

what do you mean when you say "I have been unable to accurately send and receive" ?

Are you receiving corruption or an error message?

here is a snippet of code which sets the serial port to
19200 8E1.  where fd is the file descriptor for the open serial port.

    int fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY );

    struct termios options;

    memset(&options, 0, sizeof( struct termios ) );

    /* set baud rate to 19200 */
    cfsetispeed(&options, B19200);
    cfsetospeed(&options, B19200);

    options.c_cflag |= (CLOCAL | CREAD);

    options.c_cflag |= PARENB;
    options.c_cflag &= ~PARODD;  /* Even parity */
    options.c_cflag &= ~CSTOPB;  /* 1 Stop bit  */
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;      /* 8 data bits  */

    options.c_cflag &= ~CRTSCTS;

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    options.c_iflag |= (IGNPAR); /* Ignore parity */

    options.c_iflag &= ~(IXON | IXOFF | IXANY);

    tcsetattr(fd, TCSANOW, &options);

    tcflush( fd, TCIOFLUSH );

you should then be able to just send and receive data by using the
commands.

write( fd, buffer, length );

bytes = read( fd, buffer, sizeof(buffer));

hope this helps :)

 
 
 

serial port question.

Post by Chris.Re.. » Thu, 01 Jun 2006 06:53:21


"It sounds as if you haven't detected and dealt with short reads and
short
writes."

Thanks.  I definetely know what I need to do with writes.

But what about reads?  How do I know how much more to read when I am
sent a variable amount of data?

Thanks,

Chris.

 
 
 

serial port question.

Post by Floyd L. Davids » Thu, 01 Jun 2006 07:06:59



>here is a snippet of code which sets the serial port to
>19200 8E1.  where fd is the file descriptor for the open serial port.

>    int fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY );

For portability, O_NONBLOCK is preferred over O_NDELAY.  They
are exactly identical on Linux (and most other systems), but
have subtle differences on some platforms that make O_NDELAY
non-portable.

Quote:>    struct termios options;

>    memset(&options, 0, sizeof( struct termios ) );

>    /* set baud rate to 19200 */
>    cfsetispeed(&options, B19200);
>    cfsetospeed(&options, B19200);

>    options.c_cflag |= (CLOCAL | CREAD);

Why use an OR function, when you know that the initial value
is 0?

Quote:>    options.c_cflag |= PARENB;
>    options.c_cflag &= ~PARODD;  /* Even parity */
>    options.c_cflag &= ~CSTOPB;  /* 1 Stop bit  */
>    options.c_cflag &= ~CSIZE;

Why clear bits, when the initial value is 0?

Quote:>    options.c_cflag |= CS8;      /* 8 data bits  */

>    options.c_cflag &= ~CRTSCTS;

All of the above can be a one liner:

     options.c_cflag = (CLOCAL | CREAD | CS8 | PARENB);

Quote:

>    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

>    options.c_iflag |= (IGNPAR); /* Ignore parity */

Why enable parity and then ignore it on input?

Quote:>    options.c_iflag &= ~(IXON | IXOFF | IXANY);

>    tcsetattr(fd, TCSANOW, &options);

This call really should be checked for an error return.

Quote:>    tcflush( fd, TCIOFLUSH );

This call should probably be done prior to calling tcsetattr().

Quote:>you should then be able to just send and receive data by using the
>commands.

Note that you have left the port in non-blocking mode.  Likewise
the termios settings are for raw mode and the VTIME and VMIN
elements of the c_cc array are 0.  That is usable configuration
to be sure, but the code to read and write to the port is very
dependent upon that configuration, and when used as an example
that should be explicitly pointed.

Quote:>write( fd, buffer, length );

The return value of write(2) is important, and this call
necessarily must be in a loop (unless "length" is 0).  If a
value of -1 is returned the value of errno has to be checked to
determine if the loop should continue or not.  The write
function also may or may not write the entire block of bytes
specified by "length", and the loop must handle short writes.

Given the non-blocking and raw mode configuration, the loop
should also include a delay between non-productive calls to
avoid hogging the CPU.

Quote:>bytes = read( fd, buffer, sizeof(buffer));

This also needs to be called in a loop, for the same reasons as
apply to the write(2) function.

--
Floyd L. Davidson            <http://www.apaflo.com/floyd_davidson>

 
 
 

serial port question.

Post by chris dewber » Thu, 01 Jun 2006 08:25:46


Quote:>>   int fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY );

> For portability, O_NONBLOCK is preferred over O_NDELAY.  They
> are exactly identical on Linux (and most other systems), but
> have subtle differences on some platforms that make O_NDELAY
> non-portable.

well you learn something new every day. i will use O_NONBLOCK from now on :)

Quote:>>   options.c_cflag |= (CLOCAL | CREAD);

> Why use an OR function, when you know that the initial value
> is 0?

>>   options.c_cflag |= PARENB;
>>   options.c_cflag &= ~PARODD;  /* Even parity */
>>   options.c_cflag &= ~CSTOPB;  /* 1 Stop bit  */
>>   options.c_cflag &= ~CSIZE;

> Why clear bits, when the initial value is 0?

>>   options.c_cflag |= CS8;      /* 8 data bits  */

>>   options.c_cflag &= ~CRTSCTS;

> All of the above can be a one liner:

>      options.c_cflag = (CLOCAL | CREAD | CS8 | PARENB);

>>   options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

>>   options.c_iflag |= (IGNPAR); /* Ignore parity */

> Why enable parity and then ignore it on input?

>>   options.c_iflag &= ~(IXON | IXOFF | IXANY);

>>   tcsetattr(fd, TCSANOW, &options);

> This call really should be checked for an error return.

>>   tcflush( fd, TCIOFLUSH );

well yes, there is a truck load of redundant code and
un-handled return values. I agree with and thank you for your comments
however i was merely posting this as a quick example to the OP
in an attempt to probe for extra information about the problem.
not to be the be-all end-all of solutions. :)
 
 
 

serial port question.

Post by chris dewber » Thu, 01 Jun 2006 08:49:18


Quote:

> But what about reads?  How do I know how much more to read when I am
> sent a variable amount of data?

i'm not sure exactly what you are asking so i could be completely off target
here but..

this really depends on the format of the data and the protocol you are using,
perhaps you should look at using a packet framing using a method such as SLIP,
or just read the data into a circular buffer and then break the buffer into
individual packets.

an alternative method is to use an interpacket gap time, however then you must
guarantee that an entire packet can be delivered without exceeding the gap time.

also i suggest looking at the select() call if you haven't done so already.
this will allow you to wait for data to arrive before calling the read() command.

 
 
 

serial port question.

Post by Floyd L. Davids » Thu, 01 Jun 2006 11:33:37



>Thanks.  I definetely know what I need to do with writes.

If you aren't sure about reads, is there some reason to be
positive about writes?  You might want to work up an example
program and post it to get comments.

Quote:>But what about reads?  How do I know how much more to read when I am
>sent a variable amount of data?

There are a large number of possible solutions, and without
knowing what you are actually doing there is little point in
trying to recommend which approach would be best.  For example
you don't say if you have control over both ends of this
connection and can implement your own data framing, or not.  The
same is true with timing, where we don't know what parameters
you have to work with.

Generally the best solution is to implement a simple data
framing scheme.  Then the safest way to read data is one byte at
a time with a state machine that first reads input and discards
it until a start of frame flag is seen.  It collects data until
either an error is encountered or an end of frame flag is found.
Errors can be handled in many ways, but one would be to just
start over again.  An end of frame flag invokes whatever is
required to process the frame and then starts the process again.

But in many cases it is not possible to frame data other than by
timed intervals.  In that case the usual sequence is to send
data first, then begin looking for incoming data.  After a
specified interval, if no data is received it is assumed none is
going to be sent.  If any data is received, reading the input is
continued until no data is received for some specified interval,
at which point the currently buffered data is processed and the
cycle is restarted.

Here is some example code for timed reads.  Note that this
assumes O_NONBLOCK has not been set, and that the termios
c_cc[VTIME] element is set to some value greater than 0 while
c_cc[VMIN] is 0.  Be warned that this code is untested and is
just something I threw together for an article posted to answer
a question in comp.os.linux.development.apps about a week ago.

/* these values must be larger than 10 */
#define PRE_TIMEOUT  5000    /* maximum milliseconds before data */
#define POST_TIMEOUT  100    /* minimum milliseconds after data */

#include <time.h>

unsigned int
get_data(int fd, char *buf, int size)
{
  int bytes, cnt = 0, timerflag = 0;
  unsigned int offset;
  struct timespec tv;

  tv.tv_sec  = 0;
  tv.tv_nsec = 10000  /* 10 ms, minimum granularity on some systems */

  while (1) {

    /* quite when we have enough data */
    if (size < 1) {
      break;
    }

    /* pre data timer */
    if (!timerflag && cnt >= PRE_TIMEOUT / (tv.tv_nsec / 1000)) {
      break;
    }

    /* post data timer */
    if (timerflag && cnt >= POST_TIMEOUT / (tv.tv_nsec / 1000)) {
      break;
    }

    if (0 > (bytes = read(fd, buf + offset, 1))) {
      return -1;
    }

    if (bytes > 0) {
      timerflag = 1;
      cnt       = 0;
      --size;
      ++offset;
      continue;
    }
    ++cnt;
    nanosleep(*tv, NULL);
  }

  return offset;

Quote:}

Another possible approach is asynchronous reads and writes.
Using threads or forking processes, or a polling loop, or
interrupt driven asynchronous input.  Obviously that is more
complex that the synchronous write then read cycle described
above.

Here is a URL to several examples of different ways to write
and read data to a serial port.  These example programs implement
a test program that can talk to a typical modem; however, they
can all easily be modified to talk to any serial device.  They
are all limited to simply processing a continuous stream of data
from the device by merely printing it on the screen.  Hence if
a complete packet is needed before processing is started that
functionality would need to be added.

 http://www.apaflo.com/floyd_davidson//code/terminal/

--
Floyd L. Davidson            <http://www.apaflo.com/floyd_davidson>

 
 
 

serial port question.

Post by Chris.Re.. » Thu, 01 Jun 2006 21:40:26


Thanks for your valuable insight Floyd.  I'll let you know how things
go :)

Thanks again,

Chris.

 
 
 

1. serial port question, for programmers

I am writing a program that, among other things, manipulates the serial
port /dev/cua1 (or /dev/ttyS1).  Basically, I open it with open(), then
set a few modes with termios (including ~CRTSCTS so that there's no
hardware handshaking), and then read and write it with read(), write(),
and select.

It doesn't work, so I tried this:
        cat < /dev/cua1 &
        echo AT > /dev/cua1
The modem is definitely answering it, but cat never sees it.  Why not?  I
tried raising CD, CTS, DTR, etc. but this had no effect.

stty says this:

$ stty -a < /dev/cua1
speed 300 baud; rows 43; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase =
^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
parenb -parodd cs7 hupcl -cstopb cread clocal -crtscts
ignbrk -brkint ignpar -parmrk -inpck istrip -inlcr -igncr -icrnl -ixon
-ixoff
-iuclc -ixany -imaxbel
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0
vt0
ff0
-isig -icanon iexten -echo echoe echok -echonl -noflsh -xcase -tostop
-echoprt
echoctl echoke

Questions:

        - why doesn't my 'cat' trick work?  I don't actually intend
                to use 'cat', but as I said my C program doesn't work
                either and I'm hoping that if I can figure out why
                'cat' doesn't work, I'll figure out my "C" problem,
                too.  Hint: I can talk to the modem just fine with
                "cu -l /dev/cua1"

        - does select() function properly on serial ports?

If you post a response, I'd also appreciate an E-MAIL.  Thanks.

--------------------------------
Christopher Piggott, WZ2B
Advanced Development Group
Microwave Data Systems
(a California Microwave company)

2. Runlevel

3. Serial Port Questions

4. compiling and running Jigsaw 2.0 on Unix with JSDK

5. simply (read stupid) Solaris Serial port question (x86 2.4)

6. Mouse works in emacs but no x-popup-menu

7. Serial port question

8. [5/4] make get_pid_list() scan the PID bitmap

9. nwebie serial port question

10. Palm pilot and serial port question

11. Serial port question - I really need help.

12. ip_masq and serial port questions

13. Serial Port Question