Cette page vous affiche les différences entre la révision choisie et la version actuelle de la page.
robotics:computing:linux_asynchronous_serial_programming_how_to [2011/02/19 12:25] gdo |
— (Version actuelle) | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | ====== 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 on both Roboard and desktop PC : [[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** : | ||
- | <code>#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</code> | ||
- | |||
- | And the source file **serialCom.c** with more explanations in code comments : | ||
- | <code>#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</code> | ||
- | |||
- | To generate the library : | ||
- | <code>$ gcc -o libSerialCom.so --shared serialCom.c -I.</code> | ||
- | |||
- | ====== 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 : | ||
- | |||
- | <code>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; | ||
- | }</code> | ||
- | |||
- | To generate the binary : | ||
- | <code>$ g++ -o testSerialCom testserialcom.cpp -I../../SerialCom/ -L../../SerialCom/ -lSerialCom</code> | ||
- | |||
- | ====== Performing the test ====== | ||
- | |||
- | If you have no serial port to test your code, just create two virtual ports with **socat** in a terminal. : | ||
- | |||
- | <code>$ 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]</code> | ||
- | |||
- | In an other terminal just launch our test program : | ||
- | |||
- | <code>$ sudo LD_LIBRARY_PATH=../SerialCom/ ./testSerialCom</code> | ||
- | |||
- | 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. :) |