Опрос Linux на конце последовательной передачи

Я реализую RS485 на плате для разработки рук, используя последовательный порт и gpio для включения данных.

Я устанавливаю высокий уровень разрешения данных перед отправкой и хочу, чтобы он был установлен на низкий уровень после завершения передачи.

Это можно сделать просто, написав:

//fd = open("/dev/ttyO2", ...);
DataEnable.Set(true);
write(fd, data, datalen);
tcdrain(fd); //Wait until all data is sent
DataEnable.Set(false);

Я хотел перейти с блокирующего режима на неблокирующий и использовать опрос с fd. Но я не вижу какого-либо события опроса, соответствующего «завершению передачи».

Как я могу получить уведомление, когда все данные будут отправлены?

Система: linux Язык: c++ Плата: BeagleBone Black


person peku33    schedule 26.08.2014    source источник
comment
Вы действительно не хотите делать это в пользовательском пространстве. Если драйвер последовательного порта не поддерживает ioctl TIOCSRS485, улучшите этот драйвер, внедрив его. См. stackoverflow. ком/вопросы/25250731/   -  person sawdust    schedule 27.08.2014


Ответы (2)


Я не думаю, что это возможно. Вам нужно будет либо запустить tcdrain в другом потоке, и он уведомит основной поток, либо использовать тайм-аут для poll и опросить, чтобы увидеть, был ли вывод слит.

Вы можете использовать TIOCOUTQ ioctl, чтобы получить количество байтов в выходном буфере и настроить время ожидания в соответствии со скоростью передачи данных. Это должно сократить количество опросов, которые вам нужно выполнить, до одного или двух раз. Что-то типа:

 enum { writing, draining, idle } write_state;
 while(1) {
     int write_event, timeout = -1;
     ...
     if (write_state == writing) {
         poll_fds[poll_len].fd = write_fd;
         poll_fds[poll_len].event = POLLOUT;
         write_event = poll_len++
     } else if (write == draining) {
         int outq;
         ioctl(write_fd, TIOCOUTQ, &outq);
         if (outq == 0) {
             DataEnable.Set(false);
             write_state = idle;
         } else {
             // 10 bits per byte, 1000 millisecond in a second
             timeout = outq * 10 * 1000 / baud_rate; 
             if (timeout < 1) {
                 timeout = 1;
             }
         }
     }
     int r = poll(poll_fds, poll_len, timeout);
     ...
     if (write_state == writing && r > 0 && (poll_fds[write_event].revent & POLLOUT)) {
         DataEnable.Set(true); // Gets set even if already set. 
         int n = write(write_fd, write_data, write_datalen);
         write_data += n;
         write_datalen -= n;
         if (write_datalen == 0) {
             state = draining;
         }
     }
 }
person Ross Ridge    schedule 27.08.2014

Устаревшая ветка, но я работал над RS-485 с 16550-совместимым UART под Linux и нашел

  • tcdrain работает, но добавляет задержку от 10 до 20 мс. Вроде опрос
  • Значение, возвращаемое TIOCOUTQ, кажется, подсчитывает байты в буфере ОС, но НЕ байты в UART FIFO, поэтому оно может недооценивать требуемую задержку, если передача уже началась.

В настоящее время я использую CLOCK_MONOTONIC для отметки времени каждой отправки, рассчитывая, когда отправка должна быть завершена, при проверке этого времени по отношению к следующей отправке, при необходимости откладывая. Отстой, но вроде работает

person Old Bald Guy    schedule 04.05.2016