Short version of the question: What is the "best" way to control a transmit enable GPIO in TM4C software when doing RS485 communication?
Of course, "best" is in quotation marks because that is very subjective, so here is the...
Detailed version of the question:
I'm doing RS485 communication, which is half-duplex. Three pins of my TM4C are connected to a RS485 transceiver. Those three pins are PD5/U2TX, PD4/U2RX, and PD6 used as a GPIO to control the Transmit Enable of the RS485 transceiver. When PD6 is high, the RS485 transceiver is in "transmit" mode. When PD6 is low, the RS485 transceiver is in "receive" mode. (Since RS485 is multi-drop, we will avoid bus contention with a master/slave protocol and addressing, in which the master may transmit a query to any slave at any time, and only the queried slave may respond.)
Software must do the following:
1. Just prior to transmitting, it must set the Transmit Enable GPIO high.
2. Immediately after the last stop bit is sent, it must set the Transmit Enable GPIO low.
Sounds simple enough, right?
Our application is "bare metal" using TivaWare with no RTOS. That being the case, it is structured as a polled superloop with various interrupts servicing time-sensitive events.
These options have occurred to me:
Option A: The naive option is to service transmitting entirely in main context. When there is data to transmit, we set the GPIO high and stuff as many characters as we can into the UART transmit buffer. When there is no more data to transmit, we check the EOT bit and if ready, we set the GPIO back to low. No one part of the software is allowed to "lock up" the CPU in a while loop, so with this method we cannot control exactly how much time elapses between end of transmit and setting the GPIO low. That is, if no more data to transmit but end of transmit has not yet occurred, an unknown amount of time will elapse before we get to check again. This may clip incoming data if another device starts responding before we go back into receive mode. Not good.
Option B: The slightly less naive option is to use the UART transmit interrupt and set the EOT bit of UARTCTL to 1. This means the UART Transmit Interrupt TXRIS bit will be triggered when the last stop bit has left the serializer. Main context will set the GPIO high and place the first character of the message into the UART transmit buffer. Thereafter each time a character has finished transmitting, the Transmit Interrupt is triggered. In the interrupt, we check if there is another character to send; if so we send it; if not we set the GPIO back to low. This has the advantage that time from EOT to setting the GPIO low is much shorter and much more predictable. It has the disadvantage that an interrupt must be serviced for each and every character we transmit.
Option C: Similar to option B, but using the transmit FIFO. Communication will be somewhat "bursty" but we will service fewer interrupts. Main context will stuff as much data as it can into the transmit FIFO. With EOT bit of UARTCTL set to 1, we will receive the transmit interrupt only after the entire buffer was transmitted and the transmitter will be idle. If we have additional data, we will stuff it into the FIFO. If not, we will turn off the GPIO. When I said communication will be bursty, I meant that every 16 (or 17?) bytes, there will be a somewhat longer delay between bytes. This may be perfectly acceptable. I'm not sure.
Option D: Some as yet unknown (to me) solution in software that accomplishes everything in Option C, but without the "bursty" thing. I thought about implementing a FSM that will switch between EOT being 1 and 0 depending on whether we have more data to transmit; but this will come with numerous gotchas and edge cases that will make it fail in some very hard-to-diagnose ways. Also this sentence in the datasheet (under UARTCTL description) indicates this isn't likely to work anyway: "If software requires a configuration change in the module, the UARTEN bit must be cleared before the configuration changes are written." So, that idea is out. Perhaps DMA is an option, but in someone else's question about that, cb1 wrote "Danger Will Robinson."
Any ideas or suggestions? Am I overlooking something?