UART + I2C display --> not working over 1200 baud rate for UART

Status
Not open for further replies.

jwlee2218

Member
Hello everyone, please help me wandering lost in dark
I am trying to use two teensy 4.0 and communicate with each other by UART.
Thing is, UART communication works fine,
but whenever I try to show the communication result through the I2C display (by adafruit)
error occurs over the baudrate 1200 of UART.
1200 baudrate seems to be too slow to use, so I need higher baudrate

here is the code and the wiring

// Serial Sender
Code:
int pin0 = A0;
int pin1 = A1;
unsigned long my_time;
int reset_switch = 16;
int reset_pin = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(reset_switch, INPUT_PULLUP);
Serial.begin(9600);
Serial1.begin(9600);
Serial.println("Start Send");
if(Serial.available()>0){
  Serial.flush();
  delay(50);
  }
if(Serial1.available()>0){
Serial1.flush();
delay(50);
}
}

void loop() {
  reset_pin = digitalRead(reset_switch);
  if(reset_pin == LOW){
    do_reset();
  }
  int val0 = map(analogRead(pin0), 0, 1023, 0, 1100); 
  int val1 = map(analogRead(pin1), 0, 1023, 0, 200);
  
  my_time = millis();
  Serial1.print("<");
  Serial1.print(val0);
  Serial1.print(",");
  Serial1.print(val1);  
  Serial1.print(">");

  Serial.print(reset_pin);
  Serial.print("\t");
  Serial.print(my_time);
  Serial.print("\t");
  Serial.print(val0);
  Serial.print("\t");
  Serial.println(val1);

}

void do_reset() {
  // send reboot command -----
  SCB_AIRCR = 0x05FA0004;
}

//receiver
Code:
//Receiver Code

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

int reset_switch = 16;
int reset_pin = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(reset_switch, INPUT_PULLUP);
  Serial.begin(9600);
  Serial1.begin(9600);
  
  Serial.println("Start Receive");
  
  if(Serial.available()>0){
  Serial.flush();
  delay(50);
  }
  if(Serial1.available()>0){
  Serial1.flush();
  delay(50);
  }

  Serial.println("start");
  
}

void loop() {
  
  reset_pin = digitalRead(reset_switch);
  if(reset_pin == LOW){
    do_reset();
  }  
  
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}


void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial1.available() > 0 && newData == false) {
        rc = Serial1.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
    Serial.print("Integer ");
    Serial.println(integerFromPC);
    Serial.print("Float ");
    Serial.println(floatFromPC);
}

void do_reset() {
  // send reboot command -----
  SCB_AIRCR = 0x05FA0004;
}

teensy.jpg
 
Thank you for the prompt reply!
I meant that UART communication goes complete failure at over 1200 baud rate.
However, after changing the board with Teensy 3.2, UART communication seems to work fine but there are quite lots of noise.

So there are now two questions.
Is Teensy 4.0 not good for UART with I2C display over 1200 baud rate or would this be simply the soldering problems?
and, would there be any methods to reduce noises in the UART communication?

Thank you!
 
It's not noise or soldering. (or at least probably is not those issues)

You're running into somewhat obscure limitation of serial communication where the receiver can't sync to the 8 bit words when it starts "in the middle" of certain bitstreams which use 100% of the serial bandwidth. The more capable the transmitter is of sending without any extra gaps, the worst this problem gets.

I ran your programs on a breadboard. This is the hardware I used for testing.

img.jpg

Here is the waveform you're transmitting on pin 1.

file.png

Can you tell which bits are the start and stop and which are the 8 data bits? If the receiver starts watching before the transmitter begins sending, it should properly detect the first bit as a start bit and thereafter stay properly in sync. But if the transmitter is already running and the receiver has to begin parsing bits in the middle of this, how can it figure out which bits are meant to be start and stop and which are the intended 8 bit data? It can't. :(

If your data pattern happens to have groups of mostly 1 or mostly 0 bits, sometimes receivers can find the start bit and get into sync. But for this sort of bit pattern which uses 100% of the bandwidth, there is never an idle moment of 9 bits where the receiver can reliably know the next high-to-low change in the beginning of a start bit.


Here is a modified copy of your sender, using 1 Mbit baud rate. I added code which waits for 10 microseconds once every 5000 messages.

Code:
int pin0 = A0;
int pin1 = A1;
unsigned long my_time;
int reset_switch = 16;
int reset_pin = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(reset_switch, INPUT_PULLUP);
  Serial.begin(9600);
  Serial1.begin(1000000);
  Serial.println("Start Send");
  if (Serial.available() > 0) {
    Serial.clear();
    delay(50);
  }
  if (Serial1.available() > 0) {
    Serial1.clear();
    delay(50);
  }
}

void loop() {
  static int messageCount=0;
  
  reset_pin = digitalRead(reset_switch);
  if (reset_pin == LOW) {
    do_reset();
  }
  int val0 = map(analogRead(pin0), 0, 1023, 0, 1100);
  int val1 = map(analogRead(pin1), 0, 1023, 0, 200);

  my_time = millis();
  Serial1.print("<");
  Serial1.print(val0);
  Serial1.print(",");
  Serial1.print(val1);
  Serial1.print(">");

  Serial.print(reset_pin);
  Serial.print("\t");
  Serial.print(my_time);
  Serial.print("\t");
  Serial.print(val0);
  Serial.print("\t");
  Serial.println(val1);

[B]  // once every 5000 messages, allow a brief silent time
  // for the receiver to detect line idle and then start bit
  if (++messageCount >= 5000) {  
    Serial1.flush(); // wait for buffered data to transmit
    delayMicroseconds(10); // then wait approx 10 bit times
    messageCount = 0;
  }[/B]

}

void do_reset() {
  // send reboot command -----
  SCB_AIRCR = 0x05FA0004;
}


And to demonstrate the serial word sync issue, here is a copy of your receiver program also using 1 Mbit baud rate. I added a few lines to your recvWithStartEndMarkers() which print info about any unexpected characters received between valid messages.

Code:
//Receiver Code

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

int reset_switch = 16;
int reset_pin = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(reset_switch, INPUT_PULLUP);
  Serial.begin(9600);
  Serial1.begin(1000000);

  Serial.println("Start Receive");

  if (Serial.available() > 0) {
    Serial.clear();
    delay(50);
  }
  if (Serial1.available() > 0) {
    Serial1.clear();
    delay(50);
  }

  Serial.println("start");

}

void loop() {

  reset_pin = digitalRead(reset_switch);
  if (reset_pin == LOW) {
    do_reset();
  }
  
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    parseData();
    showParsedData();
    newData = false;
  }
}


void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();
    //Serial.println((char)rc);

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
[B]    else {
      Serial.print("Unexpected character: ");
      Serial.println(rc);[/B]
    }
  }
}

//============

void parseData() {      // split the data into its parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ",");     // get the first part - the string
  integerFromPC = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
  Serial.print("Integer ");
  Serial.println(integerFromPC);
  Serial.print("Float ");
  Serial.println(floatFromPC);
}

void do_reset() {
  // send reboot command -----
  SCB_AIRCR = 0x05FA0004;
}

If you run this while the sender is already transmitting, you will see it prints many lines about garbage characters. That is scenario where it can't get in sync. Then when the sender gives that 10 microsecond pause every 5000th message, you'll see the receiver does get into sync and all messages are received properly at 1000000 baud rate.

screenshot.png

Also, you'll probably notice I changed Serial1.flush() to Serial1.clear() in setup. Serial1.flush() waits for output to fully transmit. Serial1.clear() deletes any previously received data. But it only deletes data already in buffers. It can't make the hardware sync in the middle of a 100% bandwidth usages bitstream where there isn't any clear delineation of start bits. No software can do that. It's a fundamental limitation of asynchronous serial communication format. For a receiver to automatically sync, it needs to either start at the first start bit, or occasionally see an idle time between stop and start bits so it can get in sync with the 8 bit words.
 
Last edited:
Thank you for the explanation!
I tried your method, and the serial communication works totally well.
However, the display is still under noises...
And I found out that I did not upload the code for the display, so please find below.

Code:
//Receiver Code
#include <Wire.h>
#include <Adafruit_GFX.h>       // Include core graphics library for the display
#include <Adafruit_SSD1306.h>   // Include Adafruit_SSD1306 library to drive the display
#include <Fonts/FreeMonoBold18pt7b.h>  // Add a custom font
#include <Fonts/FreeMonoBold12pt7b.h>

//Create display
Adafruit_SSD1306 display(128, 64);

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

int reset_switch = 16;
int reset_pin = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(reset_switch, INPUT_PULLUP);
  Serial.begin(9600);
  Serial1.begin(1000000);

  Serial.println("Start Receive");

  if (Serial.available() > 0) {
    Serial.clear();
    delay(50);
  }
  if (Serial1.available() > 0) {
    Serial1.clear();
    delay(50);
  }

  Serial.println("start");
    // Display Setting "BRL"g
  delay(100);  // This delay is needed to let the display to initialize
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Initialize display with the I2C address of 0x3C
  display.setTextColor(WHITE);  // Set color of the text
  display.setFont(&FreeMonoBold18pt7b);
  display.clearDisplay();  // Clear the buffer
  display.setCursor(35, 45);
  display.println("BRL");
  display.display();
  delay(1000);
  display.clearDisplay(); 
}

void loop() {

  reset_pin = digitalRead(reset_switch);
  if (reset_pin == LOW) {
    do_reset();
  }
  
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    parseData();
    showParsedData();
    newData = false;
  }
  display_values();
}


void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();
    //Serial.println((char)rc);

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
    else {
      Serial.print("Unexpected character: ");
      Serial.println(rc);
    }
  }
}

//============

void parseData() {      // split the data into its parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ",");     // get the first part - the string
  integerFromPC = atoi(strtokIndx);     // convert this part to an integer

  strtokIndx = strtok(NULL, ",");
  floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
  Serial.print("Integer ");
  Serial.print(integerFromPC);
  Serial.print("     Float ");
  Serial.println(floatFromPC);
}

void do_reset() {
  // send reboot command -----
  SCB_AIRCR = 0x05FA0004;
}


void display_values(){
  display.clearDisplay();
  display.setFont(&FreeMonoBold12pt7b);
  display.setCursor(3, 45);
  display.println(integerFromPC);
  display.setCursor(72, 45);
  display.println(floatFromPC);
  display.display();
  display.clearDisplay(); 
//  delay(1);
}

and below is the display result, showing lots of noises
그림3.png

I am using SSD1309 adafruit 2.42 OLED display with SSD1306 library.
Before confronting this problem, the library was completely compatible
and moreover, there seems no adafruit official SSD1309 library so far...

Would there be anymore solutions that you could give?
Thank you!
 
Quick followup on this thread... Teensyduino 1.54 has been released, with a fix to a rare startup issue which sometimes hit projects using both Serial1 and Wire. Whether the startup issue happens depends on the code size, so it's rare and difficult to predict.

1.54 solves that issue, so I recommend upgrading, just in case.

https://www.pjrc.com/teensy/td_download.html
 
Status
Not open for further replies.
Back
Top