0

I'm implementing RS485 on arm developement board using serial port and gpio for data enable.

I'm setting data enable to high before sending and I want it to be set low after transmission is complete.

It can be simply done by writing:

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

I wanted to change from blocking-mode to non-blocking and use poll with fd. But I dont see any poll event corresponding to 'transmission complete'.

How can I get notified when all data has been sent?

System: linux Language: c++ Board: BeagleBone Black

peku33
  • 3,628
  • 3
  • 26
  • 44
  • You really don't want to do that in userspace. If the serial port driver doesn't support the **TIOCSRS485** ioctl, then improve that driver by implementing it. See http://stackoverflow.com/questions/25250731/automatically-changing-rts-for-rs-485-communication/25253003#25253003 – sawdust Aug 27 '14 at 18:22

2 Answers2

1

I don't think it's possible. You'll either have to run tcdrain in another thread and have it notify the the main thread, or use timeout on poll and poll to see if the output has been drained.

You can use the TIOCOUTQ ioctl to get the number of bytes in the output buffer and tune the timeout according to baud rate. That should reduce the amount of polling you need to do to just once or twice. Something like:

 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;
         }
     }
 }
Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
0

Stale thread, but I have been working on RS-485 with a 16550-compatible UART under Linux and find

  • tcdrain works - but it adds a delay of 10 to 20 msec. Seems to be polled
  • The value returned by TIOCOUTQ seems to count bytes in the OS buffer, but NOT bytes in the UART FIFO, so it may underestimate the delay required if transmission has already started.

I am currently using CLOCK_MONOTONIC to timestamp each send, calculating when the send should be complete, when checking that time against the next send, delaying if necessary. Sucks, but seems to work

Old Bald Guy
  • 163
  • 1
  • 5