Skip to content
George Wilkins edited this page Aug 24, 2024 · 7 revisions

Teensyduino provides the Arduino delay functions delay(uint32_t ms), delayMicroseconds(uint32_t mus). Additionally, you can use delayNanoseconds(uint32_t ns). All those functions block the execution of the sketch for the specified period.

To see how that works, we can have a look at the usual heartbeat code

void loop(){
  digitalToggleFast(LED_BUILTIN);
  delay(250);  // 250ms
}

After toggling the led we simply call delay(250) to pause execution for 250ms and get a 2Hz blinking LED. Of course, instead of delay(250) you can use delayMicroseconds(250'000) to get the same effect.

void loop()
{
  digitalToggleFast(LED_BUILTIN);
  delayMicroseconds(250'000);  // 250'000µs = 250ms
}

DelayNanoseconds does the obvious and comes in handy if you need to wait for very short times. This is often the case if you access external hardware. Here an example showing how to readout a 74165 (parallel in / serial out) shift register.

//...
digitalWriteFast(LD, LOW);           // load pin requires a low pulse (>100 ns) to
delayNanoseconds(100);               // store the current values at the data pins A-E
digitalWriteFast(LD, HIGH);          // in the shift register

int value = digitalReadFast(QH);     // read the first data bit
// do something with the bit...

for (unsigned i = 1; i < 8; i++)     // shift out the the rest of the data
{
    digitalWriteFast(CLK, HIGH);     // shift starts with the rising edge of clk
    delayNanoseconds(50);            // the 74165 needs some time for the shifting

    int value = digitalReadFast(QH); // read the next data bit

    digitalWriteFast(CLK, LOW);      // reset the clock pin and make sure
    delay50ns();                     // that it stays low for at least 50ns

    // do something with the bit...
}
//...

How to keep background tasks alive

Teensyduino implements a yield() function which handles stuff like SerialEvents and much more in the background. In principle yield is called automatically after each iteration of loop. If your code blocks loop for e.g. 10s, yield wouldn't be called for 10s which might mess up those backup tasks. To avoid this delay(unsigned ms) calls yield while it spins.

However, delayMicroseconds and delayNanoseconds do not call yield. Therefore, try to avoid using delayMicroseconds for long delays if you rely on background tasks being run from yield().

//...
// delayMicroseconds(500'000);  // not good, doesn't call yield()
delay(500);                     // good, calls yield() while it spins
//...

Also, if your own code takes significant time it might be a good idea to manually call yield() between iterations to keep the background tasks alive:

void someFunction()
{
    for (int i = 0; i < 10000; i++)
    {
        // do something important here...

        yield();
    }
}

Delay without blocking

The concept of delaying/blocking code execution is very simple but it generates issues whenever other things should happen while the delay functions spin. The standard example for this is blinking two LEDs with different frequencies. Obviously this can not be achieved by simple delaying.

Instead, we can pre calculate the time when we need to toggle the LED. In loop we just check if reached that time. If we did, we toggle the LED and calculate the next time to toggle. If not we continue with other tasks.

uint32_t lastTime = 0;
uint32_t period = 250; //ms

void loop()
{
    if(millis() - lastTime >= period ) // if it is time to toggle
    {
        digitalToggle(LED_BUILTIN);
        lastTime = millis();           // calculate the next time for toggling
    }

    // do something else
}

Using this pattern it is easy to have second LED blinking at a different frequency:

constexpr uint32_t led_1 = 0;         // led1 on pin 0
constexpr uint32_t period_1 = 250;
uint32_t lastTime_1 = 0;

constexpr uint32_t led_2 = 1;          // led2 on pin 1
constexpr uint32_t period_2 = 350;
uint32_t lastTime_2 = 0;

void loop()
{
    uint32_t currentTime = millis();

    if(currentTime - lastTime_1 > period_1)
    {
        digitalToggle(led_1);
        lastTime_1 = currentTime;
    }

    if(currentTime - lastTime_2 > period_2 )
    {
        digitalToggle(led_2);
        lastTime_2 = currentTime;
    }
    // do something else
}

Using elapsedMillis and elapsedMicros

Teensyduino provides the useful 'autoincrementing' types elapsedMillis and elapsedMicros. The value of variables of this type increments automatically which makes them a perfect fit for the pattern we used above. Using elapsedMillis you can simply say:

constexpr uint32_t led_1 = 0, led_2 = 1;
constexpr uint32_t period_1 = 250, period_2 = 350;
elapsedMillis stopwatch_1, stopwatch_2;

void loop(){
    if (stopwatch_1 >= period_1)
    {
        digitalToggle(led_1);
        stopwatch_1 = 0;
    }

    if (stopwatch_2 > period_2)
    {
        digitalToggle(led_2);
        stopwatch_2 = 0;  // *Note below
    }
    // do something else
}

*Note: Sometimes "stopwatch_2 = 0;" is the right thing. Other times (depending on the task) "stopwatch_2 -= period_2;" gives the desired results

Clone this wiki locally