Outils pour utilisateurs

Outils du site


en:robotics:computing:communication_serie_asynchrone_sous_linux

Introduction

This article describes asynchrous serial programming using signal handler.

References
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 <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.

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 <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

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 :

To test just type some characters on cutecom input, you must see them appear on the test program terminal. :)

en/robotics/computing/communication_serie_asynchrone_sous_linux.txt · Dernière modification: 2012/09/20 10:52 (modification externe)