Delaying - TeensyUser/doc GitHub Wiki
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