Problem accessing serial port (9600 7-E-1)

I am writing a simple program which is intended to send short strings to a device i have attached to the serial port of my target machine. The simple program i have written seems to achieve this however the external device requires specific settings as follows.

9600 Baud, 7-bit data, 1 start bit, 1 stop bit, even parity.

After reading a number of samples off the web i decided on this code to set the relevant flags...

termio.c_cflag = B9600 | CS7 | PARENB | CLOCAL | CREAD;
tcflush(ttys0_fd, TCIFLUSH);
tcsetattr(ttys0_fd, TCSANOW, &termio);

I have tried every combination of c_flag settings that made sense from what i read but every time tcsetattr() is called with the specific settings the serial port seems to stop working all together and next time the program runs it cant even access the port.

When i let the program run with whatever default settings exist, i can send and receive strings no problem. What am i doing wrong? This has taken far longer than i had anticipated :(

Thanks in advance. Ill post the entire code if necessary.
Who is Participating?
Duncan RoeSoftware DeveloperCommented:
It's easier to do an stty -F {wanted device} {wanted characteristics} in the shell script before running the program.

If you really must do it programmatically:-

You should always read the current settings into your struct termios, and then modify the ones you care about. Also always use struct termios, not the old struct termio.

   struct termios termio;

   if (tcgetattr(ttys0_fd, &termio)) {perror("tcgetattr");exit(1);}

Use cfset[io]speed to set speeds:

   if (cfsetospeed(&termio, B9600)) {perror("cfsetospeed");exit(1);}
   if (cfsetispeed(&termio, B9600)) {perror("cfsetispeed");exit(1);}

Clear bits you don't want:

   termio.c_cflag &= ~(CSIZE | CSTOPB | CRTSCTS | PARODD);  

Set the bits you want:

   termio.c_cflag |= CS7 | PARENB | CLOCAL | CREAD;

(I added clearing of CRTSCTS hardware flow control).
After your tcsetattr(), you should do a tcgetattr() to another struct termios and compare it for equality with the first one, to ensure all the facilities you requested were set.

HaLAuthor Commented:
Thanks a lot for the example. This is exactly what i was looking for and it works a treat. It seems that serial devices are a little more touchy than i had previously though, at least in Linux anyway.

I was wondering why cfsetospeed() is used to set the speed instead of adding the B9600 flag directly to the c_cflag property. Perhaps to prevent multiple speeds being applied?

Thanks again, the points are yours.
Duncan RoeSoftware DeveloperCommented:
Thanks Hal. To answer your question - in "man termios" see
"(POSIX  says  that  the  baud  speed  is stored in the termios structure without specifying where precisely, and provides cfgetispeed() and cfsetispeed() for getting at it. Some systems use bits selected by CBAUD in c_cflag, other systems use separate fields, e.g.  sg_ispeed and sg_ospeed.)"
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.