Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 6 of 6

Thread: digitalWriteFast() unexpected behavior

  1. #1

    digitalWriteFast() unexpected behavior

    Greetings-

    I'd like to toggle a pin a bit faster than the ~ 2 MHz I get with successive digitalWrite() calls in a for() loop, to get a specific-length
    burst out quickly.

    I stumbled on the function digitalWriteFast() in some other threads, and it compiles without warnings, but it doesn't appear to work (at least not in what I'd consider a rational way).

    If I substitute digitalWrite() inside the for() loop in the attached code, I get exactly the specified number of pulses a little
    over 500ns apart (mostly; a few pulses per thousand are delayed a Ás or two, but there's always the correct total number).

    With digitalWriteFast() as shown, I get from 0 to 10 pulses, each 750 or 1200 ns long, at seemingly random intervals each time the loop executes.

    Inserting a Ás delay between the digitalWriteFast() calls does not cure the problem.

    Changing the *first* call to a plain digitalWrite() results in a larger, but still random number of pulses less than requested.

    Changing the *second* (LOW) call to plain digitalWrite() completes the specified number, but pulse time is only slightly faster than with both plain calls.

    I could not easily find 'official' docs on digitalWriteFast(), maybe it's no longer supported? Sorry, I'm new to this!
    Any guidance appreciated,

    zike

    Code:
    #include <TimeLib.h>
    #define ledPin 13               // to counter input
    #define resetPin 37             // to counter reset 
    
    elapsedMillis r = 0;            // update timer
    elapsedMicros w = 0;
    
    void setup() {
      Serial.begin(115200);
      pinMode(resetPin, OUTPUT);
      pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
      long nout = 235959;
      if (r > 1000) {
        r = 0;                          // reset update timer
        w = 0;
        digitalWrite(resetPin,  HIGH);  // send reset pulse to counter
        digitalWrite(resetPin,  LOW);
        while(r<30){} ;                 // delay to re-arm counter after reset pulse
        for (long p = 0; p < nout; p++) {
          digitalWriteFast(ledPin, HIGH);
          digitalWriteFast(ledPin, LOW);
        }
        Serial.println(w/1000);
      }
    }
    (using Teensy 3.5, Arduino 1.8.2, Teensyduino 1.40)

  2. #2
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    1,642
    What are you using to measure 10ns pulses?
    The forum has several threads on digitalWriteFast(),
    https://forum.pjrc.com/threads/41874...llator-example)

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,679
    You need to turn off slew rate limiting, which is on by default.

    This thread has details.

    https://forum.pjrc.com/threads/41874...l=1#post132363

  4. #4
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,066
    I experienced a similar problem some time ago. digitalWriteFast() is basically translated into just a write in the Port register. The compiler sees that you are writing a 1 and then a 0 to the same place in the next cycle and I suspect that it optimizes this "nonsense" (in his eyes) away. I remember that disabling the Fast and LTO optimizations solved that problem.

    You might also try to do a digitalReadFast in-between, so that the compiler understands that you are doing something useful.

    Code:
    digitalWriteFast(ledPin, HIGH);
    bool z = digitalReadFast(ledPin); // assign the read value to a variable
    digitalWriteFast(ledPin, (z?!z:z)); // use the variable to not to be optimized away

  5. #5
    Quote Originally Posted by PaulStoffregen View Post
    You need to turn off slew rate limiting, which is on by default.
    [/url]
    Thanks Paul, that indeed did the trick, in particular this line
    Code:
    CORE_PIN14_CONFIG = PORT_PCR_MUX(1); // no slew rate limit
    after the pinMode() declaration. Doing this, I get nice 10ns pulses at 20MHz. I suppose this strange throttling action is some kind of EMC measure? Very annoying.

    On deeper investigation, without the switch the teensy was just putting out little 50mV runts buried in the noise; the chaotic behavior reported above was just due to unrelated processor interrupts, which occasionally allowed a runt to 'grow up.' I verified this by setting noInterrupts(), which only leaves the uncountable runts.

    @ manitou -- Tx, just using an oscilloscope and frequency counter, nothing special. BTW before posting I did read many of the 455 (!!) forum threads including the term "digitalWriteFast()"; and googled it outside, as well; but honestly, even if I did read every message, I may have missed the magic sauce buried in message #18 of the 74th thread ...

    @Theremingenieur -- As noted, Paul's suggestion has it working without this. By whatever lucky accident my compiler was not quite so smart. That said, a certain antique Nixie-tube counter I want to use (for artistic reasons) can't quite do 20 MHz; so I used your trick of dummy digitalReadFast() reads (two of them) to slow it down to about 12 MHz, and now everyone is happy. Thanks!

    Which raises the question: has anyone considered providing a delayNanoseconds() function?

    Cheers,

    zike

  6. #6
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    1,642
    Quote Originally Posted by zike View Post

    Which raises the question: has anyone considered providing a delayNanoseconds() function?

    Cheers,

    zike
    https://forum.pjrc.com/threads/29105...-Second-Pulses

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •