This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C129ENCPDT: "Best" way to control Transmit Enable line of a RS485 transceiver?

Part Number: TM4C129ENCPDT

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?

  • It may prove useful to note that,  "certain, & few" of the MCU's UART Ports - provide "Modem Control signals" - and that  signal is likely to serve well as, your "DE" signal.      (that signal - or its inversion.)

    As to the "flow & logical requirements" - presented via your "Options A-D" - might (other) forms of serial protocol provide an acceptable model?      I'm thinking of  "CAN"  of course - and the belief that "coat-tailing upon an existing (proven) method" may trump, "creation of a brand new (untested) one."

  • cb1_mobile said:

    It may prove useful to note that,  "certain, & few" of the MCU's UART Ports - provide "Modem Control signals" - and that  signal is likely to serve well as, your "DE" signal.      (that signal - or its inversion.)

    As to the "flow & logical requirements" - presented via your "Options A-D" - might (other) forms of serial protocol provide an acceptable model?      I'm thinking of  "CAN"  of course - and the belief that "coat-tailing upon an existing (proven) method" may trump, "creation of a brand new (untested) one."

    Hi @cb1_mobile, thank you for your input.

    Regarding your first point, I noticed that the UART we're using (UART2 on the TM4C129ENCPDT) has optional CTS and RTS pins. And it just so happens that our RS485_TXEN signal (the net name our designers gave to our GPIO that is connected to the RS485 transceiver's DE input) is on PD6, which just happens to be this UART's RTS pin. In another thread about RS485 (where, as luck would have it, you happened to be involved in the discussion) someone suggested using the RTS pin for exactly that purpose. However, as I looked further into it, I found yet another thread where someone tried unsuccessfully to use the RTS pin for that purpose, and it was explained that the RTS pin is driven low when the UART receiver is empty, not when transmitting. So that does not look promising.

    By the way, the search engine is just atrocious and for the life of me I cannot seem to locate any of those threads again.

    If the CTS pin is needed (jumpered to the RTS pin?) that is not possible because it is used for another purpose on this board.

    Regarding your questions and suggestion to use CAN. This RS485 interface is for compatibility with existing equipment.

    For now I've implemented Option C. Or Option 3. Whichever it was called. We're using the FIFOs, the Tx Interrupt is configured to fire on EOT, and there is a very slight delay after every 17th byte. (17 because the 1st byte pushed into the FIFO is transferred immediately to the shift register; subsequently 16 additional bytes are needed to fill the FIFO to the limit, and the Tx interrupt occurs after the 17th byte and its stop bit is completely shifted out.)

    I am glad to report that the EOT interrupt does indeed occur (immediately) after the stop bit is completely shifted out. Some MCUs give that interrupt too soon, etc.

  • Since all of the devices on the RS485 multi-drop line will understand how master control is passed, the "bursts" of option C should not be an issue. However, you could use the uDMA to load the TXFIFO, then use the EOT interrupt routine to disable the transceiver.
  • Thank you - yet I "continue" in the belief that (certain) aspects of the "CAN Bus & its More Advanced, Multi-Drop, Protocol"  will prove both helpful & may be adapted to your "RS485" design.

    Thru many "high effort design challenges" - following (i.e. "borrowing") proven, successful methods (i.e. "CAN protocol") proves far more, "Protected/Developed/Insightful than (any)  "Newly introduced technique!"       Note too the "vote of  "incomplete confidence" ...  "Should NOT be an issue!"      Such hedge is proper  - as any new design will LONG - prove suspect...

    Such argument has already been WON -  "USE of the API" proves FAR superior to the,  "To be invented - then tested - sometimes functional, (maybe) "DRM."