====== Introduction ====== This article describes asynchrous serial programming using signal handler. == References == * Serial Programming HOWTO : [[http://tldp.org/HOWTO/Serial-Programming-HOWTO/]] == Hardware used : == * A desktop PC. * An internet connection. == Softwares : == * Ubuntu 10.10 installed : [[http://www.ubuntu.com/]] * Package **build-essential** and **socat** already installed on your Ubuntu operating system. ====== Asynchronous serial I/O library ====== 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 : * Data are available on the serial port * A signal SIGIO is send to the application waiting for it * This application stops its actual work * And it calls the signal handler function * At the end of the handler, application go back to its previous 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 /* 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 #include #include #include #include #include #include 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. ====== Test program ====== 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 #include /* 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 ====== Performing the test ====== 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 : {{:robotics:computing:linux_asynchronous_serial_programming:cutecom.png|}} To test just type some characters on **cutecom** input, you must see them appear on the test program terminal. :)