Teensy 4.1 RXTX errors

dorin

Member
Hello All,
I am trying to set up a communication between 2 Teensy 4.1 devices using the Serial1 port (RX1, TX1 on pins 0 and 1). I set up a Master Teensy device that sends data and a Slave Teensy that receives data. Master Teensy runs a counter using a timer, and then sends the counter value to the Slave Teensy through Serial1. Both Teensy devices send the counter value (generated or received) through the USB Serial port.
My bug: the Slave device reports incorrect value every multiple of 256 counted value. It looks like the MSB of that value is loaded from the previous Serial1 transmission.
Any help or suggestion appreciated.
Code and capture of values received through USB are following:
Master Teensy below:
Code:
/*Master Teensy*/
#define RXTX_BAUD 4000000
IntervalTimer TXRX; // declares interval timer to impose a known period on USB received data 
uint16_t kk_serial = 0;

void setup() {
	if (RXTX_BAUD >= 6000000)   
	  CCM_CSCDR1=105450240; // *0x0649_0B00‬ //UART_CLK_SEL bit set to 0 running from the 480MHz PLL
	Serial1.begin(RXTX_BAUD);
 
  TXRX.priority(1); // just reset is higher
  TXRX.begin(TimerCall, 40);  //TimerCall function runs every Sample_uS
}//end setup 

void loop() {}// everything is done timed by an interval timer set in the setup()

void TimerCall()
{  
  if (kk_serial < 65535) // 2^16 count
    {kk_serial = kk_serial+1;}
  else
    {kk_serial=0;}
       
    Serial.write((char *)&kk_serial, 2); // same as above 
    Serial1.write((char *)&kk_serial, 2); // same as above 
}

Slave Teensy below:
Code:
/*Slave Teensy*/
#define RXTX_BAUD 4000000
    uint16_t kk_recvd = 0;
    
void setup() {
  if (RXTX_BAUD >= 6000000)   
  CCM_CSCDR1=105450240; // *0x0649_0B00‬ UART_CLK_SEL bit set to 0 running from the 480MHz PLL
  Serial1.begin(RXTX_BAUD);
}// setup() end 

void loop() {
    if(Serial1.available() > 1) 
    { // last received read first: MSB, LSB
      kk_recvd = ((Serial1.read()<<8) + Serial1.read());
      TimeCall(); // calls timer function, but not a real timer
    }   
}// loop

void TimeCall()
{
    Serial.write((char *)&kk_recvd, 2); // sends sample counter value as a 16 bit number to USB       
    Serial1.flush();     
}


Capture.JPG
 
The T4 is little-endian, you must read the LSB first.

Also this line:
Code:
      kk_recvd = ((Serial1.read()<<8) + Serial1.read());
Does not have a defined order of execution - expressions do not impose a left-to-right order constraint in C or C++, unless a sequence point is used.
This is one way to read the 2 byte value LSB first:
Code:
      kk_recvd = Serial1.read();
      kk_recvd |= (Serial1.read() << 8);
Consecutive assignments do impose a sequencing.

The normal way to send data is not to use code that depends on endianness - ie explicitly write each byte in a defined order that will be the same whatever endianness the processor is, then the code is portable.

There's also the issue of synchronization - with the scheme you use there's no way to detect or recover from falling out of synch, typically comms uses packets in order to provide a such abilities.
 
You might want to look at the EasyTransfer or SerialTransfer library. These libraries provide functions to send/receive data via UART in simple packet structures. I recommend SerialTransfer over EasyTransfer, but a lot of people use EasyTransfer because it's "easy".
 
Hello Mark,
Thank you for the reply.
I missed the order of execution on the kk_recvd = ((Serial1.read()<<8) + Serial1.read());, but that seemed to work fine in general. Probably the compiler did the right thing.
Before posting the issue, I have tried code variants that explicitly send MSB and LSB on the channel in any particular order desired. With those codes I observe the same error.
Worth noting is that the error happens when the second byte received has a value of 0x00. Does not happen when the value is not 0x00. I would guess is a receive error.
The reason I chose a 2-byte counter is to verify if the serial channel can run at the speed I desired (4Mb/s in this case). The counter transmits all possible combinations of MSB-LSB there are.
It seems to fail the test when the second byte has a value 0x00. It also failed at 6Mb/s and 100kb/s.
I will post a version of the code for 100kb/s with the order of MSB/LSB reversed compared to the one here.

Dorin
 
This is the update of the code, running at 100kb/s with a 1ms rate.
in this version I reversed the order of MSB vs LSB, the Transmit Teensy sends MSB first and the Receive Teensy reads LSB first. (if Receive Teensy reads MSB first the data is misaligned all the time, probably that indicates that the Serial Buffer memory is FILO and not FIFO)

Code for transmit:
Code:
#define RXTX_BAUD 100000
IntervalTimer TXRX; // declares interval timer to impose a known period on USB received data 
uint16_t kk_serial = 0;

void setup() {
	if (RXTX_BAUD >= 6000000)   
	  CCM_CSCDR1=105450240; // *0x0649_0B00‬ //UART_CLK_SEL bit set to 0 running from the 480MHz PLL
	Serial1.begin(RXTX_BAUD);
 
  TXRX.priority(1); // just reset is higher
  TXRX.begin(TimerCall, 1000);  //TimerCall function runs every Sample_uS
}//end setup 

void loop() {}// everything is done timed by an interval timer set in the setup()

void TimerCall()
{   
  if (kk_serial < 65535) // 2^16 count
    {kk_serial = kk_serial+1;}
  else
    {kk_serial=0;}
    
   //Serial1.write((char *)&kk_serial, 2); // replaced by below, with LSB sent first 
   uint8_t kks_LSB, kks_MSB = 0;    
   kks_LSB = kk_serial & 0xFF;
   kks_MSB = kk_serial >> 8;  //uint16_t value = LSB | uint16_t(MSB) << 8 ;   
   Serial1.write(kks_MSB);      
   Serial1.write(kks_LSB);   
   
     Serial.write((char *)&kk_serial, 2); // same as above, but LSB goes out first 
}

code for Receive:
Code:
/*Slave Teensy*/
#define RXTX_BAUD 100000
    uint16_t kk_recvd = 0;
    
void setup() {
  if (RXTX_BAUD >= 6000000)   
  CCM_CSCDR1=105450240; // *0x0649_0B00‬ UART_CLK_SEL bit set to 0 running from the 480MHz PLL
  Serial1.begin(RXTX_BAUD);
}// setup() end 

void loop() {
    if(Serial1.available() > 1) 
    { // last received read first: MSB, LSB
      uint8_t kks_LSB, kks_MSB = 0;    
      kk_recvd = 0;     

    //    kk_recvd = ((Serial1.read()<<8) + Serial1.read()); //Replaced by below MSB is read first
  kks_LSB = Serial1.read(); // LSB is first read by Serial1.read()     
  kks_MSB = Serial1.read();
  kk_recvd = 256*kks_MSB+kks_LSB;
      
      TimeCall(); // calls timer function, but not a real timer
    }   
}// loop

void TimeCall()
{
    Serial.write((char *)&kk_recvd, 2); // sends sample counter value as a 16 bit number to USB       
    //Serial.printf(" %d \n",kk_recvd);
    Serial1.flush();     
}

observed error: glitch has an upward motion, but happens when the second receive is 0x00.
in particular it also happens when the sequence goes from 0xFFFF to 0X000, the received value at 0x0000 is 0x00FF.
Capture1.JPG

I wonder if this error/bug could be replicated by anyone?
Thank you for the support.
 
You might want to look at the EasyTransfer or SerialTransfer library. These libraries provide functions to send/receive data via UART in simple packet structures. I recommend SerialTransfer over EasyTransfer, but a lot of people use EasyTransfer because it's "easy".

I will try to use the SerialTransfer, is that efficient for sending 2 bytes at regular intervals?
 
I will try to use the SerialTransfer, is that efficient for sending 2 bytes at regular intervals?

There is overhead, but don't worry about that. You're not going to get very far doing serial data transfer without some structure. The code below is an example program from SerialTransfer. As you can see, you can send/receive data structures, which I suspect is what you'll eventually want. SerialTransfer uses a slightly non-standard form of what is known as COBS (constant overhead byte stuffing), so it's a fairly efficient form of structured send/receive.

Code:
#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[] = "hello";


void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);

  testStruct.z = '$';
  testStruct.y = 4.5;
}


void loop()
{
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t sendSize = 0;

  ///////////////////////////////////////// Stuff buffer with struct
  sendSize = myTransfer.txObj(testStruct, sendSize);

  ///////////////////////////////////////// Stuff buffer with array
  sendSize = myTransfer.txObj(arr, sendSize);

  ///////////////////////////////////////// Send buffer
  myTransfer.sendData(sendSize);
  delay(500);
}
 
Back
Top