This article describes asynchrous serial programming using signal handler.
The following code is very simple. There is just one function to initialize the serial communication.
Only serial port device, baudrate and if there is a parity checking can be set.
Everything else is hard coded, supposing we use 8 bits of data, 1 stop bit and a prefix on parity error.
But the most important thing is the definition of a signal handler function type. All signal handlers return nothing and take one integer in parameter. This integer is the signal which throws the interruption. Here is a light description of how signals work :
This signal handler is given as a parameter of the serialConfiguration function. Then you can develop your own handler and just pass it to serialConfiguration.
Here is the header file serialCom.h :
#ifndef _SERIALCOM_H_ #define _SERIALCOM_H_ #ifdef __cplusplus extern "C" { #endif #include <termios.h> /* Controls : CS8 = 8 bits * CREAD = Enable reading */ #define SERIAL_CONTROL (CS8 | CREAD) #define NO_PARITY_CHECK 0 #define PARITY_CHECK 1 /* Input : PARMRK = If IGNPAR is not set, prefix a character with a parity error or framing error with \377 \0 */ #define SERIAL_INPUT PARMRK /* Handler type definition */ typedef void (*serial_handler)(int status); /* Serial port initialization function */ int serialConfiguration(serial_handler, const char* device, tcflag_t baudrate, char parity); #ifdef __cplusplus } #endif #endif
And the source file serialCom.c with more explanations in code comments :
#ifdef __cplusplus extern "C" { #endif #include <fcntl.h> #include <sys/signal.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <termios.h> #include <serialCom.h> int serialConfiguration(serial_handler handler, const char *device, tcflag_t baudrate, char parity) { /* Definition of new serial port configuration */ struct termios newtio; /* Definition of signal action */ struct sigaction saio; /* Open the device * O_RDWR : open in read/write mode * O_NONBLOCK : open in not blocked mode. Read will return immediatly */ int fdesc = open(device, O_RDWR | O_NONBLOCK); if (fdesc < 0) { perror(device); return fdesc; } /* Install the signal handler before making the device asynchronous */ saio.sa_handler = handler; saio.sa_flags = 0; saio.sa_restorer = NULL; sigaction(SIGIO, &saio, NULL); /* Allow the process to receive SIGIO */ fcntl(fdesc, F_SETOWN, getpid()); /* Make the file descriptor asynchronous. * It is not possible to enable SIGIO receiving by specifying * O_ASYNC when calling open (see open man page) */ fcntl(fdesc, F_SETFL, O_ASYNC); /* Set new port settings */ newtio.c_cflag = baudrate | SERIAL_CONTROL; /* Input settings */ if (parity == NO_PARITY_CHECK) { newtio.c_iflag = IGNPAR | SERIAL_INPUT; } else { newtio.c_iflag = SERIAL_INPUT; } tcflush(fdesc, TCIFLUSH); tcflush(fdesc, TCOFLUSH); tcsetattr(fdesc, TCSANOW, &newtio); return fdesc; } #ifdef __cplusplus } #endif
To generate the library :
$ gcc -o libSerialCom.so --shared serialCom.c -I.
Here is a little test program testSerialCom.cpp which just print the byte it received, reading bytes one by one. We use C++ there just to show the lib is usable with C/C++ project. Explanations are in code comments :
using namespace std; #include <iostream> #include <serialCom.h> /* Flag indicating datas are ready to be read */ static bool flag = false; /* SIGIO handler */ void my_handler(int status) { /* Data ready to be read */ flag = true; } /* MAIN */ int main(int argc, char **argv) { /* Initialize the serial communication */ int fdesc = serialConfiguration(my_handler, "/dev/ttyS0", B115200, NO_PARITY_CHECK); /* Data buffer */ char buf; /* Reading result */ int res = 0; /* Initialize data flag */ flag = false; while (buf != '\t') { /* If there is a data to be read */ if (flag == true) { /* Read one byte */ do { res = read(fdesc, &buf, 1); cout << buf << endl; } while (res > 0); flag = false; } } return 0; }
To generate the binary :
$ g++ -o testSerialCom testserialcom.cpp -I../../SerialCom/ -L../../SerialCom/ -lSerialCom
If you have no serial port to test your code, just create two virtual ports with socat in a terminal. :
$ socat -d -d PTY: PTY: 2011/02/17 23:58:36 socat[12866] N PTY is /dev/pts/3 2011/02/17 23:58:36 socat[12866] N PTY is /dev/pts/4 2011/02/17 23:58:36 socat[12866] N starting data transfer loop with FDs [3,3] and [5,5]
Before launching test program, do not forget to change the port asked in the main function of the testSerialCom.c. And rebuild your program. Then in an other terminal just launch the test program :
$ sudo LD_LIBRARY_PATH=../SerialCom/ ./testSerialCom
And finally run cutecom with following parameters :
To test just type some characters on cutecom input, you must see them appear on the test program terminal. :)