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
#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);
}
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);
}
#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 pagina 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;
}
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.