Teensy 4.1 Sensed bit length dropped to half after update and rebuild

Hello,

I've had code running on qty 2 Teensy 4 and then 4.1 for quite a few years detecting the bits on a serial stream with a Tamagawa (qty 4) 23 bit absolute encoder. The PCB I am using has serial 3 and 4 and the bit stream is 2.5 mbs, and earlier when I tried using the UARTs and twiddling the frequency, I had no success (apparently using FlexIO it might be possible, but that's a major PCB rework). Instead I twiddle the Tx pin with a time waster to achieve 2.5 mbs @600 Mhz core rate) using asm NOP, then change the DE/RE state wait the prescribed Tamagawa time for reception, and then capture about 1000 values for the 60 expected Rx bits from the encoder. Then I hover over the table from either end and detect the stream. Yes bizarre, but it has worked for years at 50 samples per second. it used to be that a single bit covered 14 samples, but recently when I rebuild the code due to library upgrades, the encoders were inop, and my code was telling me the bit length was now 8 or so. I checked power, core speed, temperature, and other possible sources and all look normal. I tried another 4.1, same. Strangely, the Tx timing is unchanged and the encoder responds, so it has to be uniquely in the Rx section.

Finally I changed my code to expect the new bit length, but I rather figure what went wrong.

Regards
P.S: Paul, your Teensy 4+ is simply the best computing device I've ever worked on since 1980 and I've touched SEL, VAX, PDP11, Apple, PET, Vic20, C64, Atari, every type of PC ,assembler, punch cards, dos, windows, AI...), picos, so much more, nothing gave me as much pleasure and reliability as your devices and libraries. I tossed a box full of Phidgets and SBCs for your Teensy and my observatories 250 feet yonder are finally reliable and don't need me to maintain them at -30C and +30C, mosquitoes, dark, sunny, snow, ice, at all as often.

// Mount Encoders
#define BIT_LEN 60
#define TXBITLEN 126 // was 126
#define RXBITLEN 109 // was 109
#define STRMDLY 10
#define DBG 23
const int EncDir[2] = {41, 40};
const int EncTx [2] = {17, 15};
const int EncRx [2] = {16, 14};
unsigned char EncData[4][6] = {0, 0, 0, 0};
bool Bits[BIT_LEN];
void TamEncodersInit()
{
// initialize the digital pins of encoders
pinMode(EncDir[0], OUTPUT);
pinMode(EncTx [0], OUTPUT);
pinMode(EncRx [0], INPUT);
pinMode(EncDir[1], OUTPUT);
pinMode(EncTx [1], OUTPUT);
pinMode(EncRx [1], INPUT);
pinMode(DBG, OUTPUT);
}
void TamEncoders()
{
int Offset = 0;
noInterrupts();
if (0) // Method 1 (temporal capture of mid bit)
{
// Encoder 0 handler
EncoderTx(0); // Send command
TimeWaster(TXBITLEN); // Delay before releasing bus
EncoderRx(0); // Read data
// Encoder 1 handler
EncoderTx(1); // Send command
TimeWaster(TXBITLEN); // Delay before releasing bus
EncoderRx(1); // Read data
}
else // Method 2 (atomic capture with post stream scaling bit parsing)
{
// Encoder 0 Streaming handler
EncoderTx(0); // Send command
TimeWaster(STRMDLY); // Delay before releasing bus
EncoderRx2(0); // Read data
// Encoder 1 Streaming handler
EncoderTx(1); // Send command
TimeWaster(STRMDLY); // Delay before releasing bus
EncoderRx2(1); // Read data
Offset = 2;
}
interrupts();

memcpy(&DevIO.TamEnc[0][0], &EncData[1+Offset][0], 6);
memcpy(&DevIO.TamEnc[1][0], &EncData[0+Offset][0], 6);

static int sub = 0;
#define SUB_MAX 25
if ((++sub > SUB_MAX) && 0) // DEBUG Tamagawa encoders
{
sub = 0;
for (int e=0; e<4; e++)
{
for (int n=0; n<6; n++)
{
Serial.print(EncData[e][n], HEX);
Serial.print(" ");
}
int posn = EncData[e][4]<<16 | EncData[e][3]<<8 | EncData[e][2];
Serial.print(posn);
Serial.print(" - ");
}
Serial.println(" ");
} // Print position - debug
}
// Receive encoder reply - time sequenced method
void EncoderRx(int Encoder)
{
int n = 0;
unsigned char Data = 0;
// Search for encoder start bit but timeout otherwise
do
{
if (digitalReadFast(EncRx[Encoder]) == LOW) break;
n++;
} while (n < 2000);
digitalWriteFast(DBG, HIGH); // Debug
TimeWaster(RXBITLEN * 0.1); // Delay to middle of bit
int b = 0;
for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
{
digitalWriteFast(DBG, (n & 0x1)); // Debug
digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN); // Ignore start bit
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
Bits[b++] = digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN);
digitalReadFast(EncRx[Encoder]); TimeWaster(RXBITLEN); // Ignore stop bit
}
digitalWriteFast(DBG, LOW); // Debug
b = 0;
for (n = 0; n < 6; n++)
{
Data = 0;
for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
EncData[Encoder][n] = Data;
}
}
// Receive encoder reply - pattern search and scaling method
unsigned char DataStream[ENC_STREAM_SIZE];
void EncoderRx2(int Encoder)
{
unsigned char Data = 0;
static float BitLenMax[2] = {0.f, 0.f};
float BitLen = 0;
int diff = 0;
// Capture Rx stream
unsigned int n = 0;
do
{
DataStream[n] = digitalReadFast(EncRx[Encoder]);
TimeWaster(5);
n++;
} while (n < ENC_STREAM_SIZE);
// Debug
//if (Encoder == 0) memcpy(DevIO.Data, DataStream, sizeof DevIO.Data);
int Start = 0;
int End = ENC_STREAM_SIZE-1;
while (DataStream[Start] && (Start < (ENC_STREAM_SIZE-1))) { Start++; }
while (DataStream[End ] && (End > 1 )) { End--; }

// Check if detected boudaries are valid
if ((Start < End) && (Start < 150) && (End < (ENC_STREAM_SIZE - 150)))
{
// The following is incorrect as the last stop can also include data bits high!!!
// Thereby wrongly detecting the last stop bit position
diff = End - Start;
BitLen = (float)diff / (float)(BIT_LEN - 1); // Last stop bit not counted
if (BitLen > BitLenMax[Encoder]) BitLenMax[Encoder] = BitLen;
BitLen = 7.97; // was 14.07f;
// int BitCnt = 0;
float off = (float)Start + BitLen/2;
int b = 0;
for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
{
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen; // Ignore start bit
for (int m=0; m<8; m++)
{
Bits[b++] = DataStream[(int)off];
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen;
}
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
off += BitLen; // Ignore stop bit
}
b = 0;
for (n = 0; n < 6; n++)
{
Data = 0;
for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
EncData[Encoder+2][n] = Data;
}
}
static int sub = 0;
#define SUB_CNT 100
if ((++sub > SUB_CNT) && 1)
{
if (sub > (SUB_CNT+1)) sub = 0;
Serial.printf("Enc:%1d S:%4u E:%4u df:%4u ln:%5.2f lnmx:%5.2f\n",
Encoder,
Start,
End,
diff,
BitLen,
BitLenMax[Encoder]);
}
}
// Send command to transmit 0010 0000 01 + pause before releasing bus
void EncoderTx(int Encoder)
{
digitalWriteFast(EncDir[Encoder], HIGH); // set bus for master

digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0`
digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN); // 1
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN); // 0
digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN); // 1

digitalWriteFast(EncDir[Encoder], LOW); // set bus for slave
}
void TimeWaster(int repetitions)
{
int n = 0;
for (n = 0; n < repetitions; n++)
{
__asm__("nop");
}
}
 
May I suggest pasting code inside a "code" section? That will preserve the formatting. See the "</>" button. I generally don't read any code if it's not formatted well because it's harder to read.
 
Yup, I was too quick then tried to edit it after posting... I removed code that wasn't enable (...).

Code:
// Mount Encoders
#define BIT_LEN   60
#define TXBITLEN  126 // was 126
#define RXBITLEN  109 // was 109
#define STRMDLY   10
#define DBG 23
const int EncDir[2]  = {41, 40};
const int EncTx [2]  = {17, 15};
const int EncRx [2]  = {16, 14};
unsigned char EncData[4][6]  = {0, 0, 0, 0};
bool Bits[BIT_LEN];

void TamEncodersInit()
{
  // initialize the digital pins of encoders
  pinMode(EncDir[0], OUTPUT);
  pinMode(EncTx [0], OUTPUT);
  pinMode(EncRx [0], INPUT);
  pinMode(EncDir[1], OUTPUT);
  pinMode(EncTx [1], OUTPUT);
  pinMode(EncRx [1], INPUT);
  pinMode(DBG, OUTPUT);
}

void TamEncoders()
{
  int Offset = 0;

  noInterrupts();

  if (0)  // Method 1 (temporal capture of mid bit)
  {
...
  }
  else  // Method 2 (atomic capture with post stream scaling bit parsing)
  {
    // Encoder 0 Streaming handler
    EncoderTx(0);           // Send command
    TimeWaster(STRMDLY);    // Delay before releasing bus
    EncoderRx2(0);          // Read data

    // Encoder 1 Streaming handler
    EncoderTx(1);           // Send command
    TimeWaster(STRMDLY);    // Delay before releasing bus
    EncoderRx2(1);          // Read data
    Offset = 2;
  }

  interrupts();
 
  memcpy(&DevIO.TamEnc[0][0], &EncData[1+Offset][0], 6);
  memcpy(&DevIO.TamEnc[1][0], &EncData[0+Offset][0], 6);
 
  static int sub = 0;
  #define SUB_MAX 25
  if ((++sub > SUB_MAX) && 0)   // DEBUG Tamagawa encoders
  {
    sub = 0;
    for (int e=0; e<4; e++)
    {
      for (int n=0; n<6; n++)
      {
        Serial.print(EncData[e][n], HEX);
        Serial.print(" ");
      }
      int posn = EncData[e][4]<<16 | EncData[e][3]<<8 | EncData[e][2];
      Serial.print(posn);
      Serial.print(" - ");
    }
    Serial.println(" ");
  } // Print position - debug
}

// Receive encoder reply - pattern search and scaling method
unsigned char DataStream[ENC_STREAM_SIZE];
void EncoderRx2(int Encoder)
{
  unsigned char Data = 0;
  static float BitLenMax[2] = {0.f, 0.f};
  float BitLen = 0;
  int diff = 0;

  // Capture Rx stream
  unsigned int n = 0;
  do
  {
    DataStream[n] = digitalReadFast(EncRx[Encoder]);
    TimeWaster(5);
    n++;
  } while (n < ENC_STREAM_SIZE);

  // Debug
//if (Encoder == 0) memcpy(DevIO.Data, DataStream, sizeof DevIO.Data);

  int Start = 0;
  int End   = ENC_STREAM_SIZE-1;

  while (DataStream[Start] && (Start < (ENC_STREAM_SIZE-1))) { Start++; }
  while (DataStream[End  ] && (End   > 1                  )) { End--;   }
 
  // Check if detected boudaries are valid
  if ((Start < End) && (Start < 150) && (End < (ENC_STREAM_SIZE - 150)))
  {
    // The following is incorrect as the last stop can also include data bits high!!!
    // Thereby wrongly detecting the last stop bit position
    diff = End - Start;
    BitLen = (float)diff / (float)(BIT_LEN - 1);  // Last stop bit not counted
    if (BitLen > BitLenMax[Encoder]) BitLenMax[Encoder] = BitLen;

BitLen = 7.97; // was 14.07f;

//    int BitCnt = 0;
    float off = (float)Start + BitLen/2;
    int b = 0;
    for (n = 0; n < 6; n++) // Bytes are CF SF DF0 DF1 DF2 CRC
    {
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
      off += BitLen;  // Ignore start bit
      for (int m=0; m<8; m++)
      {
        Bits[b++] = DataStream[(int)off];
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
        off += BitLen;
      }
//if ((Encoder == 0) && (BitCnt<60)) DevIO.DataBits[BitCnt++] = (short)off;
      off += BitLen;  // Ignore stop bit
    }

    b = 0;
    for (n = 0; n < 6; n++)
    {
       Data = 0;
       for (int m = 0; m < 8; m++) if (Bits[b++]) Data |= (1<<m);
       EncData[Encoder+2][n] = Data;
    }
  }

  static int sub = 0;
  #define SUB_CNT 100
  if ((++sub > SUB_CNT) && 0)
  {
    if (sub > (SUB_CNT+1)) sub = 0;
    Serial.printf("Enc:%1d  S:%4u  E:%4u  df:%4u  ln:%5.2f  lnmx:%5.2f\n",
              Encoder,
              Start,
              End,
              diff,
              BitLen,
              BitLenMax[Encoder]);
  }
}

// Send command to transmit 0010 0000 01 + pause before releasing bus
void EncoderTx(int Encoder)
{
  digitalWriteFast(EncDir[Encoder], HIGH);  // set bus for master
 
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0`
  digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN);  // 1
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], LOW ); TimeWaster(TXBITLEN);  // 0
  digitalWriteFast(EncTx [Encoder], HIGH); TimeWaster(TXBITLEN);  // 1
 
  digitalWriteFast(EncDir[Encoder], LOW);  // set bus for slave
}

void TimeWaster(int repetitions)
{
  int n = 0;

  for (n = 0; n < repetitions; n++)
  {
    __asm__("nop");
  }
}
 
Are you saying you had to add the line of code below? What are the units of "BitLen"?

Code:
BitLen = 7.97; // was 14.07f;

Also, can you tell us what encoder you are using? Manufacturer and part number? I'd be curious to know what is the protocol and whether perhaps you could use one of the T4 peripherals to read the data.
 
Also, can you tell us what encoder you are using? Manufacturer and part number?
From their first post, I am guessing one of these:
And guessing not one of the "New" versions as mentioned for years...

but recently when I rebuild the code due to library upgrades
What was the changes in libraries? Could there now be functional differences?

Also if you updated to the most recent release of Teensyduino, that includes a whole new compiler.
From what version did you upgrade from?

With new releases/compilers and the like, I can easily imagine timings might change and relying on timing code using
Code:
void TimeWaster(int repetitions)
{
  int n = 0;

  for (n = 0; n < repetitions; n++)
  {
    __asm__("nop");
  }
}

My guess could always be problematic. Do you have something like a logic Analyzer you can hook up to see what is going on?


(apparently using FlexIO it might be possible, but that's a major PCB rework
If these are the pins you are using:
Code:
const int EncDir[2] = {41, 40};
const int EncTx [2] = {17, 15};
const int EncRx [2] = {16, 14};
I believe all of these are on FlexIO 3:
1783444446875.png

Which works, the only downside with FlexIO3 is it does not support DMA...
 
What was the changes in libraries? Could there now be functional differences?

Also if you updated to the most recent release of Teensyduino, that includes a whole new compiler.
From what version did you upgrade from?

I think when he says "libraries", he means Teensy4 core. There don't seem to be any libraries being used, even those included with TeensyDuino.

With new releases/compilers and the like, I can easily imagine timings might change and relying on timing code using
Code:
void TimeWaster(int repetitions)
{
  int n = 0;

  for (n = 0; n < repetitions; n++)
  {
    __asm__("nop");
  }
}

My guess could always be problematic. Do you have something like a logic Analyzer you can hook up to see what is going on?

Yes, if he's going to use a spin delay for timing, he should use delayNanoseconds().
 
Last edited:
1) Apologies, 20 bit - TS5648 N100, 10 bit per data, below.
2) Of course UART is the proper solution but I was not able to get 2.5 M without compromising other functionality.
3) I previously had it finding the first and last bits but it's commented out as the trailing CRC can have trail bits high messing with the detection (although there is a max tracking), finally hard coding it to 14.07 was steady for a few years (and many builds) until I fired up the Arduino IDE over the weekend and it did its usual upgrading of I know what not, then the bit length had dropped to 8. I do have backups and a second observatory to compare the build environment.
4) I do have a logic analyser and it served a lot in the early work and was intending on using it and an oscilloscope.
5) Teensyduino 1.62.0, IDE 2.3.10
6) I tried delayNanoseconds() and I was not able to have it as consistent as the time waster. I'll need to 'tune' it due to the granularity difference - it's a clear night so the two observatories are running, can't work the code until tomorrow.
7) I'll look into FlexIO 3 - but there is a lot more code handling 6 R/C servos, 4 steppers, 2 DC motors, 3 ABI encoders, serial barcode, ethernet, I2C temperature sensor, vacuum pressure ADC, etc - most of the I/O is already used/assigned/configured (timers, etc).
8) I did say I knew this method did not ensure certainty over time, but it was rock steady for a few years so I left it alone. My original intention was serial, but it was not reliable enough for two encoders, 50/sec each - if flexIO 3 is possible, I'll happily switch.

Thank you all.
1783466562892.png
 
I tested delayNanoseconds() and found it problematic. So I timed it and two of my timewasters and found the delayNanoseconds() has an overhead that is never handled and is quantized at small values - in particular the bit collection code of mine requires very fast sampling which delayNanoseconds() cannot provide. The table below feeds the first column into the three APIs, which is cycles for the first two, and nanoseconds for the last. The log-log plot shows all are non linear at low values, but delayNanosecond is the worst, and the best is the new TimeWaster2 that should not be influenced by compiler changes and dependencies (?)

Code:
Benchmarking TW(), TW2(), delayNanoseconds()
units   TW_ns     TW2_ns    NS_ns
1       38.33     28.33     31.67  
2       23.33     40.00     31.67  
3       31.67     51.67     31.67  
4       26.67     51.67     31.67  
5       46.67     50.00     31.67  
6       50.00     73.33     31.67  
7       53.33     80.00     31.67  
8       56.67     86.67     31.67  
9       60.00     93.33     31.67  
10      61.67     100.00    31.67  
20      95.00     186.67    43.33  
30      128.33    266.67    55.00  
40      161.67    353.33    53.33  
50      195.00    433.33    76.67  
60      228.33    520.00    76.67  
70      261.67    600.00    90.00  
80      295.00    686.67    96.67  
90      328.33    766.67    110.00  
100     366.67    853.33    116.67  
200     700.00    1686.67   216.67  
300     1033.33   2520.00   316.67  
400     1366.67   3353.33   416.67  
500     1700.00   4186.67   516.67  
600     2033.33   5020.00   616.67  
700     2366.67   5853.33   716.67  
800     2700.00   6686.67   816.67  
900     3033.33   7520.00   916.67  
1000    3366.67   8353.33   1016.67

1783797464012.png

C:
void TimeWaster(int repetitions)
{
  int n = 0;

  for (n = 0; n < repetitions; n++)
  {
    __asm__("nop");
  }
}

static inline void TimeWaster2(uint32_t repetitions)
{
  uint32_t cycles = repetitions * g_cyclesPerIteration;
  uint32_t start  = ARM_DWT_CYCCNT;

  while ((ARM_DWT_CYCCNT - start) < cycles)
  {
    // busy wait
  }
}

void benchmarkTimeWasters()
{
    Serial.println("Benchmarking TW(), TW2(), delayNanoseconds()");
    Serial.println("units   TW_ns     TW2_ns    NS_ns");

    const float nsPerCycle = 1e9f / (float)F_CPU_ACTUAL;

    auto measure_ns = [&](auto func, uint32_t units) {
        uint32_t start = ARM_DWT_CYCCNT;
        func(units);
        uint32_t end = ARM_DWT_CYCCNT;
        return (end - start) * nsPerCycle;
    };

    // ---------------------------------------------------------
    // 1 to 9
    // ---------------------------------------------------------
    for (uint32_t units = 1; units <= 9; units++)
    {
        float tw_ns  = measure_ns(TimeWaster, units);
        float tw2_ns = measure_ns(TimeWaster2, units);
        float ns_ns  = measure_ns(delayNanoseconds, units);

        char buf[128];
        snprintf(buf, sizeof(buf),
                 "%-7lu %-9.2f %-9.2f %-9.2f",
                 (unsigned long)units,
                 tw_ns, tw2_ns, ns_ns);
        Serial.println(buf);
    }

    // ---------------------------------------------------------
    // 10 to 90 by 10s
    // ---------------------------------------------------------
    for (uint32_t units = 10; units <= 90; units += 10)
    {
        float tw_ns  = measure_ns(TimeWaster, units);
        float tw2_ns = measure_ns(TimeWaster2, units);
        float ns_ns  = measure_ns(delayNanoseconds, units);

        char buf[128];
        snprintf(buf, sizeof(buf),
                 "%-7lu %-9.2f %-9.2f %-9.2f",
                 (unsigned long)units,
                 tw_ns, tw2_ns, ns_ns);
        Serial.println(buf);
    }

    // ---------------------------------------------------------
    // 100 to 1000 by 100s
    // ---------------------------------------------------------
    for (uint32_t units = 100; units <= 1000; units += 100)
    {
        float tw_ns  = measure_ns(TimeWaster, units);
        float tw2_ns = measure_ns(TimeWaster2, units);
        float ns_ns  = measure_ns(delayNanoseconds, units);

        char buf[128];
        snprintf(buf, sizeof(buf),
                 "%-7lu %-9.2f %-9.2f %-9.2f",
                 (unsigned long)units,
                 tw_ns, tw2_ns, ns_ns);
        Serial.println(buf);
    }
}
 
Last edited:
It's possible that since the original code used an __asm__ statement with no apparent side-effects (no registers or memory marked as outputs) and no volatile specifier, the new compiler simply decided to omit it.
 
It's possible that since the original code used an __asm__ statement with no apparent side-effects (no registers or memory marked as outputs) and no volatile specifier, the new compiler simply decided to omit it.
But then the message length would have lengthened, not shortened - the time waster seems to have slowed down. Could it be the opposite, that it used to toss the NOP whereas now it isn't?
 
Last edited:
This should be repeatable and about as good as you can do.

Code:
static inline void delayCycles(uint32_t cycles)
{
    uint32_t begin = ARM_DWT_CYCCNT;
    while (ARM_DWT_CYCCNT - begin < cycles) ; // wait
}
 
But then the message length would have lengthened, not shortened - the time waster seems to have slowed down. Could it be the opposite, that it used to toss the NOP whereas now it isn't?
Either case is possible, the compiler can omit any statement it thinks doesn't have any effect unless it's marked as volatile.
 
The compiler usually generates very different busy looping code depending on whether the input (desired busy time) is a compile time constant. For very short delays, whether the code is used inline or compiled as a separate function also matters.

Testing with a loop gives the compiler a difficult optimization decision. It might "unroll" the loop (maybe if using "Fastest" optimization which is -O3) and build many copies of the loop's code, or it could implement the loop as described, where the function call will be made with a runtime value rather than a compile time constant. Likewise, calling through a function pointer might be optimized away, or might be another factor that discourages the compiler from calling the busy function with compile time input. This sort of function call also might have overhead than a normal fixed location function call.

You might consider using special compiler features like the always-inline attribute and __builtin_constant_p macro in your busy function. Or if you prefer C++ syntax, maybe look at "if constexpr". And of the sake of a useful test, maybe consider copy and paste of the code many times rather than a loop. It's not "elegant" code, but then a busy loop isn't either...
 
Last edited:
Thanks all for the replies. I'll next try to FlexIO3 2.5mbps serial - I failed 3 years ago when I first tried, and have mostly forgotten the problems I had. There appears to be no conflict with the 33 pins I am using and FlexIO3 (see below).

On another note, one of the Teensy 4.1 when I power up the observatory, fails registration with Windows, until I cycle the board local power - I've cut the trace between Vusb and Vin. All 4 Teensy 4.1 have been fine for a few years, but this began a week ago. Know that last summer lightning struck a tree 40 feet from the domes and obliterated 7000$ of electronics including a couple of Teensys. Maybe a latent failure?

✔ Encoder pins:​

14, 15, 16, 17, 40, 41, 30, 31 → 8

✔ Programming pins:​

34, 35 → 2

✔ Fan:​

29 → 1

✔ AIP:​

22 → 1

✔ Motors:​

10, 12, 28, 18, 5, 24, 36, 19, 8, 26, 20 → 11

✔ RS485:​

0, 1, 11 → 3

✔ Barcode:​

7 → 1
 
Back
Top