Teensy Hardware Flow Control RTS/CTS

When enabled does the hardware assert the RTS on full FIFO? - or is that an interrupt response? At the conservative speed of 4Mbaud it takes 2us to get 8 bits. Even if it is hardware driven how much time (ns's I suppose) until the sender can see the change on CTS and stop on the 'next' byte? Pushing the RWFIFO interrupt from 4 to 5-6 bytes could be enough to allow the sketch to pull more from the buffer - and fewer interrupts would smooth the overall system flow - at 2us per byte the FIFO would fire every 8us? Of course at sane baud rates it would be 10 or 20 times that.

I'm working to get a demo - but I have no hardware monitoring - just USB spew - so the more I try to 'see', the more random the result. Note to self: We know 4M and 6Mbps work no problem with good wires and an attentive receiver. For RTS/CTS - Since baud 'speed' is all relative - a slow-mo wreck is still a wreck - it'd probably be better at 200Kbps with controlled delays/waits and USB seeming FAST for spew rather than another random avenue for causing the trouble. Even at 200Kbps 2K messages will go 12 per second with a stodgy pace of 40us per character so it takes delayMicroseconds(2520) before a 64byte buffer fills instead of 126us - set up an elapsedMicros and relax and watch it tick and save the racy edge conditions for after the fundamentals are seen to be working. Make the transmitter buffer bigger than the receiver buffer so sending won't block - but a 'lazy' [~delayMicroseconds(40) per byte] receiver will get overrun, especially since I decided to test out and in on two Teensy serial ports with two Teensy's.
 
I'm not sure, what you're talking about (sometimes, my English leaves me...)
But, there is - i think - a problem in the receiver code.

On the Hardware-side, the RTS output works:
Code:
#define PIN_RTS 6 //Output
#define PIN_CTS 18 //Input

#define PIN_TEST 13 //Input

void setup() {
 delay(1000);
 Serial.begin(9600);
 Serial1.begin(110);
 
 serial_set_rts(PIN_RTS);
//serial_set_cts(PIN_CTS);
 
 Serial2.begin(110); 
 pinMode(PIN_TEST, INPUT);

 Serial.println("Start.");

 //Disable Interrupts of UART0:
 //Comment this out!
 NVIC_DISABLE_IRQ(IRQ_UART0_STATUS);
}

int count = 0;

void sendChar(){
 Serial2.print(0); 
 count++;
 delay(20);
}

void loop() {
  
 sendChar();
 Serial.println(count);
 if (digitalRead(PIN_TEST)) {
    Serial.println("SET RTS");while(1);
 }
}

To test it, use a Teensy 3, and a breadboard and connect pins 0 & 10 and 6 & 13.
When RTS goes, high, the LED turns on.

In the Code above, the interrupt for UART 0 is diabled. On the USB-Serial-console you should see the following output:
Code:
Start.
1
2
3
4
SET RTS
- The Watermark is reached, and RTS goes high.

BUT:
With enabled interrupt for UART0, this does never happen. (comment out the NVIC_DISABLE_IRQ(IRQ_UART0_STATUS); line)
The reason is, that the fifo gets read first, and AFTER that, there is a check if the rxbuffer has room.
This can't work...

Code from serial1.c:
Code:
do {
                if (use9Bits && (UART0_C3 & 0x80)) {
                    n = UART0_D | 0x100;
                } else {
                    n = UART0_D;
                }
                newhead = head + 1;
                if (newhead >= RX_BUFFER_SIZE) newhead = 0;
                if (newhead != tail) {
                    head = newhead;
                    rx_buffer[head] = n;
                }
            } while (--avail > 0);

this is ok for connections without handshake - but with enabled handshake, it has to be changed...

sorry if i missed the topic..

Edit: pin numbers were wrong
 
Last edited:
ok, I finally got the teensy receive with RTS handshake to work.
The solution is simply to disable RDRF and IDLE when buffer is full, and enable them again when there is space.

rx threshold set to 7. rx buffer is 64. I enable RDRF is there is at least RCFIFO spaces available. RCFIFO is the number of characters currently stranded in FIFO when interrupts were disabled.

2015-09-24_18-13-30.png
 
What do you do if there is a byte still in transmit when the fifo is full and RTS is being set. You have no headroom to capture that byte.

I'm not sure what you mean. If threshold is set at 7, RTS would not be set if fifo is 8, it will be set when fifo is 7. So what you described will never happen.

7 is a valid value for threshold as per datasheet. And as I have described in an earlier post, max is 7 to allow 1 more space for the character currently in the shift register. That's why if you look at the logic analyzer data, when the rts pulse is longer than 1 character, there will be 1 character that will be in the middle of the rising edge of rts. That is the character in the shift register that will take the last spot in the fifo.
 
Serial1.c is too long to post here, but this is the "main" change. there are minor changes elsewhere.

This is new getchar function

Code:
int serial_getchar(void)
{
	uint32_t head, tail;
	int c;

	head = rx_buffer_head;
	tail = rx_buffer_tail;
	if (head == tail) return -1;
	if (++tail >= RX_BUFFER_SIZE) tail = 0;
	c = rx_buffer[tail];
	rx_buffer_tail = tail;
#ifdef HAS_KINETISK_UART0_FIFO
	if ((UART0_C2 & UART_C2_RIE)==0) {//rx interrupt currently disabled
		int freespace;
		if (head >= tail) //rx head and tail would be unchanged from above if interrupts were disabled
			freespace = RX_BUFFER_SIZE + tail - head;
		else
			freespace = head - tail;
		if (freespace >= UART0_RCFIFO) {
			UART0_C2 |= (UART_C2_RIE | UART_C2_ILIE);//enable rx interrupts
		}
	}
#else
	UART0_C2 |= UART_C2_RIE;
#endif
	return c;

This is the else block (available>0) of the status_isr
Code:
			__enable_irq();
			head = rx_buffer_head;
			tail = rx_buffer_tail;
			do {
				newhead = head + 1;
				if (newhead >= RX_BUFFER_SIZE) newhead = 0;
				if (UART0_MODEM & UART_RXRTSE) {
					if (newhead == tail) {
						UART0_C2 &= ~(UART_C2_RIE | UART_C2_ILIE);//disable rx interrupts
						break;
					}
				}
				if (use9Bits && (UART0_C3 & 0x80)) {
					n = UART0_D | 0x100;
				} else {
					n = UART0_D;
				}
				head = newhead;
				rx_buffer[head] = n;
			} while (--avail > 0);
			rx_buffer_head = head;

this also requires the serial_set_cts and serial_set_rts that is in current Serial1.c on github
https://github.com/PaulStoffregen/cores/blob/master/teensy3/serial1.c

I set the threshold value in set_rts
Code:
int serial_set_rts(uint8_t pin)
{
	if (!(SIM_SCGC4 & SIM_SCGC4_UART0)) return 0;
	if (pin == 6) {
		CORE_PIN6_CONFIG = PORT_PCR_MUX(3);
	} else if (pin == 19) {
		CORE_PIN19_CONFIG = PORT_PCR_MUX(3);
	} else {
		UART0_MODEM &= ~UART_RXRTSE;
		UART0_RWFIFO = 4;
		return 0;
	}
	UART0_MODEM |= UART_RXRTSE;
	UART0_RWFIFO = 7;
	return 1;
}

and also declare the functions in HardwareSerial.h like in latest version
https://github.com/PaulStoffregen/cores/blob/master/teensy3/HardwareSerial.h

The non fifo code part has a similar change.

I also changed all UART0_C2 assignments to use bitwise operations instead of straight assignment.

Paul, is there bitband operators for bits of UART registers?
 
Last edited:
would'nt it be a cleaner approach to check the available space in RX_BUFFER before entering the loop?
something like

while (avail && rx_buffer_not_full) {
add received byte to buffer
}

i think, there is no need to switch interrupts off/on.
 
code snippet #2 '__enable_irq();' is because all int's stop on entry to uart0_status_isr() as commented in current source to prevent a race condition recovering from an IDLE FIFO. A workaround for a Freescale "catch_22"
 
code snippet #2 '__enable_irq();' is because all int's stop on entry to uart0_status_isr() as commented in current source to prevent a race condition recovering from an IDLE FIFO. A workaround for a Freescale "catch_22"

i was refering to this:
doughboy said:
Code:
UART0_C2 &= ~(UART_C2_RIE | UART_C2_ILIE);//disable rx interrupts
 
i was refering to this:

the prior off/on was needed as noted - this "//disable rx interrupts" is unique to a FULL buffer. It happens before the data is pulled and then exits - stopping further incoming int's until space is made by "serial_getchar(void)"

<edit>: I took post#32 "while (avail" to literally mean 'avail' from snip 1.
Snip#2 >> error: 'avail' was not declared in this scope ;)
 
Last edited:
There are no such interrupts if rts stays high.

avail: snip#2

That seems to make sense - now - but if that were the case, how did uart0_status_isr() get called. I'm done digging for now.

BTW - I put my sample down to a mere 2,000,000 baud and pushed the lengths beyond my Rx&Tx buffers (so code should block waiting for Tx) and all seems well running out one teensy serial while taking in another port with the 2nd teensy mirroring with the same code. This is well beyond length it was doing before where it started to fail - so it is time to add Tx buffer space and/or stall Rx reading to see a controlled need for rts/cts.
 
That seems to make sense - now - but if that were the case, how did uart0_status_isr() get called. I'm done digging for now.

NVIC_TRIGGER_INTERRUPT() or NVIC_SET_PENDING() in getchar() - but only if the buffer is full. Getchar removes one byte fromthe buffer, so there is room for one byte.
 
Last edited:
would'nt it be a cleaner approach to check the available space in RX_BUFFER before entering the loop?
something like

while (avail && rx_buffer_not_full) {
add received byte to buffer
}

i think, there is no need to switch interrupts off/on.

I'm pretty sure I tried all permutations I can think of without disabling interrupt and nothing worked.
I think if you do as you are suggesting, the interrupt will never be re triggered again. It is stated in the datasheet that the flag must be cleared for interrupts to re trigger, and to clear interrupt flag, you MUST read S1 and D registers.
The tx code turns interrupts on/off between putchar and isr, so this is just the complement for rx.
I need to see the alternative working code to determine if it is cleaner or not.
 
I'm not sure what you mean. If threshold is set at 7, RTS would not be set if fifo is 8, it will be set when fifo is 7. So what you described will never happen.
You can't guarantee that the transmitters will not send a couple of extra bytes when setting RTS. Maybe that works for your particular application but it is not universal to all that would use this api. So setting the watermark to half is the best approach for a general purpose application.
 
You can't guarantee that the transmitters will not send a couple of extra bytes when setting RTS. Maybe that works for your particular application but it is not universal to all that would use this api. So setting the watermark to half is the best approach for a general purpose application.

uart is a protocol standard. It is guaranteed, otherwise, the other end is not protocol compliant. :)
by your reasoning, whatever you set it to will never work 100%.

The RTS signal goes high while still receiving the current character, which means the sender is still sending the current character. Therefore, the sender will be able to see it's cts is high and not send the next character (cts is implemented in hardware as gate to transmitter, we are not talking about sender that implements hardware handshake in software). The ARM processor will set RTS high as soon as the stop bit of the 7th character is complete and character is moved to fifo. So RTS will be high shortly after the start bit of the next character arrives. If there is big latency issue, then I think the question is, should you be using that high a baud rate (i.e. you are using too high a baud rate if latency is causing buffer overrun).

I suppose we can say, there is no right or wrong answer. The code will work with threshold set to any value between 1 and 7. I have tested with rx buffer down to 16 bytes at 4.608 mbaud and no data loss.

just fwiw, from what I have read, the esp8266 has a 128 character fifo and the firmware sets the rx threshold to 127.

I will enable the buffer overrun interrupt when I do my load testing to determine if fifo buffer overrun occured or not.
 
Last edited:
You can't guarantee that the transmitters will not send a couple of extra bytes when setting RTS. Maybe that works for your particular application but it is not universal to all that would use this api. So setting the watermark to half is the best approach for a general purpose application.

I agree, a solution is needed which tolerates the other side sending a couple extra bytes *after* we've signaled RTS.

My gut feeling is Frank's idea to check the buffer's available space before draining the FIFO is probably the best path. So far, I haven't actually done much with this, but I'm going to look at it later today. Any input is most welcome.

I really want to get high quality RTS/CTS flow control working for the upcoming 1.26 release.
 
You can't guarantee that the transmitters will not send a couple of extra bytes when setting RTS. Maybe that works for your particular application but it is not universal to all that would use this api. So setting the watermark to half is the best approach for a general purpose application.
Yep - I have seen this for example when I have used this with XBees. I would typically get 2 more bytes of data from them. This for example busted the use of Serial control when I used to do stuff with boards from Basic Micro (Basic Atom Pros), so I had to roll my own code...
 
An alternate way is to set RTS via digitalWrite (when buffer is full).. supersimple, reliable, no fiddling with interrupts and bugs in hardware....
...can use any pin and every Serial on T3.. works with fifo and without.

edit: used this with my "audiostreaming"-experiment. Worked great.
 
Last edited:

I've added your code as a text file on github, so we won't lose it. Thanks for the hard work on this!

https://github.com/PaulStoffregen/cores/blob/master/teensy3/serial1_doughboy.txt

Lately I've been thinking quite a bit about how I want to implement RTS output. My main concern with allowing the hardware to control RTS is we can tolerate only 4 more bytes after signaling the other side to stop sending. The common case involves 1 or 2 extra bytes, but I want to be able to tolerate many more (possibly far more than the entire FIFO).

So I'm contemplating software controlled RTS, like Frank suggested, but using high and low watermark thresholds at assert and deassert RTS. I want to give that a serious try. If it doesn't work out, I'll merge your interrupt-disabling code and we'll probably just not support RTS on the non-FIFO ports.
 
Hi,
I was wondering if I can use the pins 3, 4 on Teensy 3.1 for Rx Tx per the diagram below:

Teensy3.1SoftwareSerial.jpg

Attempting to use the above pins with the following code:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(4, 3); // RX, TX
const int ledPin = 13;
void setup()
{
pinMode(ledPin, OUTPUT);
// Open serial communications and wait for port to open:
Serial.begin(57600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}


Serial.println("Goodnight moon!");

// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
mySerial.println("Hello, world?");
}

void loop() // run over and over
{
char c;

if (mySerial.available()) {
c = mySerial.read();
Serial.write("SoftSerial-> ");
Serial.println(c);
digitalWrite(ledPin, HIGH); // set the LED on
delay(1000); // wait for a second
digitalWrite(ledPin, LOW); // set the LED off
delay(1000); // wait for a second
}
if (Serial.available())
mySerial.write(Serial.read());
}

I was able to send message from Teensy 3.1 to UART, but I was unable to receive on Teensy 3.1 from UART.

The Hardware Serial1 port (pins 0,1) appear to work well, indicating my UART hardware is working fine.

Do I need to set up RTS and CTS for the above pins (3,4) ?

Thanks in advance
 
Back
Top