RS485 delay after transmit

Nick Gammon has a tutorial on RS485 communication. From that tutorial

I understand the (hidden) flushing, I understand the need for a delay.
The question is why the text states "two character times"? I thought that 1 character time should be sufficient.

Note:
I don't have modules to test.

His alternate method

digitalWrite (ENABLE_PIN, HIGH);  // enable sending
 sendMsg (fWrite, msg, sizeof msg);
 
 while (!(UCSR0A & (1 << UDRE0)))  // Wait for empty transmit buffer
     UCSR0A |= 1 << TXC0;  // mark transmission not complete
 while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmission to complete

  digitalWrite (ENABLE_PIN, LOW);  // disable sending  

makes sense and is independent of bit rate.

I do understand that but it is platform dependent; something I prefer to prevent. Although the program that I'm working on is intended for AVR and the alternative can be used it means that I have to find similar solutions for any other architecture.

Any idea why the "two character times" is mentioned?

Make no sense to me when you can simply do a Serial.flush()

There is a 1 byte FIFO. That means the maximum bytes that still need to be sent is 2, 1 byte in the FIFO, 1 in the shiftout register.

So is the other method, in fact on an ESP the FIFO is much bigger.

@sterretje

The scenario is as follows

  • set the TX mode
  • write a message over serial
  • wait until the serial send buffer is empty
  • set the RX mode

The problem that occurs is that although the buffer is empty, the last character might be in progress. If one sets the RX mode while this character is not send completely an error occurs.

I do know that I have to wait because the last byte is still leaving the microcontroller. The question is why Nick Gammon suggests to wait basically two character times and not one character time.

Ah, that explains it.

Could it be that it was something that Nick Gammon has determined by trial and error?

And will be dependent upon the design of the hardware serial section of any microcontroller. Some have a much deeper FIFO; a further complication being, if you send a 3 character message through an eight character fifo, for example, you then must either know to optimize the delay, or you must suffer much longer default delays to protect against varying length messages.

As the sending is asynchronous and there is a time between characters. A byte takes 8 bits + start + stop + optional parity = 11 bits.

Warming: assumptions ahead

I assume the time between the characters is also 1 bit so total goes to at least 12 bits. So time of one byte is not sufficient. As Nick is very pragmatic I assume he rounded it to the time of two bytes.

I

So is waiting for 2 bytes, Serial.flush() is not

The issue with Serial.flush() is not much different from that with a call to delay(), in that nothing else will proceed while the code waits for flush() to return(at least in an AVR); the only palpable difference is that the user's code need not deal with it by tuning the call to delay(), instead it's 'under the hood'.
That being said, the impact on performance in either case will lessen if one uses a higher baud rate; delaying a millisecond for every character outbound with long messages, as is more or less the case with 9600 baud, can make a system seem unresponsive; not so much with the same message at 115200 baud.

Except you don't need to know the baud rate and you don't need to change it if you do change the baud rate. Gammons hack is totally unnecessary.

@sterretje
Had a look in my RS485 library to see how I implemented this.

RS485 implements stream class RS485 : public Stream and it has flush() calling stream->flush() and a delay for the last byte.

void RS485::flush()
{
#ifdef ARDUINO_ARCH_STM32
  while (Serial.availableForWrite() < ( SERIAL_TX_BUFFER_SIZE - 1));
#else
  _stream->flush();
#endif
  delayMicroseconds(_microsPerByte);  //  for the last byte.
}

The STM32 did not support flush() so it is done with an active loop.

After the flush() there is a delayMicroseconds() for the last byte in the transmit register.
The user can tune this delay with void setMicrosPerByte(mpb)to squeeze every unneeded microsecond from the communication.

Why no _stream->flush(); used? · Issue #15 · RobTillaart/RS485 · GitHub discusses the flush() and shows scope images that the above flush() works.

(update) see also RS485.write doesnt work on STM32, _stream.flush() not supported on STM32 · Issue #30 · RobTillaart/RS485 · GitHub