News » ioctl sillyness

ioctl and UART output queue size

After a bit of experimentation, and quite a bit of googling, I've come to the conclusion that hard coded delays or additional hardware are the only reliable way to drive RTS to switch the RS485 drivers on and off under Linux using GCC code.

This seems a bit nuts, but the ioctl 'TIOCOUTQ' only returns the number of bytes in the operating system serial queue, not the 16 character hardware FIFO built into most UARTS.  I can understand why though, as the exact hardware underlying a serial port these days could be just about anything, so writing a reliable abstracted interface to it would be a bit of a mess.  It's a little annoying though!

Consider the code:
// Reset the RTS pin  ENABLE 485 output.
sercmd = TIOCM_RTS;
ioctl(SerHnd, TIOCMBIC, &sercmd);
usleep(100);  // 100us settling time for RTS

// populate test string
for (i=0;i<32;i++) output[i]=i;

// send it to the port..
write(SerHnd,output,32);

do { // wait for ourput queue to be 0 characters
    ioctl(SerHnd, TIOCOUTQ, &serstat);
} while(serstat>0);

// set the RTS pin  DISABLE 485 output
sercmd = TIOCM_RTS;
ioctl(SerHnd, TIOCMBIS, &sercmd);
This invariably chops off the last 13-14 characters of the 32 byte string when sending at 19.2kbps.  If you just echo serstat, straight after the write() command you invariably get 15 or 16 as the result.  As per:
write(SerHnd,output,32);
ioctl(SerHnd, TIOCOUTQ, &serstat);
printf ("%i Bytes in queuen",serstat);
So, not only is the ioctl() call not getting the harware FIFO, the time taken to execute the write() and ioctl() calls is a little vague as well, which you would expect from a multi-tasking OS.

Two options I could see:

a) Use usleep() delays to pad out things and build suitable guard times into my protocol definition.

b) Use additional hardware in the RS485 interface to swtich the RTS signal for me.

Option a) is easier but yuck, yuck, yucky.  While I am going to have to use guard times in the protocol, I can't think of anything nastier than software delay loops for this task.  So, option b) it is, some extra hardware.

There are some clever circuits around the web for option b, using a 555 timer as a monostable to hold the driver enable bit as per: http://www.lvr.com/rs-485_circuits.htm.  Me thinks I might just go down that path, much less potential pain than the software route.

And thinking of software, in my flip-flop world of not making clear decisions, I may see if I can shoe-horn my modbus RTU code into the smaller PIC, seems silly to waste all that effort.  (yes, I know, can't make up my mind....)

Powered by Etomite CMS.