Unix C Program to access Serial Port through device file

Accessing the serial port through file

Unix Serial Port Device File information
Port Address       IRQ
COM1(/dev/ttyS0) 0x3F84
COM2(/dev/ttyS1) 0x2F83
COM3(/dev/ttyS2) 0x3E84
COM4(/dev/ttyS3) 0x2E83

Configuring the Serial Port
Most systems support the POSIX terminal (serial) interface for changing parameters such as baud rate, character size, and so on. The first thing you need to do is include the file ; this defines the terminal control structure as well as the POSIX control functions.The two most important POSIX functions are tcgetattr and tcsetattr. These get and set terminal attributes, respectively; you provide a pointer to a termios structure that contains all of the serial options available:
c_cflag -> Control options
c_lflag -> Line options
c_iflag -> Input options
c_oflag -> Output options
c_cc -> Control-characters
c_ispeed -> Input baud (new interface)
c_ospeed-> Output baud (new interface) 
Control Options
The c_cflag member controls the baud rate, number of data bits, parity, stop bits, and hardware flow control. There are constants for all of the supported configurations.
CBAUD Bit mask for baud rate
B0 0 baud (drop DTR)
B50 50 baud
B75 75 baud
B110 110 baud
B134 134.5 baud
B150 150 baud
B200 200 baud
B300 300 baud
B600 600 baud
B1200 1200 baud
B1800 1800baud
B2400 2400 baud
B4800 4800 baud
B9600 9600 baud
B19200 19200 baud
B38400 38400 baud
B57600 57,600 baud
B76800 76,800 baud
B115200 115,200 baud
CSIZE Bit mask for data bits
CS5 5 data bits
CS6 6 data bits
CS7 7 data bits
CS8 8 data bits
CSTOPB 2 stop bits (1 otherwise)
CREAD Enable receiver
PARENB Enable parity bit
PARODD Use odd parity instead of even
HUPCL Hangup (drop DTR) on last close
CLOCAL Local line – do not change “owner” of port
LOBLK Block job control output
CRTSCTS : Enable hardware flow control (not supported on all platforms)
EXTA External rate clock
EXTB External rate clock
The c_cflag member contains two options that should always be enabled, CLOCAL and CREAD. These will ensure that your program does not become the ‘owner’ of the port subject to sporatic job control and hangup signals, and also that the serial interface driver will read incoming data bytes.
The baud rate constants (CBAUD, B9600, etc.) are used for older interfaces that lack the c_ispeed and c_ospeed members.
Never initialize the c_cflag (or any other flag) member directly; you should always use the bitwise AND, OR, and NOT operators to set or clear bits in the members.
Different operating system versions (and even patches) can and do use the bits differently, so using the bitwise operators will prevent you from clobbering a bit flag that is needed in a newer serial driver.
Setting the Baud Rate
The baud rate is stored in different places depending on the operating system. Older interfaces store the baud rate in the c_cflag member using one of the baud rate constants in table 4, while newer implementations provide the c_ispeed and c_ospeed members that contain the actual baud rate value.
The cfsetospeed and cfsetispeed functions are provided to set the baud rate in the termios structure regardless of the underlying operating system interface. Typically you’d use the code in Listing 2 to set the baud rate.
Example: Setting the baud rate
struct termios options;

/*Get the current options for the port…*/
tcgetattr(fd, &options);
/* Set the baud rates to 19200…*/
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
/* Enable the receiver and set local mode…*/
options.c_cflag = (CLOCAL | CREAD);
/* Set the new options for the port…*/
tcsetattr(fd, TCSANOW, &options);
The tcgetattr function fills the termios structure you provide with the current serial port configuration. After we set the baud rates and enable local mode and serial data receipt
The TCSANOW constant specifies that all changes should occur immediately without waiting for output data to finish sending or input data to finish receiving. There are other constants to wait for input and output to finish or to flush the input and output buffers.
Most systems do not support different input and output speeds, so be sure to set both to the same value for maximum portability.
TCSANOW Make changes now without waiting for data to complete
TCSADRAIN Wait until everything has been transmitted
TCSAFLUSH Flush input and output buffers and make the change
Setting the Character Size
Unlike the baud rate, there is no convienience function to set the character size. Instead you must do a little bitmasking to set things up. The character size is specified in bits:
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag = CS8; /* Select 8 data bits */
Setting Parity Checking
Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation.
No parity (8N1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag = CS8;
Even parity (7E1):
options.c_cflag = PARENB
options.c_cflag &= ~PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag = CS7;
Odd parity (7O1):
options.c_cflag = PARENB
options.c_cflag = PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag = CS7;
Space parity is setup the same as no parity (7S1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag = CS8;
Setting Hardware Flow Control
UNIX support hardware flow control using the CTS (Clear To Send) and RTS (Request To Send) signal lines. If the CNEW_RTSCTS or CRTSCTS constants are defined on your system then hardware flow control is probably supported.
Enable hardware flow control:
options.c_cflag = CNEW_RTSCTS; /* Also called CRTSCTS */
Disable hardware flow control:
options.c_cflag &= ~CNEW_RTSCTS;
Setting Software Flow Control
Software flow control is enabled using the IXON, IXOFF, and IXANY constants:
options.c_iflag = (IXON | IXOFF | IXANY);
Disable software flow control simply mask those bits:
options.c_iflag &= ~(IXON | IXOFF | IXANY);
Opening a Serial Port
Serial port is a file, the open function is used to access it.  In UNIX device files are usually not accessable by normal users. Workarounds include changing the access permissions to the file in question, running your program as the super-user (root), or making your program set-userid so that it runs as the owner of the device file
Example: Opening a serial port.
#include< stdio.h > /* Standard input/output definitions */
#include< string.h > /* String function definitions */
#include< unistd.h > /* UNIX standard function definitions */
#include< fcntl.h > /* File control definitions */
#include< errno.h > /* Error number definitions */
/* open_port()’ – Open serial port Returns the file descriptor on success or -1 on error */
int open_port(void){
int fd; /* File descriptor for the port */
fd = open(“/dev/ttyS0″, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1){
/* Could not open the port.*/
perror(“open_port: Unable to open /dev/ttyS0 – “);
}else
fcntl(fd, F_SETFL, 0);
return (fd);
}
Other systems would require the corresponding device file name, but otherwise the code is the same.
Open Options
fd = open(“/dev/ttyS0″, O_RDWR | O_NOCTTY |O_NDELAY);
O_NOCTTY flag tells UNIX that this program doesn’t want to be the “controlling terminal” for that port. If you don’t specify this then any input (such as keyboard abort signals and so forth) will affect your process. O_NDELAY flag tells UNIX that this program doesn’t care what state the DCD signal line is in – whether the other end of the port is up and running. If you do not specify this flag, your process will be put to sleep until the DCD signal line is the space voltage.
Writing Data to the Port
Writing data to the port is easy, write system call to send data it
n = write(fd,buf,4);
if (n < 0)
fputs(“write() of 4 bytes failed!n”, stderr);
The write function returns the number of bytes sent or -1 if an error occurred.
Reading Data from the Port
Reading data from a port is a little clusmy. When you operate the port in raw data mode, each read system call will return the number of characters that are actually available in the serial input buffers. If no characters are available, the call will block (wait) until characters come in, an interval timer expires, or an error occurs.
fcntl(fd, F_SETFL, FNDELAY);
The FNDELAY option causes the read function to return 0 if no characters are available on the port. To restore normal (blocking) behavior, call fcntl() without the FNDELAY option:
fcntl(fd, F_SETFL, 0);
This is also used after opening a serial port with the O_NDELAY option.
Closing a Serial Port
Close the serial port, just use the close system call:
close(fd);
Sample Working  Program
#include < stdio.h >
#include < termios.h >
#include < unistd.h >
#include < fcntl.h >
#include < string.h >
char devicename[80];
int main(){
int fd,i;
char a,buf[]=”Welcome to the world of linux”;
struct termios oldtio,newtio;
strcpy(devicename,”/dev/ttyS0″);
fd = open(“/dev/ttyS0″, O_RDWR | O_NOCTTY ); /*File descriptor to open serial port 0*/
if(fd<0){
perror(devicename);
}
tcgetattr(fd,&oldtio); /* Get the current port settings */
cfsetispeed(&newtio, B115200); /*Set the baud rate*/
newtio.c_cflag &= ~CSIZE; /*Character size mask*/
newtio.c_cflag = CS8; /*Set character size as 8-bit*/
newtio.c_cflag &= ~(PARENB | PARODD); /*Set no parity*/
newtio.c_cflag &= ~CSTOPB; /*Set stop bit as 1-bit*/
newtio.c_cflag &= ~CRTSCTS; /*Set no hardware flow control*/
newtio.c_cflag = CLOCAL; /*Ignore modem control lines*/
newtio.c_cflag = CREAD; /*Enable receiver*/
newtio.c_iflag = IGNPAR; /*Ignore parity and framing errors at input*/
newtio.c_oflag &= ~OPOST; /* Enable implementation-defined o/p processing*/
newtio.c_oflag != OFILL; /* Send fill characters for delay use time delay*/
newtio.c_lflag &= ~ECHO; /*Echo input characters*/
newtio.c_lflag &= ~ECHONL; /*Echo NL even if ECHO is not set*/
newtio.c_lflag &= ~ICANON; /*Enable canonical mode special characters*/
newtio.c_lflag &= ~ISIG; /*Generate corresponding signal for INTR,QUIT,SUSP*/
newtio.c_lflag &= ~IEXTEN; /*Enable implementation-defined input processing*/
tcflush(fd, TCIOFLUSH); /*Flush both pending i/p and unTxed o/p*/
tcsetattr(fd,TCSANOW,&newtio); /*Set the new attributes*/
for(i=0;i
write(fd,&buf[i],1); /*Write to port*/
usleep(50);
}
read(fd,&a,1);
while(a!=’q'){
write(fd,&a,1); /*Read from port and write to port*/
read(fd,&a,1);
}tcflush(fd,TCIOFLUSH); /*Flush both pending i/p and unTxed o/p*/
tcsetattr(fd,TCSANOW,&oldtio); /*Set the old attributes*/
tcdrain(fd); /*Waits unitl fd transmits to object*/
fflush(stdout); /*File flush*/
close(fd); /*Close the file descriptor*/
return 0;
}

Related posts:

  1. How to redirect the Shell Program output to file and Screen in Unix
  2. Unix Commands to lookup system information
  • Toni

    Thanks for this article.
    Somebody knows if POSIX based Operating Systems have functions to return informations about the actual state of serial port (like baud_rate)?