0

我正在使用串行端口和 gpio 在 arm 开发板上实现 RS485 以进行数据启用。

我在发送前将数据启用设置为高电平,我希望在传输完成后将其设置为低电平。

它可以通过编写简单地完成:

//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

4

2 回答 2

1

我不认为这是可能的。您要么必须tcdrain在另一个线程中运行并让它通知主线程,要么使用 timeout onpoll并轮询以查看输出是否已耗尽。

您可以使用TIOCOUTQioctl 获取输出缓冲区中的字节数并根据波特率调整超时。这应该会将您需要进行的轮询数量减少到一两次。就像是:

 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;
         }
     }
 }
于 2014-08-27T05:47:42.367 回答
0

过时的线程,但我一直在 Linux 下使用与 16550 兼容的 UART 研究 RS-485 并找到

  • tcdrain 有效 - 但它增加了 10 到 20 毫秒的延迟。好像被抽中了
  • TIOCOUTQ 返回的值似乎计算 OS 缓冲区中的字节数,而不是 UART FIFO 中的字节数,因此如果传输已经开始,它可能会低估所需的延迟。

我目前正在使用 CLOCK_MONOTONIC 为每次发送添加时间戳,计算发送应该何时完成,在检查下一次发送的时间时,如有必要延迟。糟透了,但似乎有效

于 2016-05-04T17:45:24.077 回答