Issues with Serial1/2/3 on teensy 3.2

Status
Not open for further replies.

_Max_

Member
I am trying to make my new Teensy 3.2 work with Serial1/2/3 on Arduino 1.8.1, Teensyduino 1.35 and it seems to send garbage.
I debugged it down to absolutely minimal sketch:
void setup() {
Serial1.begin(9600, SERIAL_8N1);
Serial2.begin(9600, SERIAL_8N1);
Serial3.begin(9600, SERIAL_8N1);
}

void loop() {
Serial1.write(0xA5);
Serial2.write(0xA5);
Serial3.write(0xA5);
}

tried with just one serial line in the sketch as well.

Capturing serial lines with Salae clone, that always works fine for me.

Framing is always ok. Regardless of which baud rate, number of bits or parity options I try. No frame errors.

However the data is only correct in about 30% cases.
In other 30% cases it's shifted by 4 bits.
In other about 30% cases, it's shifted by 6 bits.
In the rest of the cases, it's shifted, plus couple of bits flipped.

One more data point: once it sends some byte, it keeps sending it for a few seconds, then switches to something else.
Not changing every frame.

Any ideas?
 
I am trying to make my new Teensy 3.2 work with Serial1/2/3 on Arduino 1.8.1, Teensyduino 1.35 and it seems to send garbage.
I debugged it down to absolutely minimal sketch:
void setup() {
Serial1.begin(9600, SERIAL_8N1);
Serial2.begin(9600, SERIAL_8N1);
Serial3.begin(9600, SERIAL_8N1);
}

void loop() {
Serial1.write(0xA5);
Serial2.write(0xA5);
Serial3.write(0xA5);
}

tried with just one serial line in the sketch as well.

Capturing serial lines with Salae clone, that always works fine for me.

Framing is always ok. Regardless of which baud rate, number of bits or parity options I try. No frame errors.

However the data is only correct in about 30% cases.
In other 30% cases it's shifted by 4 bits.
In other about 30% cases, it's shifted by 6 bits.
In the rest of the cases, it's shifted, plus couple of bits flipped.

One more data point: once it sends some byte, it keeps sending it for a few seconds, then switches to something else.
Not changing every frame.

Any ideas?
slow down the loop first, by inserting a delay that reflects the Baud rate.
You are sending much more data than the Baud rate can handle.
AFAIK Serialx.write is non blocking.
 
Thanks for the very quick reply.

Here is the new sketch:
void setup() {
Serial1.begin(115200, SERIAL_8N1);
}

void loop() {
Serial1.write(0xA5);
delayMicroseconds(500);
}

The behavior somewhat changed, now I can see changes even in consecutive bytes:

decode.jpg

There is also occasional noise showing as frame errors.
At 115200, the gap between bits is very uneven...
 
Also tried with Serial1.flush() instead of the delay.
It gets even worse, with frame errors all over at both 9600 and 115200.
 
From: https://www.pjrc.com/teensy/td_uart.html
Serial1.availableForWrite()
Returns the number of bytes that may be written without significant delay. If you write more than this number, Serial1.write() may need to wait for data to transmit before it can place your data into the transmit buffer.

So it makes me think that write is supposed to wait if buffer is full.

tried:
void loop() {
while (!Serial1.availableForWrite());
Serial1.write(0xA5);
}

and the data is as scrambled as ever.
 
From the Saleae picture I deduce that you have connection (wiring) problems: warnings at beginning of bytes due to flaky bits (wrong length?)
 
I would suggest to not use a clone!

I have used real Saleae logic analyzers to decode Serial data at much higher baud rates. Also is this clone still being sold? Are they still referring you to Saleae?

The software does not look like any current versions of their programs... Maybe a real old version they lifted?

Maybe their version does not properly handle 3.3v outputs? Or maybe try increasing the sample rate?
 
Thanks for all the responses, it's really good to see such an active forum where people are trying to help.
The software that I use is PulseView from Sigrok.

The problem persisted even with 200K samples on 9600 baud rate.

I finally managed getting the consistent stream with much longer delays between writes. So it is buffer overflow issue indeed.

It still puzzles me as the documentation is talking about flush() and availableForWrite() functions that are supposed to allow avoiding such
overflows without putting delays. Neither of them worked. flush() returned immediately and allowed overflows. availableForWrite was returning non-zeros immediately as well. So my immediate problem is solved, but looks like the libraries or the documentation is confusing.

thanks for all the help!
 
From the Saleae picture I deduce that you have connection (wiring) problems: warnings at beginning of bytes due to flaky bits (wrong length?)

Unrelated to the original topic, but can you please elaborate on this one? I am using pretty thin wires supplied in some generic Arduino kit, connecting with two wires through the breadboard. Should I hit limits on length, thickness? Would you recommend some better wires on Amazon or anywhere else?
 
It still puzzles me as the documentation is talking about flush() and availableForWrite() functions that are supposed to allow avoiding such
overflows without putting delays. Neither of them worked. flush() returned immediately and allowed overflows. availableForWrite was returning non-zeros immediately as well.

If there really is a problem with the code or the documentation, I want to find and fix it.

But so far, everything here looks like a failure of your logic analyzer or its software.

Please help me understand what you found wrong with flush() and availableForWrite(), either functionally or in their documentation. I understand your immediate problem is solved, but if there's something wrong on the Teensy side, I want to improve it so nobody else hits the same issue. Please help?
 
If there really is a problem with the code or the documentation, I want to find and fix it.

But so far, everything here looks like a failure of your logic analyzer or its software.

Please help me understand what you found wrong with flush() and availableForWrite(), either functionally or in their documentation. I understand your immediate problem is solved, but if there's something wrong on the Teensy side, I want to improve it so nobody else hits the same issue. Please help?

Sure, I would be glad to help.

I can't copy-paste right now from what I was running, but code like this still produced scrambled output:
void setup() {
//the actual baud-rate that I need is 26300
Serial1.begin(26300, SERIAL_8N1);
}

void loop() {
Serial1.write(0xA5);
Serial1.flush();
}

I would expect flush() to wait until the buffer is available.
From the documentation: "Wait for any transmitted data still in buffers to actually transmit. If no data is waiting in a buffer to transmit, flush() returns immediately."

Also tried:
void setup() {
//the actually baud-rate that I need is 26300
Serial1.begin(26300, SERIAL_8N1);
}

void loop() {
while (!Serial1.availableForWrite());
Serial1.write(0xA5);
}

Same result.

However, the following worked:
void setup() {
//the actually baud-rate that I need is 26300
Serial1.begin(26300, SERIAL_8N1);
}

void loop() {
Serial1.write(0xA5);
delayMicroseconds(500);
}

500us got my bytes nicely separated on the PulseView output, which makes sense as at 26300, one bit is 38us.
Multipled by 10 is about 380us, so 500 is good enough.

So you was clearly right at the very beginning that this is buffer overflow from writing too quickly.

The question is why flush() or availableForWrite() don't wait until the transmission is over and why
https://www.pjrc.com/teensy/td_uart.html documentation for availableForWrite() claims that "If you write more than this number, Serial1.write() may need to wait for data to transmit before it can place your data into the transmit buffer.". I understand that it says "may", so it "may not" as well, but still pretty confusing and leads to believe that write() is indeed blocking and availableForWrite() is meant to avoid blocking.
 
I'm running this on a Teensy 3.2 right now.

Code:
void setup() {
  Serial1.begin(26300, SERIAL_8N1);
}

void loop() {
  Serial1.write(0xA5);
  Serial1.flush();
}

Here is the result on my oscilloscope.

file.png

First and most importantly, Serial1 does indeed work properly for this code. I know you are skeptical, but it does indeed work. However, there are some issues common to *ALL* hardware serial communication, which you may not know.

Obviously you need to use good quality equipment for troubleshooting. A cheap clone logic analyzer running at a sample rate less than 5X the baud rate is always going to give you trouble. I know good quality oscilloscopes are expensive. You probably can get ok results from cheap gear, but it's difficult and unreliable since you don't have a good scope for comparison.

No matter how good your equipment, there is a fundamental an issue with parsing continuous serial data if the receiver is out of sync. 8N1 format uses 1 start bit (low), 8 data, and 1 stop bit (high). When Teensy or *any* other device sends continuously, the next start bit begins immediately after the prior stop bit. If you connect to such a signal in the middle of such data, where the start bit is missed, how would you ever know which low bits are the start bits and which are data bits?

You can't know. Your logic analyzer can't parse that. Neither can my oscilloscope. Neither could the UART in any chip.

Here is the exact same scope setup, if I disconnect and reattach the scope probe while the data is running:

file.png

You can easily see this is the exact same waveform. But the scope is showing unknown data with framing errors (the red marks) because it expected to see a stop bit (high) but an actual data bit was low. This process repeats over and over. It never manages to correctly parse the data stream. It's the exact same Teensy running the exact same code as above (I didn't reprogram it, just left it running). The only difference is I momentarily disconnected the scope probe, and when I clipped it back on there was a 8 in 10 chance of connecting during the 8 data bits, which of course happened here.

This is a problem common to all 8N1 serial communication. If the receiver doesn't begin parsing at the start bit (which is only a 1 in 10 chance doing hot plug of an active 100% usage data stream), it will mistake the first low data bit as a start bit. It's doing exactly what it should. You're never supposed to begin without a start bit. Ordinary 8N1 serial was never designed to be hot-plugged like USB. You're supposed to get wrong data if you begin parsing the stream after a start bit.

Of course, 8N1 serial parsing does recover if there are ever any idle times of 9 bits or longer. But if you always keep transmitting without any gaps between each stop bit and the next start bit, and out-of-sync receiver will never recover. This is simply how 8N1 serial works. The lack of recovery in parsing a continuous stream is one of the many weaknesses of this old and simple protocol.

I know Serial1 works reliably on Teensy 3.x. I've been through this over and over, and of course it's been used successfully by many thousands of people on Teensy 3.x over the last 5 years. If you're still not convinced, and I certainly can understand skepticism, hopefully this long-winded explanation of how 8N1 receiver parsing works can help you avoid more pitfalls as you continue to test. Teensy is extremely reliable, far moreso than that clone logic analyzer, but all testing needs to be done with an awareness of how the protocol really works, so you don't end up reaching a false conclusion things are broken with a test that no UART could ever pass because is violates the protocol.

Then again, while it's unlikely, I am willing to accept any reproducible bug report. Real bugs are sometimes discovered, even after many years of widespread use. The documentation is never perfect and can always use improvement.
 
Maybe I should have mentioned, if Teensy is already running and transmitting data when you start your logic analyzer capture, even though you may have already had the wires physically connected, that is essentially a hot-plug condition from the point of view of waveform analysis code in the logic analyzer software. With continuous no-gaps 8N1 transmission, you have only a 1 in 10 chance of starting within a stop bit, so the first falling edge seen is the proper beginning of a start bit. You've got an 80% chance of starting within the data, and a 10% change of landing within the start bit. Those 9 of 10 cases are almost certain to parse incorrectly.
 
Thanks Paul for the elaborate explanation. I properly synced the power-on event of Teensy and the controlled device, so Teensy now controls it perfectly.

May I just suggest to remove a note that "Serial1.write() may need to wait for data to transmit" and maybe explicitly call out the non-blocking nature
of write() in the function documentation?
 
May I just suggest to remove a note that "Serial1.write() may need to wait for data to transmit" and maybe explicitly call out the non-blocking nature of write() in the function documentation?
"write()" does block and wait for sufficient buffer space.
 
Status
Not open for further replies.
Back
Top