Teensy 4.1 digitalWrite, digitalWriteFast Problem

Teensy 4.1 digitalWrite, digitalWriteFast Problem

[Environment]
1. I want to create a 2MHz clock.
2. The signals of 'CLOCK_H' and 'CLOCK_L' are inverted.

[Problem]
1. The waveform is broken.

What is the reason?


fig.jpg


Green Waveform: CLOCK_H ~ CLOCK_L Voltage


Code:
#define CLOCK_H   1
#define CLOCK_L   2


volatile long TIMER_CLOCK1 = 0;
const volatile long PERIOD_1 = 100;
long delay_value_ns1=180;
long delay_value_ns2=180;

void setup() {
 pinMode(CLOCK_H,OUTPUT);  pinMode(CLOCK_L,OUTPUT);
 Serial.begin(115200);
 digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
}


void loop() {
 
  if(TIMER_CLOCK1 < millis()){
    TIMER_CLOCK1 = millis() + PERIOD_1;
    boolean f1 = true;
    for (int i = 0; i <28; i++) {
      if(f1){
        digitalWriteFast(CLOCK_H, HIGH); digitalWriteFast(CLOCK_L, LOW);
        f1=false;
        delayNanoseconds(delay_value_ns1);
      }else{
        digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
        f1=true;
        delayNanoseconds(delay_value_ns2);
      }        
    }
    digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
   
  }


}



Teensy4.1 seems to have a digitalWriteFast, digitalWrite problem.
Is there a solution?
 
Does it look broken when run with a longer delay? Just to confirm the scope connects.

What is the upper non-Green line showing

Replace the WriteFast with ToggleFast after setting them out of sync like at the end of the If()

Toggle works with no delays - if that doesn't show there are long leads or bad GND perhaps to the scope?
 
@defragster
Thank you for your answer.

[The version I used is 1.56.]

1. The different colored waveforms were created using 'Teensy3.5'.
(The delay values are different.)

2. I'll test it with 'digitalToggleFast'.
Thank you for your answer.

3. When I use 'digitalWrite' and 'digitalWriteFast', the signal seems unstable.
Is this how it should be?
 
@jmarsh
Thank you for your answer.

It's a communication protocol between each other. It's already set.

@defragster, @jmarsh
1. It seems possible to create a 'clock' through 'ToggleFast'.
2. At this time, you need to judge and give a signal based on each data value. Therefore, is there anything that can replace 'digitalWriteFast'?


Code:
#define CLOCK_H   1
#define CLOCK_L   2
#define DATA_H    3
#define DATA_L    4
int DATA[10] = {1,0,0,0,1, 1,0,0,0,1};
volatile long TIMER_CLOCK1 = 0;
const volatile long PERIOD_1 = 100;
long delay_value_ns1=180;
long delay_value_ns2=180;


void setup() {
  pinMode(CLOCK_H,OUTPUT);  pinMode(CLOCK_L,OUTPUT);
  Serial.begin(115200);
  digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
  digitalWriteFast(DATA_H, LOW);  digitalWriteFast(DATA_L, HIGH);
}

void loop() {

  
  if(TIMER_CLOCK1 < millis()){
    TIMER_CLOCK1 = millis() + PERIOD_1;
    boolean f1 = true;

    
    for (int i = 0; i <10; i++) {
      if(f1){

        
        digitalToggleFast(CLOCK_H); digitalToggleFast(CLOCK_L);
        f1=false;
        if(DATA[i] == 1) {digitalWriteFast(DATA_H, HIGH); digitalWriteFast(DATA_L, LOW); }  //<- HERE
                     else{digitalWriteFast(DATA_H, LOW); digitalWriteFast(DATA_L, HIGH); } //<- HERE
        delayNanoseconds(delay_value_ns1);

        
      }else{
        
        
        digitalToggleFast(CLOCK_H); digitalToggleFast(CLOCK_L);        
        f1=true;
        if(DATA[i] == 1) {digitalWriteFast(DATA_H, HIGH); digitalWriteFast(DATA_L, LOW); } //<- HERE
                     else{digitalWriteFast(DATA_H, LOW); digitalWriteFast(DATA_L, HIGH); } //<- HERE
        delayNanoseconds(delay_value_ns2);

        
      }          
    }

    
    digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
    
  }

  

}


Looking at the source code above, isn't it inevitable to use 'digitalWriteFast'?
 
CASE1 :
Code:
if(TIMER_CLOCK1 < millis()){
    TIMER_CLOCK1 = millis() + PERIOD_1;


    for (int i = 0; i <10; i++) {
        digitalToggleFast(CLOCK_H); digitalToggleFast(CLOCK_L);
        if(DATA[i] == 1) { digitalWriteFast(DATA_H, HIGH); digitalWriteFast(DATA_L, LOW); } //<- HERE
                     else{ digitalWriteFast(DATA_H, LOW); digitalWriteFast(DATA_L, HIGH); } //<- HERE
        delayNanoseconds(delay_value_ns1);    
    }
   
    digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
   
  }

CASE2:
Code:
if(TIMER_CLOCK1 < millis()){
    TIMER_CLOCK1 = millis() + PERIOD_1;


    for (int i = 0; i <10; i++) {
        digitalToggleFast(CLOCK_H); digitalToggleFast(CLOCK_L);
        if(DATA[i] == 1) {
          digitalWriteFast(DATA_H, HIGH); digitalWriteFast(DATA_L, LOW);
          __asm__ __volatile__ ("nop\n\t");
        } //<- HERE
        else{
          digitalWriteFast(DATA_H, LOW); digitalWriteFast(DATA_L, HIGH);
          __asm__ __volatile__ ("nop\n\t");
        } //<- HERE
        delayNanoseconds(delay_value_ns1);     
    }
    
    digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
    
  }
 
How are the GNDs connected ???
What is the transmission line length ??? some millimeters ?? some centimeters ??? some meters ???
If your line is long, use RS422 or RS485 line drivers. With 120ohms termination resistors and twisted pair cable, your transmission will be reliable on very long distance.
 
@Angelo

1. Thanks for the answer.

2. The length of the transmission line is in 'cm'.

3. GND is not used.

4. The frequency is around 2MHz.

- Can I use 'With 120ohms termination resistors'? Is it beneficial?

5. I feel that 'digitalWrite' and 'digitalWriteFast' do not work out of the box on Teensy 4.1.
 
5. I feel that 'digitalWrite' and 'digitalWriteFast' do not work out of the box on Teensy 4.1.
If they didn't the default Blink sketch (that turns the onboard LED on and off) wouldn't even work.

You are not giving us proper information here, which makes it very difficult for anyone to help you. You need to give a proper description of what you're trying to build and how you've verified that it isn't working rather than just one vague scope image.
 
I ran your program from msg #1 on a Teensy 3.5. Here are the waveforms my oscilloscope sees.

file.png


Here's a photo of the test setup on my workbench, so you can see how I tested (about as simple as possible)

1732909245978.png
 
I ran your program from msg #1 on a Teensy 3.5. Here are the waveforms my oscilloscope sees.
From what I gather, they're trying to generate some sort of TMDS signal using two GPIOs. The pink trace in the first picture is generated using a T3.5 but using the same code on a T4.1 is failing and generating the green trace.
 
I repeated this test with Teensy 4.1. Same setup, same code from msg #1.

You can see the frequency is slightly higher, due to Teensy 4.1 having much faster speed for the slight software overhead while bit-banding this waveform.

file2.png


Again, here's a photo so you can see how I tested. Simple as possible, same as the test with Teensy 3.5.

1732909769312.png
 
I think the original image shows the 'scope's channel difference (Math -), which will of course be spikey as digitalWriteFast is not instantaneous. You need to simulataneously write two GPIO's with a single register access to get properly synchronized edges.
 
I think the original image shows the 'scope's channel difference (Math -),

Teensy 4.1 is still connected and running the code from msg #1, so I turned on my scope's math function. Here's the result.

file3.png



So far I made all these measurements with my scope's bandwidth limiting turned on. As you can see in the photos of msg #10 and #13, I'm using ordinary ground lead clips which cause overshoot and ringing if my scope's full 200 MHz bandwidth is enabled.

For the sake of testing, I turned off bandwidth limit, but didn't change the test setup.... sorry, but fiddling with setting up short ground wires for good quality 200 MHz measurement is just more work than I'm willing to do right now.

Here is the result zoomed in to the fastest time scale my scope supports. Indeed the yellow trace rises before the green trace falls. But they're only about 2ns apart, which is also about the speed of the rise/fall time (as observable with my scope). With "only" 200 MHz bandwidth, the effect is a pretty smooth transition. Maybe something ugly might be observable with a higher bandwidth scope and more work on the ground connections? Perhaps the math function might look like a stair-step with brief pause or less steep rise half way? But I don't believe it could possibly look anything like the screenshot in msg #1 if the oscilloscope & probes are working properly.

file4.png
 
For the sake of experimentation, I tried adding a short delay. It turns out 6 NOP instructions are needed to get the math waveform to "look bad" when viewed on my 200 MHz bandwidth scope. (higher bandwidth might be able to observe this with less delay)

file5.png


This is the exact code used on Teensy 4.1 for this measurement:

Code:
#define CLOCK_H   1
#define CLOCK_L   2


volatile long TIMER_CLOCK1 = 0;
const volatile long PERIOD_1 = 100;
long delay_value_ns1=180;
long delay_value_ns2=180;

void setup() {
 pinMode(CLOCK_H,OUTPUT);  pinMode(CLOCK_L,OUTPUT);
 Serial.begin(115200);
 digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
}

#define SHORTDELAY() asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");

void loop() {
 
  if(TIMER_CLOCK1 < millis()){
    TIMER_CLOCK1 = millis() + PERIOD_1;
    boolean f1 = true;
    for (int i = 0; i <28; i++) {
      if(f1){
        digitalWriteFast(CLOCK_H, HIGH); SHORTDELAY(); digitalWriteFast(CLOCK_L, LOW);
        f1=false;
        delayNanoseconds(delay_value_ns1);
      }else{
        digitalWriteFast(CLOCK_H, LOW); SHORTDELAY(); digitalWriteFast(CLOCK_L, HIGH);
        f1=true;
        delayNanoseconds(delay_value_ns2);
      }       
    }
    digitalWriteFast(CLOCK_H, LOW); digitalWriteFast(CLOCK_L, HIGH);
  
  }


}

Even with this delay added, at the original zoomed out time scope math waveform's non-monotonic rise time becomes less than 1 pixel on my scope screen. Only the overshoot and ringing (due to lengthy ground clips) are easily visible.
 
@jmarsh

1. I think you're right.
When I remove the oscilloscope ground, it works fine.

2. Can I ask a question because I don't know much?
asm("nop") : What is this?
Is it like a fixed delay? I don't know much, so I'm asking.
 
Usually NOP gives 1 cycle delay. But the CPU can sometimes skip it without any delay. In other cases, a previous instructions can still be within the CPU's pipeline and cause delay, which technically isn't due to the NOP instruction but the actual CPU behavior can be quire complex.
 
Usually NOP gives 1 cycle delay. But the CPU can sometimes skip it without any delay. In other cases, a previous instructions can still be within the CPU's pipeline and cause delay, which technically isn't due to the NOP instruction but the actual CPU behavior can be quire complex.
Yes, thank you for your reply.
I really appreciate it.
Have a nice day.
 
Back
Top