====== 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. :)