Logo de AulaDigital

Programacion Serie Como 3

CÓMO Programar el puerto serie en Linux: Programas Ejemplo Anterior Siguiente Indice

3. Programas Ejemplo

Todos los ejemplos provienen de miniterm.c. El buffer está limitado a 255 caracteres, como la longitud máxima de cadena para el proceso de entrada canónica. (<linux/limits.h> o <posix1_lim.h>).

Vea los comentarios que hay en el código para una explicación del uso de los diferentes modos de entrada. Espero que el código sea comprensible. El ejemplo de entrada canónica está mejor comentado, el resto de los ejemplos están comentados sólo donde difieren del ejemplo de entrada canónica para remarcar las diferencias.

Las descripciones no son completas, por eso le invito a experimentar con los ejemplos para obtener mejores soluciones para su aplicación.

�No olvide dar los permisos apropiados a los puertos serie:

chmod a+rw /dev/ttyS1 

3.1 Proceso de Entrada Canónico

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* la tasa de baudios esta definida en  <asm/termbits.h>, que esta 
   incluida <termios.h> */

#define BAUDRATE B38400

/* cambie esta definicion por el puerto correcto  */
#define MODEMDEVICE "/dev/ttyS1"

#define _POSIX_SOURCE 1 /* fuentes cumple POSIX  */

#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

main()
{
   int fd,c, res;
   struct termios oldtio,newtio;
   char buf[255];

/* 
      Abre el dispositivo modem para lectura y escritura y no como controlador
      tty porque no queremos que nos mate si el ruido de la linea envia 
      un CTRL-C.
*/

   fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
   if (fd <0) {  perror(MODEMDEVICE); exit(-1);  }

   tcgetattr(fd,&oldtio); /* almacenamos la configuracion actual del puerto */

   bzero(newtio, sizeof(newtio)); /* limpiamos struct para recibir los
                                        nuevos parametros del puerto */

/*
      BAUDRATE: Fija la tasa bps. Podria tambien usar cfsetispeed y cfsetospeed.
      CRTSCTS : control de flujo de salida por hardware (usado solo si el cable 
      tiene todas las lineas necesarias Vea sect. 7 de Serial-HOWTO)
      CS8     : 8n1 (8bit,no paridad,1 bit de parada)
      CLOCAL  : conexion local, sin control de modem
      CREAD   : activa recepcion de caracteres
*/

   newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

/*
      IGNPAR  : ignora los bytes con error de paridad
      ICRNL   : mapea CR a NL (en otro caso una entrada CR del otro ordenador 
      no terminaria la entrada) en otro caso hace un dispositivo en bruto 
      (sin otro proceso de entrada)
*/

   newtio.c_iflag = IGNPAR | ICRNL;

/*
      Salida en bruto.
*/
   newtio.c_oflag = 0;

/*
      ICANON  : activa entrada canonica
      desactiva todas las funcionalidades del eco, y no envia segnales al
      programa
      llamador
*/

   newtio.c_lflag = ICANON;

/* 
      inicializa todos los caracteres de control
      los valores por defecto se pueden encontrar en /usr/include/termios.h, 
      y vienen dadas en los comentarios, pero no los necesitamos aqui
*/

   newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */
   newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
   newtio.c_cc[VERASE]   = 0;     /* del */
   newtio.c_cc[VKILL]    = 0;     /* @ */
   newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
   newtio.c_cc[VTIME]    = 0;     /* temporizador entre caracter, no usado */
   newtio.c_cc[VMIN]     = 1;     /* bloqu.lectura hasta llegada de caracter. 1 */
   newtio.c_cc[VSWTC]    = 0;     /* '\0' */
   newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
   newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
   newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
   newtio.c_cc[VEOL]     = 0;     /* '\0' */
   newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
   newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
   newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
   newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
   newtio.c_cc[VEOL2]    = 0;     /* '\0' */

/* 
      ahora limpiamos la linea del modem y activamos la configuracion del
      puerto
*/

   tcflush(fd, TCIFLUSH);
   tcsetattr(fd,TCSANOW,&newtio);

/*
      configuracion del terminal realizada, ahora manejamos las entradas.
      En este ejemplo, al introducir una  'z' al inicio de linea terminara el 
      programa.  
*/

   while (STOP==FALSE) {     /* bucle hasta condicion de terminar */

/* 
   bloque de ejecucion de programa hasta que llega un caracter de fin de
   linea, incluso si llegan mas de 255 caracteres.
   Si el numero de caracteres leidos es menor que el numero de caracteres 
   disponibles, las siguientes lecturas devolveran los caracteres restantes.
   'res' tomara el valor del numero actual de caracteres leidos.
*/

                          res = read(fd,buf,255);
                          buf[res]=0;             /* envio de fin de cadena, a fin de poder usar printf */
                          printf(":%s:%d\n", buf, res);
                          if (buf[0]=='z') STOP=TRUE;
                       }

/* restaura la anterior configuracion del puerto  */

   tcsetattr(fd,TCSANOW,&oldtio);
}

3.2 Proceso de Entrada NO Canónico

En el modo de proceso de entrada no canónico, la entrada no está ensamblada en líneas y el procesamiento de la entrada (erase, kill, delete, etc.) no ocurre. Dos parámetros controlan el comportamiento de este modo: c_cc[VTIME] fija el temporizador de carácter, y fija el número mínimo de caracteres a recibir antes de satisfacer la lectura.

Si MIN > 0 y TIME = 0, MIN fija el número de caracteres a recibir antes de que la lectura esté realizada. Como TIME es cero, el temporizador no se usa.

Si MIN = 0 y TIME > 0, TIME indica un tiempo de espera. La lectura se realizará si es leído un sólo carácter, o si se excede TIME (t =TIME *0.1 s). Si TIME se excede, no se devuelve ningún carácter.

Si MIN > 0 y TIME > 0, TIME indica un temporizador entre caracteres. La lectura se realizará si se reciben MIN caracteres o el tiempo entre dos caracteres excede TIME. El temporizador se reinicia cada vez que se recibe un carácter y sólo se hace activo una vez que se ha recibido el primer carácter.

Si MIN = 0 y TIME = 0, la lectura se realizará inmediatamente. Devolverá el número de caracteres disponibles en el momento, o el número de caracteres solicitados. De acuerdo con Antonino (ver contribuciones), podría poner un fcntl(fd, F_SETFL, FNDELAY); antes de leer para obtener el mismo resultado.

Modificando newtio.c_cc[VTIME] y newtio.c_cc[VMIN] se pueden comprobar todos los modos descritos arriba.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* fuentes cumple POSIX */
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

main()
{
   int fd,c, res;
   struct termios oldtio,newtio;
   char buf[255];

   fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
   if (fd <0) {  perror(MODEMDEVICE); exit(-1);  }

   tcgetattr(fd,&oldtio); /* salva configuracion actual del puerto  */

   bzero(newtio, sizeof(newtio));
   newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
   newtio.c_iflag = IGNPAR;
   newtio.c_oflag = 0;

/* pone el modo entrada (no-canonico, sin eco,...) */

   newtio.c_lflag = 0;

   newtio.c_cc[VTIME]    = 0;   /* temporizador entre caracter, no usado */
   newtio.c_cc[VMIN]     = 5;   /* bloquea lectura hasta recibir 5 chars  */

   tcflush(fd, TCIFLUSH);
   tcsetattr(fd,TCSANOW,&newtio);


   while (STOP==FALSE) {                            /* bucle para entrada */
                          res = read(fd,buf,255);   /* devuelve tras introducir 5 */
                          buf[res]=0;               /* asi podemos printf... */
                          printf(":%s:%d\n", buf, res);
                          if (buf[0]=='z') STOP=TRUE;
                       }
   tcsetattr(fd,TCSANOW,&oldtio);
}

3.3 Entrada Asíncrona

#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>

#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* fuentes cumple POSIX  */
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

void signal_handler_IO (int status);   /* definicion del manejador de segnal */
int wait_flag=TRUE;                    /* TRUE mientras no segnal recibida */

main()
{
   int fd,c, res;
   struct termios oldtio,newtio;
   struct sigaction saio;           /* definicion de accion de segnal  */
   char buf[255];

/* abre el dispositivo en modo no bloqueo (read volvera inmediatamente) */

      fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
   if (fd <0) {  perror(MODEMDEVICE); exit(-1);  }

/* instala el manejador de segnal antes de hacer asincrono el dispositivo */

   saio.sa_handler = signal_handler_IO;
   saio.sa_mask = 0;
   saio.sa_flags = 0;
   saio.sa_restorer = NULL;
   sigaction(SIGIO,&saio,NULL);

/* permite al proceso recibir SIGIO */

      fcntl(fd, F_SETOWN, getpid());

/* Hace el descriptor de archivo asincrono (la Página del manual dice solo
      O_APPEND y O_NONBLOCK, funcionara con  F_SETFL...) */

   fcntl(fd, F_SETFL, FASYNC);
   tcgetattr(fd,&oldtio); /* salvamos conf. actual del puerto */

/* 
      fija la nueva configuracion del puerto para procesos de entrada canonica
*/

   newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
   newtio.c_iflag = IGNPAR | ICRNL;
   newtio.c_oflag = 0;
   newtio.c_lflag = ICANON;
   newtio.c_cc[VMIN]=1;
   newtio.c_cc[VTIME]=0;
   tcflush(fd, TCIFLUSH);
   tcsetattr(fd,TCSANOW,&newtio);

/* bucle de espera para entrada. Normalmente se haria algo util aqui  */

   while (STOP==FALSE) {
                          printf(".\n");usleep(100000);

/* 
tras recibir SIGIO, wait_flag = FALSE, la entrada esta disponible y puede ser leida
 */

                if (wait_flag==FALSE) {
                                   res = read(fd,buf,255);
                                   buf[res]=0;
                                   printf(":%s:%d\n", buf, res);
                                   if (res==1) STOP=TRUE; /* para el bucle si solo entra un CR */
                                   wait_flag = TRUE;      /* espera una nueva entrada */
                                }
                       }
/* restaura la configuracion original del puerto  */
      tcsetattr(fd,TCSANOW,&oldtio);
}

 
/***************************************************************************
* manipulacion de segnales. pone wait_flag a FALSE, para indicar al bucle  *
* anterior que los caracteres han sido recibidos                           *
***************************************************************************/

void signal_handler_IO (int status)
{
   printf("recibida segnal SIGIO.\n");
   wait_flag = FALSE;
}

3.4 Espera de Entradas de Origen Múltiple.

Esta sección está al mínimo. Sólo intenta ser un indicación, y por tanto el ejemplo de código es pequeño. Esto no sólo funcionará con puertos serie, sino que también lo hará con cualquier conjunto de descriptores de archivo.

La llamada select y las macros asociadas usan un fd_set. Esto es una tabla de bits, que tiene una entrada de bit para cada número de descriptor de archivo válido. select aceptará un fd_set con los bits fijados para los descriptores de archivos relevantes y devuelve un fd_set, en el cual los bits para el descriptor del archivo están fijados donde ocurre una entrada, salida o excepción. Todas la manipulaciones de fd_set se llevan a cabo mediante las macros proporcionadas. Ver también la página del manual select(2).

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

main()
{
   int    fd1, fd2;  /* origenes de entrada  1 y 2 */
   fd_set readfs;    /* descriptor de archivo */
   int    maxfd;     /* mixmum file desciptor used */
   int    loop=1;    /* bucle mientras TRUE */

/* 
   open_input_source abre un dispositivo, fija el puerto correctamente
   y devuelve un descriptor de archivo
*/

   fd1 = open_input_source("/dev/ttyS1");   /* COM2 */
   if (fd1<0) exit(0);
   fd2 = open_input_source("/dev/ttyS2");   /* COM3 */
   if (fd2<0) exit(0);
   maxfd = MAX (fd1, fd2)+1;  /* entrada maxima de bits (fd) a probar */

/* bucle para entrada */
      while (loop) {
                      FD_SET(fd1, &readfs);  /* comprobacion origen 1  */
                      FD_SET(fd2, &readfs);  /* comprobacion origen 2 */

/* bloqueo hasta que la entrada esta disponible  */
                         select(maxfd, &readfs, NULL, NULL, NULL);
                      if (FD_ISSET(fd1))         /* entrada de origen 1 esta disponible */
                         handle_input_from_source1();
                      if (FD_ISSET(fd2))         /* entrada de origen 2 esta disponible */
                         handle_input_from_source2();
                   }

}   

El ejemplo dado bloquea indefinidamente hasta que una entrada de una de las fuentes está disponible. Si necesita un temporizador para la entrada, sólo sustituya la llamada select por:

int res;
struct timeval Timeout;

/* fija el valor del temporizador en el bucle de entrada  */
Timeout.tv_usec = 0;  /* milisegundos */
Timeout.tv_sec  = 1;  /* segundos */
res = select(maxfd, &readfs, NULL, NULL, &Timeout);
if (res==0)
/* numero de descriptores de archivo con input = 0, temporizador sobrepasado */ 

Este ejemplo concluye el tiempo de espera tras un segundo. Si este tiempo transcurre, select devolverá 0, pero tenga cuidado porque Timeout se decrementa por el tiempo actualmente esperado para la entrada por select. Si el valor de retardo es cero, select volverá inmediatamente.


Anterior Siguiente Indice