recibir - ¿Cómo puedo implementar el tiempo de espera para leer() al leer desde un puerto serie(C/C++)
probar puerto serial linux (5)
Estoy leyendo bytes desde un puerto serie en C ++ utilizando un descriptor de archivo y la función posix / unix read (). En este ejemplo, estoy leyendo 1 byte desde el puerto serie (las configuraciones de velocidad en baudios y similares se omiten para mayor claridad):
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
char buf[1];
int bytesRead = read(fd, buf, 1);
close(fd);
return 0;
}
Si el dispositivo conectado a / dev / ttyS0 no envía ninguna información, el programa se bloqueará. ¿Cómo puedo establecer un tiempo de espera?
He intentado establecer un tiempo de espera como este:
struct termios options;
tcgetattr(fd, &options);
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);
Pensé que se suponía que debía dar un tiempo de espera de 1 segundo, pero no importa. Creo que he entendido mal a VMIN y VTIME. ¿Para qué se utilizan VMIN y VTIME?
Luego busqué en la web y encontré a alguien hablando de la función select (). ¿Es esa la solución y, de ser así, cómo se podría aplicar eso al programa anterior para hacer un tiempo de espera de 1 segundo?
Cualquier ayuda es apreciada. Gracias por adelantado :-)
Hay varios enfoques posibles. Si el programa finalmente sincronizará más de una operación de E / S, select()
es la opción más clara.
Sin embargo, si la única entrada es de este i / o, entonces la selección de E / S sin bloqueo y sincronización es un método sencillo. Lo he expandido de un solo carácter de E / S a caracteres múltiples para que sea un ejemplo más general completo:
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/time.h>
int main(void)
{
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); // sometimes "O_NONBLOCK"
char buf[10];
int done = 0, inbuf = 0;
struct timeval start, now;
gettimeofday (&start, NULL);
while (!done)
{
int bytesRead = read(fd, &buf[inbuf], sizeof buf - inbuf);
if (bytesRead < 0)
{
error_processing_here();
continue;
}
if (bytesRead == 0) // no data read to read
{
gettimeofday (&now, NULL);
if ((now.tv.sec - start.tv_sec) * 1000000 +
now.tv.usec - start.tv_usec > timeout_value_in_microsecs)
{
done = 2; // timeout
continue;
}
sleep(1); // not timed out yet, sleep a second
continue;
}
inbuf += bytesRead;
if (we have read all we want)
done = 1;
}
if (done == 2)
timeout_condition_handling();
close(fd);
return 0;
}
Sí, use select(2)
. Pase un conjunto de descriptores de archivo que contenga solo su fd en el conjunto de lectura y los conjuntos de escritura / excepción vacíos, y pase un tiempo de espera apropiado. Por ejemplo:
int fd = open(...);
// Initialize file descriptor sets
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);
// Set timeout to 1.0 seconds
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
// Wait for input to become ready or until the time out; the first parameter is
// 1 more than the largest file descriptor in any of the sets
if (select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1)
{
// fd is ready for reading
}
else
{
// timeout or error
}
select () es la forma en que resolvería este problema.
Hay varias páginas en Internet que brindarán información sobre cómo usar select (), como http://www.unixguide.net/unix/programming/2.1.1.shtml
Puede intentar la señal de captura para detener la operación de lectura. use la alarma (1) antes de leer, y si no se devolvió la función de lectura, la alarma enviará la señal SIGALRM, luego puede crear la función de procesamiento de señal para capturar esta señal, como esta:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
static jmp_buf env_alarm;
static void sig_alarm(int signo)
{
longjmp(env_alarm, 1);
}
int main(void)
{
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
char buf[1];
if (signal(SIGALRM, sig_alarm) == SIG_ERR)
{
exit(0);
}
if (setjmp(env_alarm) != 0)
{
close(fd);
printf("Timeout Or Error/n");
exit(0);
}
alarm(1);
int bytesRead = read(fd, buf, 1);
alarm(0);
close(fd);
return 0;
}
Pero use select o poll o epoll será mejor si su programa es grande.
¿Para qué se utilizan VMIN y VTIME?
Si MIN> 0 y TIEMPO = 0, MIN establece la cantidad de caracteres que se recibirán antes de que se satisfaga la lectura. Como TIME es cero, el temporizador no se usa.
Si MIN = 0 y TIME> 0, TIME sirve como un valor de tiempo de espera. La lectura se cumplirá si se lee un solo carácter o si se excede el tiempo (t = TIME * 0.1 s). Si se excede el TIEMPO, no se devolverá ningún carácter.
Si MIN> 0 y TIME> 0, TIME sirve como un temporizador entre caracteres. La lectura se cumplirá si se reciben MIN caracteres, o si el tiempo entre dos caracteres excede TIME. El temporizador se reinicia cada vez que se recibe un carácter y solo se activa después de que se haya recibido el primer carácter.
Si MIN = 0 y TIME = 0, la lectura se cumplirá inmediatamente. Se devolverá el número de caracteres disponibles actualmente o el número de caracteres solicitados. De acuerdo con Antonino (ver contribuciones), podrías emitir un fcntl (fd, F_SETFL, FNDELAY); antes de leer para obtener el mismo resultado.
Fuente: http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html