(E-Mail Removed) (Killans - First And Last And Always) wrote:
>I'm developing an application in C++ on RedHat 7.3 which reads data from
>a serial port (RS422 - I'm using an Amplicon board) which comes
>from another third-party device. The communication protocol for
>the device specified that the device sends data using 8O1 characters
>- i.e. 8 data bits, parity bit using odd parity, and 1 stop bit.
Can't possibly work! You've only got 8 data bits per character
frame. If you use the high bit for parity, your data *necessarily*
must be 7 bit data.
>However, this has been causing me problems. I'm a relative newcomer to
>Linux development, and a complete newcomer to serial programming. I've
>been using the "Serial Programming HOWTO" and the "Serial Programming
>Guide for POSIX Operating Systems" that Google was good enough to find
>for me.
Be careful of the "Serial Programming HOWTO", as it has a number
of serious flaws. The guide for POSIX is an excellent resource!
>Referring to those documents, I had configured the serial port in
>software to use those settings:
>
> TermIOs portParams; // Terminal parameters for RS422 port
You aren't showing your entire code segment here, but I'm
willing to bet, based on what you have shown, that one of the
problems with the HOWTO is built into your code.
Note that POSIX says you must initialize the termios struct by
calling tcgetattr(); however, that does *not* mean what you get
is in any way a valid configuration for your purposes! Hence
you should not assume that any member of the termios struct is
in any way correct. Either zero every member, or set it to some
specific value known to be required by your program.
You are only turning on and off certain bits. What about any
bits that you don't happen to be setting? They might be right,
and they might be wrong. Hence, the first of these changes
should be
portParams.c_cflag = PARENB; // Switch on "enable parity bit" flag
Which zeros all bits except the ones you are setting. Obviously
you then have no need to switch any other bits off. And for that
matter you could set all the required bits with just one statement,
portParams.c_cflag = (PARENB | PARODD | CS8);
Of course, the first thing you'll discover about the above line is
that it is incomplete, and will *not* work! You probably want this,
portParams.c_cflag = (PARENB | PARODD | CS8 | CREAD | CLOCAL);
(Turning on the receiver does seem line a useful idea, eh? ;-)
> // Set to 8-bit odd parity with 1 stop bit and no flow control
> portParams.c_cflag |= PARENB; // Switch on "enable parity bit" flag
> portParams.c_cflag |= PARODD; // Switch on "odd parity" flag
> portParams.c_cflag &= ~CSTOPB; // Switch off "2 stop bits" flag
> portParams.c_cflag &= ~CSIZE; // Reset the character size bits
> portParams.c_cflag |= CS8; // 8 data bits
> portParams.c_cflag &= ~CRTSCTS; // Turn off hardware flow control
>
>and to enable the INPCK and ISTRIP settings, as recommended by
>those two documents (planning to implement parity error checking later):
>
> // Enable parity checking, and strip the parity bit
> portParams.c_iflag |= (INPCK | ISTRIP);
The same comments as above go for all of the termios flag members.
Plus, there is one more gotcha that isn't discussed in either of
the resources you are using. As the POSIX guide mentions, POSIX
requires certain members to exist in the termios, but also
allows the platform to have other "non-standard" members... and
Linux has one, which is important. You want something like this
in your configuration section,
#include <sys/ioctl.h> /* defines N_TTY */
#ifdef __linux__
/* for linux only */
tty.c_line = N_TTY; /* set line discipline */
#endif
The above prevents a common problem where a serial port has be
used previously by another application (for isdn, ppp, or a
mouse are common examples) that sets the line discipline to a
value other than N_TTY (which is 0). If that program exits and
first resets the original configuration, there perhaps no
problem; but if the program crashes, is killed with SIGKILL, or
simply does not reset the port, it can leave the line discipline
value set to something which absolutely will not work for your
application. (I'm not sure about the others, but N_PPP is
guaranteed to make your serial port appear to be dead as door
nail for use by a terminal program.)
>However, I've been finding that the data bytes that my application has
>received have had the top bit stripped out, effectively reducing them
>to 7-bit values containing screwed-up data.
>
>On disabling the ISTRIP setting, everything worked. However, when I
>enabled the PARMRK setting, I was getting a ton of parity error markers
>(0xFF) coming through with the data, which suggests that the underlying
>serial driver might be interpreting the top data bit as a parity bit.
>
>So, it appears that I can't get 8-bit data and a parity bit via the
>serial port. This is inconvenient, because I'd really like to have
>genuine parity error checking available.
It really isn't very useful anyway... ;-)
Parity checking has several problems. First, it requires too much
overhead to check each and every byte. Moreover, it can easily miss
certain multibit errors.
Hence you are far better off blocking your data into some type of
a large frame, and using CRCs to validate the blocks, as one example
of a better method.
>I'd really appreciate any advice any experts here have. Have I totally
>misunderstood something? As I said, I'm a newbie at this, so it's entirely
>possible that I've screwed up somewhere, so please let me know if I have.
>
>Incidentally, I'm also a newcomer to the comp.os.linux* heirarchy. My
>apologies if I've posted to an inappropriate group - if there's a more
>appropriate group I should have posted this to, please let me know.
I'd say you've made an *excellent* choice by crossposting to two very
applicable newsgroups.
>Thanks very much in advance,
>
>Mike
--
FloydL. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska)
(E-Mail Removed)