Practical UART speeds for T3.6 to T3.6 Communication

Status
Not open for further replies.

Scientist

Well-known member
I have an instrument that uses two Teensy 3.6 in its design. One is a supervisor that controls all the high level functions of the instrument, the second runs a fast data acquisition (DAQ) sub system. About once a second the DAQ sub system will need to send 2KB of data to the supervisor over a serial connection (UART4 on the Supervisor UART3 on the DAQ) with 5cm of wire. I need to do this as quickly as possible as the DAQ is dead while the transfer is occurring. My question is how quickly can the two UARTS practically communicate? From the data sheet, it seems like F_BUS/16 = 3.75MBaud is the maximum frequency, but in the real world that seems unlikely to work. Has anyone experimented with what sort of speeds are reliable over a twisted pair?

Thanks!
 
I suspect your real issue will not be the wire speed but rather how fast can you receive the characters into the supervisor program. I think I remember that UART4 does not have a receive FIFO so it will be necessary to detect character available, grab the character, and store it safely away pretty fast to avoid over runs. A similar question occurs on the transmit side. Perhaps a couple of quick simulations would give you a target baud rate.
 
Serial1 & Serial2 run from F_CPU, so in theory they can go up to 11.25 Mbit/sec when running at 180 MHz. These 2 ports also have FIFOs, which should really help at the higher speeds.

That speed might work if you use RTS/CTS flow control. It might also work without flow control if you increase the buffer sizes by editing serial1.c or serial2.c, and if your usage transmits the 2K of data infrequently enough for the receiver to always empty its receive buffer before you transmit again. But flow control would be safer.
 
I tried to use 600,000 baud with my lcd, but it didnt work, i would presume it's a lcd firmware issue as ive seen others here do teensy to teensy @ 6Mega baud
 
I found that 4.6MBit/s were reliable - but Serial 1 and with flowcontrol. No transmission-errors (~3cm) occured.
 
I am tied to UART3 and UART4 due to the hardware - but from reading the K66 reference it seems like all the UARTS have 128 word FIFOs. Am I mistaken? No provision for flow control, any guesses on what sort of through put I can achieve?
 
it seems like all the UARTS have 128 word FIFOs. Am I mistaken?
Yes.

The max throughput depends on your sketch only. We can not guess that. It depends on how fast it can read the incoming data. Increasing the rx-buffer helps a lot, if you don't use hardware flow-control.

Edit: But don't under-estimate the Teensy. It is really very fast.
 
Last edited:
The sketch isn't written yet - but as I control both ends of the link my thought is to have both processors drop all their other tasks and shuttle the data through as quickly as possible (which assuming 1Mbaud would be ~ 20ms). Anyway, I guess the proof is in me trying it out. I am just trying to get a handle on my timings as I wait for the hardware to be spun.
 
but from reading the K66 reference it seems like all the UARTS have 128 word FIFOs. Am I mistaken?

Sadly, you're mistaken on this point. Only Serial1 and Serial2 have hardware FIFOs, and they are 8 bytes in each direction. All 6 ports have software-based buffering.
 
Without RTS/CTS flow control, you're almost certainly going to have to increase the buffer on the receiving side.

You can also edit the interrupt priority, but it's already at 64 which gives it a higher priority (lower numbers = higher priority) than USB at 112 and most other interrupts which default to 128.
 
DMA on both sender and receiver, after a preliminary handshake to start the transfer ?
 
I guess, for only 1MBIT, that's not needed.
Before using DMA, i'd consider spending two more pins for RTS/CTS. It is more reliable anyway.
 
Last edited:
Also when I read something like this I wonder about the structure of the program. Especially with comments like:
I need to do this as quickly as possible as the DAQ is dead while the transfer is occurring.

that is with our without flow control, if you do something like: Serial.write(my_buffer, 2048);
The code is going to fill the output buffer. I believe the default for Serial3 tx buffer is 40 bytes. It will then hang around until at least 2000+ bytes are sent before that write completes.

You can maybe increase the size of the buffer to 2K+, which can maybe get you around this.

Or you might try to organize the code to keep a Head/Tail type pointers, and if possible do something like:
If <more data to send>
<do write of only as much as will fit in buffer.
Process DAQ data

That is make use of Serial3.availableForWrite to break your output up into write statements which do not block your program

And as others mentioned, if it looks like the RX side has issues keeping up, I would then look into RTS/CTS...
 
Thanks for the guidance. Unfortunately the physical layer is already set, so I will see what I can do with two wires and a tight loop and then go from there.
 
First increase the rx-buffer to 2KB or more.
That would be my first attempt as well. But wonder if his sampling code can deal with having the Serial port's ISR being called to output the next byte to the hardware...
 
Yup, but the 2KB are sent as block sent once a second - so there might be a check for "buffer empt" needed, before starting to sample the next block.

Or, on the TX-Side, DMAing the sampled data directly to the UART..
Anyway, it's always better to try the easiest approach first :)
 
DMA both Tx and Rx, with a parallel Interval Timer on the Rx side so you know if you've missed part or all of a message. I'm assuming the messages are always of known length. Having messages just "Show up" with minimal processor intervention is my definition of heaven!
 
So I have the hardware for this project working and need to revisit the serial communications. For testing, where the maximum speed is not too critical, I have been creating a byte array of the ascii representation of the data I need to send (to make it east to read) and am trying to send it out the serial port as well as the USB port. I create the array to output from an array of integers:

Code:
 HG_Buffer_Index += sprintf(&HG_Out_Buffer[HG_Buffer_Index], "%d,", HG_Array[i]);

Then I send the array to the USB port (for debug) and to Serial4:
Code:
Serial.write(HG_Out_Buffer, HG_Buffer_Index); //send high gain output buffer to USB
int timeStart = SYST_CVR;  // time how long it takes
Serial4.write(HG_Out_Buffer,HG_Buffer_Index); // send high gain output buffer to Serial port 4
int timeEnd = SYST_CVR;

However I get the following compilation error for Serial4.write() :
PHA_V1_2:176: error: invalid conversion from 'char*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]
Serial4.write(HG_Out_Buffer,HG_Buffer_Index);
^
In file included from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/WProgram.h:16:0,
from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Arduino.h:1,
from /var/folders/sc/_7jsdt4j2b930c8hb647_f9h004s3n/T/arduino_build_324592/sketch/PHA_V1_2.ino.cpp:1:
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/HardwareSerial.h:368:17: error: initializing argument 1 of 'virtual size_t HardwareSerial4::write(const uint8_t*, size_t)' [-fpermissive]
virtual size_t write(const uint8_t *buffer, size_t size)
^
invalid conversion from 'char*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]

It works fine withe Serial.write() on the USB port. Any insight on why these seem to work differently?
 
It's always helpful to have all the code. In this case, the definition of HG_Out_Buffer.

But I suspect from the compiler errors that it's defined as a char array. Probably the easiest way to fix this is to cast it where you use it, e.g., :
Serial4.write( (const uint8_t *) HG_Out_Buffer,HG_Buffer_Index);
 
Thanks! You are correct. Recasting HG_Out_Buffer using Serial4.write( (const uint8_t *) HG_Out_Buffer,HG_Buffer_Index); fixed it. I was confused as to why this was causing an issue for Serial4 and not Serial. Now to see how fast I can push the serial transfer ...
 
However I get the following compilation error for Serial4.write() :
Code:
PHA_V1_2:176: error: invalid conversion from 'char*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]
Serial4.write(HG_Out_Buffer,HG_Buffer_Index);
^
In file included from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/WProgram.h:16:0,
from /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/Arduino.h:1,
from /var/folders/sc/_7jsdt4j2b930c8hb647_f9h004s3n/T/arduino_build_324592/sketch/PHA_V1_2.ino.cpp:1:
/Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/HardwareSerial.h:368:17: error: initializing argument 1 of 'virtual size_t HardwareSerial4::write(const uint8_t*, size_t)' [-fpermissive]
virtual size_t write(const uint8_t *buffer, size_t size)
^
invalid conversion from 'char*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]

It works fine withe Serial.write() on the USB port. Any insight on why these seem to work differently?
That's a bug in HardwareSerial. The Arduino Print class (which HardwareSerial inherits from) has methods for 'char*' and 'uint8_t*', but 'write(char*)' is hidden in HardwareSerial. Serial/usb_serial_class (the USB serial emulation) has 'using Print::write;', which makes the hidden method available.
 
Status
Not open for further replies.
Back
Top