Using processor-managed transmitterEnable pin for RS485, on Teensy 3.2

Status
Not open for further replies.

pramilo

Well-known member
Hi,

Teensyduino supports the transmitterEnable(pin) on all UARTs for use in applications where you need to signal that Transmission is ongoing. A typical case are RS485 chips but also applies to tri-state buffers.

Teensyduino implements this in software so you can use any pin as TxEnable.

There have been some recent reports in the forum of people having issues with the timing of TxEnable and subsequent communication problems (myself included).
See here https://forum.pjrc.com/threads/5706...erial-when-using-Transmit-Enable-pin?p=215480 and here https://forum.pjrc.com/threads/56281-Troubles-with-RS485-between-2-teensy

From my thorough investigation so far, one of the solutions is to have Teensy processor manage the Tx Enable pin natively in Hardware. This is something the processor can do with proper UART configuration, which I'll describe below.
This seems to work correctly and overcome the problems described when using the software implemented transmitterEnable.

The processor manages TxEnable by configuring the UARTx_MODEM register with the TXRTSE flag. This modifies the RTS pin to work as a TX Enable.

There is one constraint to using hardware-managed TXEnable:
you are limited on the pins that can be used for this (which are the UART hardware RTS pins)
  • For Serial 1: can be pins 6 or pin 19 (there may be another pin according to the datasheet but not sure)
  • For Serial2: pin 22 only
  • For Serial 3: pin 2 only
The pin you choose for TxEnable is the pin that you need to connect to the DE/RE pin of your chip (if RS485) or equivalent, to perform the indication of Active Transmission.

NOTE: Even though we are re-purposing the UART hardware RTS pin, RTS flow control can still be used because, once again, RTS support is implemented by Teensyduino in software (bit banged), so you can assign the RTS function to any other pin. This means that if you need traditional RTS functionality it is still available, even if using this TxEnable approach.

Therefore, here is how to enable processor(hardware)-managed TxEnable for RS485 and other transceivers in Teensy 3.2(for other models, refer to the datasheet)

For Serial1:

Code:
Serial1.begin(....)

/* Activation of TxEnable*/

// uncomment ONE of the lines below depending on which pin you want to use for TxEnable on Serial 1 (6 or 19)
//CORE_PIN6_CONFIG = PORT_PCR_MUX(3);
//CORE_PIN19_CONFIG = PORT_PCR_MUX(3);
UART0_MODEM |= (UART_MODEM_TXRTSE | UART_MODEM_TXRTSPOL);

[COLOR="#FF0000"]// DON'T issue the Serial1.transmitterEnable(x) instruction as that would turn on the software managed TxEnable and duplicate the functionality (causing malfunction probably)[/COLOR]


// now use the UART normally.
// no need to wait for Serial1.flush(). everything is managed by the processor and library
Serial1.write('a');
Serial1.write('b');
...

For Serial2:

Code:
Serial2.begin(....)

/* Activation of TxEnable*/

// Serial 2 can only use pin 22 for hardware managed TXEnable
CORE_PIN22_CONFIG = PORT_PCR_MUX(3);
UART1_MODEM |= (UART_MODEM_TXRTSE | UART_MODEM_TXRTSPOL);

[COLOR="#FF0000"]// DON'T issue the Serial2.transmitterEnable(x) instruction as that would turn on the software managed TxEnable and duplicate the functionality (causing malfunction probably)[/COLOR]

// now use the UART normally.
// no need to wait for Serial2.flush(). everything is managed by the processor and library
Serial2.write('a');
Serial2.write('b');
...

For Serial3:

Code:
Serial3.begin(....)

/* Activation of TxEnable*/

// uncomment ONE of the lines below depending on which pin you want to use for TxEnable on Serial 1 (6 or 19)
CORE_PIN2_CONFIG = PORT_PCR_MUX(3);
UART2_MODEM |= (UART_MODEM_TXRTSE | UART_MODEM_TXRTSPOL);

[COLOR="#FF0000"]// DON'T issue the Serial3.transmitterEnable(x) instruction as that would turn on the software managed TxEnable and duplicate the functionality (causing malfunction probably)[/COLOR]


// now use the UART normally.
// no need to wait for Serial3.flush(). everything is managed by the processor and library
Serial3.write('a');
Serial3.write('b');
...

That's it.

Best Regards
Pedro
 
Hi

Just a quick update on the instructions above:

The instructions UARTx_MODEM |= should always be placed AFTER the Serialx.begin().

If you place them before Serialx.begin, Teensy appears to crash / lock up. I'm unsure as to why this happens (couldn't find anything about this in the documentation), but I'd presume it to be related to UART not being initialized.

Interestingly, you can issue Serial.end() normally (no need to revert UARTx_MODEM) and Serial.begin() again without any problems. This is probably because Serial.end() does not really undo all that Serial.begin() does; it just stops scanning for characters and disables all interrupts, but keeps clock configuration etc.


---

Further to this, I noticed I have a typo in my previous post regarding the code for Serial3 above. The typo in the Comment. The code itself is correct.
Nevertheless please see below the updated code block with the corrected commented (correction signaled in Green)

Code:
Serial3.begin(....)

/* Activation of TxEnable*/

[COLOR="#008000"]// Serial 3 can only use pin 2 for hardware managed TXEnable[/COLOR]
CORE_PIN2_CONFIG = PORT_PCR_MUX(3);
UART2_MODEM |= (UART_MODEM_TXRTSE | UART_MODEM_TXRTSPOL);

[COLOR="#FF0000"]// DON'T issue the Serial3.transmitterEnable(x) instruction as that would turn on the software managed TxEnable and duplicate the functionality (causing malfunction probably)
[/COLOR]

// now use the UART normally.
// no need to wait for Serial3.flush(). everything is managed by the processor and library
Serial3.write('a');
Serial3.write('b');
 
The processors are setup such that you must enable access to a subsystem before you use (or even touch) them, or it will fault.

That is why the first thing that the function serial2_begin does is:
Code:
	SIM_SCGC4 |= SIM_SCGC4_UART1;	// turn on clock, TODO: use bitband
Again each Serial port has it's own clock enable and they may be in different registers... that is they are not all in SIM_SCGC4...
 
Hi,
I don't mean to hijack this thread, but does anyone happen to have looked at doing the same on Teensy 4.0? Specifically, I need to do this for Serial3, for which the hardware RTS pin would be pin 18. That much I found out (p. 308 in the reference manual), but the code to implement this, i.e. assign this functionality to the pin, is well beyond me.

It would be great if anyone could help me with this and/or point me in the right direction.

Thanks,
Wolfgang
 
@wpunkts - not sure what you are asking? I am confused by your question as this thread is about hardware managed Transmitter pin, and you are mentioning RTS pin?

The Transmitter enable pin, is often used for setups where the Serial communication is Half duplex... That is your code can either send data or receive data but not both at the same time. Often times to do this, you have a support chip with an IO pin that either directs your output to the external signal going out, or routes the external signal coming in.

Examples of this are things like: RS485.

Another example that I use is communicating with Robotis (Dynamixel) servos. Where I use a level shifter chip(or chips) to both control the direction of the signal, but also to drive the external signal at +5v...
 
Hi Kurt,
I mention RTS because of this quote from the original post:
The processor manages TxEnable by configuring the UARTx_MODEM register with the TXRTSE flag. This modifies the RTS pin to work as a TX Enable.

There is one constraint to using hardware-managed TXEnable:
you are limited on the pins that can be used for this (which are the UART hardware RTS pins).
So, my understanding is that I need to find which Teensy 4.0 pin is the hardware RTS pin for Serial3 (that should be 18 if I read the reference manual correctly), and configure it to function as a hardware-managed transmitter enable pin.
I could well be wrong, though. As I mentioned, this is (presently) beyond my abilities, which makes it all the more interesting to figure out.
 
I think what Kurt was trying to establish/understand is your setup.

If we know your setup better we can provide more useful (and appropriate) advice

So for part 1, if you could tell us what you are trying to it'd be great.
Are you trying to implement an RS485 chip?

Part 2 in reply to your question:

Resorting to Hardware Managed Tx Enabled is only required if you're doing high speed/high concurrency.

In normal scenarios where you need a transmit enable pin you can use the standard library function "SerialX.transmitEnable(pin number)".

If you need to do some troubleshooting, you can use the standard transmitEnable function and everytime you transmit issue the instruction "SerialX.flush()". This makes the program wait for the transmission to end before continuing.

If that works, but you then need something that works faster/non-blocking then you should consider the Hardware Managed TxEnable pin.

With regards to implementing that, probably the second line of the instructions for T 3.2 work for T4 (UART2_MODEM....)

The first line you need to adjust to your pin number and try.

I don't have the datasheet at hand but basically what you need to look for in the datasheet is:
A) the possible configurations for the pin you're using. There should be a table.
The PORT_PCR... Instruction sets the pin to the function defined in the table. In your case uou need it to be RTS. (This is pins can have many functions)

B) Look up the UART_MODEM register in the datasheet.
Confirm that the same flags that we're using here also apply to T 4 or look for equivalent ones.

If have the opportunity I'll have a peek at the T 4 datasheet and post back.

In any case, if you post more information about your use case/application that would be helpful for everyone (you included).

Best
Pedro
 
Hi Pedro,

thank you for taking the time.

Yes, this is about an RS485 chip. And yes, using the standard library function of "SerialX.transmitEnable(pin number)" will probably be good enough, but the fact is that I don't know yet. My reasoning is that any potential problem I can eliminate before I even start will help me a lot.

To give you a little background: we are working on a machine that uses three different sets of motors (they're lifting columns and linear actuators). These come from the manufacturer with control boxes (essentially the motor drivers) and handsets. Unfortunately there is no off-the-shelf solution to have these control boxes talk to each other and/or drive them from just a single handset. This creates two problems: 1) the operator has to handle three handsets (inconvenient), and 2) we can't implement necessary safety features (such as, for instance, disabling actuator A if the lifting columns are extended beyond a certain point, etc.). The simple solution to this is to build a single custom handset that is connected to all control boxes (solves problem 1), and to implement the safety features in that custom handset (the original handsets receive feedback from the control boxes, and I can use these feedbacks to implement those features). But, because this is 2019, you can't just have a box with buttons, it has to be a touch panel. This is where the RS485 comes into play. The plan is to use a 4Discovery display module from 4D Systems. It's nice, small, and comes with RS485 built in. Plus, the advantage over a box with buttons is that we can always implement new features in software.

To make a long story short, this will be the first time I really work with RS485, and after a search for Teensy and RS485 turned up this thread, I thought it would be a good idea to have hardware-managed TxEnable.

As far as implementation is concerned, I had already searched the reference manual for keywords like UART_MODEM, PORT_PCR, etc. and found nothing. Teensy 3.X and 4.0 appear to have enough difference that I can't figure it out by myself.
 
Thanks for clarifying this

Based on your description, my best advice to you would be to start with the standard "transmitEnable (pin)" function.

The reason I went with Hw Managed TX Enable was due to race conditions I was having.

I would definitely start with the transmitEnable(pin) because that is well known to work.

Once you have a baseline set up, if you still feel you want to improve on your code with Hw managed Tx Enable re-visit this.

The thing is, if you go with Hw Managed Tx Enable now, you are introducing a level of uncertainty at this point: I don't think anyone's ever tried this on T 4 so if things don't work at some point you'll be left wondering if this is the cause.

Starting with the library supported transmitEnable should give you a baseline that you know works.

For future proofing and keeping your options open, I'd still connect the TxEnable pin to pin 18 that you mention.
This keeps the options open if you ever want to switch from the software managed trabsmitEnable(pin) in the library, to the hw managed tx enable.

From my own experience, we ran with the software implementation of transmitEnable(pin) for a few years.
It was only when we increased the frequency of the system (a lot!) that we started noting some corner cases that we researched the hardware alternative.

One good trick and best practice to make sure you don't run into issues with the library transmitEnable (pin) approach is:

1) buffer each transmission in an array and then call SerialX.write(array), thus passing the full array in one go.
2) avoid as much as posible calling SerialX.write(byte) to transmit things byte by byte
Writing byte by byte is what may cause the race conditions; writing an array in one go, overcomes those issues.

Writing as an array in one go worked very well for us for many years and you get great performance improvements at various levels if you write as an array in one go. (You really do!)

My advice to you would be to go with the library transmitEnable (pin) and when sending data, send the whole packet in an array.

In the future if you ever want to try Hw managed Tx enable it's very simple: replace the call to transmitEnable with the two lines that enable it via hardware (code goes in the aame place and they're 2 lines; easy to change if needed).
But for now I'd start with a tried and tested approach using the library.
 
Thank you very much. That sounds like solid advice (especially array vs. byte), and it's reassuring to hear that software TxEnable works in most cases.
I had already layed out a little prototype board with the RS485 chip's DE and RE pins connected to Teensy's pin 18, because I thought I might as well. I should get the board next week, and then I can start playing and see what happens.

Thanks again,
Wolfgang
 
Status
Not open for further replies.
Back
Top