2012-05-09 2 views
13

Я читаю байты из последовательного порта на C++, используя файловый дескриптор и функцию read() posix/unix. В этом примере я читаю 1 байт из последовательного порта (настройки скорости передачи и похож опущены для ясности):Как я могу реализовать таймаут для чтения() при чтении из последовательного порта (C/C++)

#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; 
} 

Если устройство подключено к/DEV/ttyS0 не отправляет какую-либо информацию, программа будет похмелья. Как установить тайм-аут?

Я попытался установить тайм-аут, как это:

struct termios options; 
tcgetattr(fd, &options); 
options.c_cc[VMIN] = 0; 
options.c_cc[VTIME] = 10; 
tcsetattr(fd, TCSANOW, &options); 

Я думал, что это должно было дать 1 второй тайм-аут, но это не имеет никакого значения. Я думаю, что неправильно понял VMIN и VTIME. Для чего используются VMIN и VTIME?

Затем я искал в Интернете и обнаружил, что кто-то говорит о функции select(). Это решение, и если да, то как применить это к программе выше, чтобы сделать 1-секундный тайм-аут?

Любая помощь приветствуется. Заранее спасибо :-)

+0

Использование 'tcsetattr()' 'из VTIME' не просто; он требует других настроек режима, которые некоторые последовательные драйверы не поддерживают. См. Мой ответ для общего решения. – wallyk

+0

Это лучшее объяснение, которое я нашел в Интернете для VMIN и VTIME [http://unixwiz.net/techtips/termios-vmin-vtime.html](http://unixwiz.net/techtips/termios- Vmin-vtime.html). Согласно статье, когда бит ICANON выключен, он позволяет «сырой режим» изменять интерпретацию VMIN и VTIME. Установка бита ICANON заставит код работать должным образом. – arpl

ответ

16

Да, используйте select(2). Перейдите в набор дескрипторов файлов, содержащий только ваш fd в наборе чтения и пустых наборах записи/исключения, и передайте соответствующий тайм-аут. Например:

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 
} 
+0

Большое спасибо. Кажется, это работает так, как я этого хотел. У меня еще три вопроса: могу ли я использовать select для создания тайм-аута при открытии порта? Могу ли я использовать один и тот же код для таймаута в операции записи, установив FD_SET (fd, & write_fds)? И последнее: для чего используется except_fds? – pvh1987

+2

poll() обычно лучше, чем select(). –

+0

Отлично работает! Если я запустил это в цикле, мне нужно FD_ZERO и FD_SET на каждой итерации, или один раз перед циклом? – Andy

0

Есть несколько возможных подходов. Если программа в конечном итоге будет синхронизироваться с более чем одной операцией ввода-вывода, select() - это четкий выбор.

Однако, если единственным входом является этот ввод-вывод, то выбор неблокирующего ввода-вывода и синхронизации является простым методом. Я расширил его из одного символа ввода/вывода для нескольких символов, чтобы сделать его более общем полный пример:

#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; 
} 
+0

Это намного больше кода, чем при использовании select(), и может подождать до секунды после того, как данные будут доступны до того, как они будут прочитаны. –

+0

@ MartinC.Martin: Это может выглядеть так, потому что мой код является полным рабочим примером, включая чтение и проверку ошибок, тогда как в примере выбора показан только фрагмент того, что потребуется. – wallyk

+0

В течение 1 секунды сна может быть поспешность (особенно при высоких скоростях бод), поэтому неплохо использовать сон. Более короткие спячки могут также тратить время процессора, что имеет большее значение в устройствах с низким энергопотреблением. Этот код можно было бы улучшить, взяв непроявленный apporach, используя 'select'. – Isaac

1

Вы можете попытаться захватить сигнал, чтобы остановить операцию чтения. использовать сигнал тревоги (1) перед читать, а если прочитать функция не возвращается, сигнал тревоги пошлет сигнал SIGALRM, то вы можете создать функцию обработки сигнала, чтобы захватить этот сигнал, как это:

#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; 
} 

Но использовать выберите или опрашивать или epoll будет лучше, если ваша программа большая.

+0

Есть интересный случай о выборе, он проверяет, что fd может писать, но он будет блокироваться при записи, поэтому я думаю, что метод select не может нормально работать в моем случае, но ваш метод может работать нормально. это хороший метод. – kangear

1

Что такое VMIN и VTIME?

Если MIN> 0 и TIME = 0, MIN устанавливает количество символов для приема до того, как прочитано будет выполнено. Поскольку TIME равно нулю, таймер не используется.

Если MIN = 0 и TIME> 0, TIME служит как значение таймаута. Чтение будет выполнено , если один символ будет считан или TIME будет превышен (t = TIME * 0,1 с). Если TIME превышено, символ не будет возвращен.

Если MIN> 0 и TIME> 0, TIME служит в качестве межсимвольного таймера.Чтение будет выполнено, если получены символы MIN, или время между двумя символами превышает TIME. Таймер перезапускается каждый раз, когда символ получен и становится активным только после того, как был получен символ .

Если MIN = 0 и TIME = 0, чтение будет выполнено немедленно. количество доступных в настоящее время символов или количество запрошенных символов . Согласно Антонино (см. Вклады), вы можете выпустить fcntl (fd, F_SETFL, FNDELAY); перед чтением, чтобы получить тот же результат.

Источник: http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

 Смежные вопросы

  • Нет связанных вопросов^_^