Pages: [1]   Go Down
Author Topic: Class to simplify "blink without delay"  (Read 1794 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

One thing that seems to trip up beginners to the Arduino is the idea of implementing delays without using delay(). Hence the "blink without delay" example sketch. But that sketch is a bit confusing. You have to set up a "start" variable, assign the time to it, check it by doing subtraction rather than addition, and so on. And it gets more confusing if you want two time intervals.

The "Elapsed" class below tries to simplify that. The class has a couple of member variables (startus and startms) which remember when you last "reset the timer". Then you simply call intervalMs (for milliseconds) or intervalUs (for microseconds) to see if your required time period is up. When it is, you can do something, like flash a LED. Then you can reset the timer if you want to.

Example snippet:

Code:
if (t2.intervalMs () > 200)   // time up?
    {
    digitalWrite(2, !digitalRead (2));   // toggle the LED
    t2.reset ();   // reset the timer
    }  // end if time up

Class and example code below:

Code:
// Elapsed class
// Author: Nick Gammon
// Date: 11th May 2011
class Elapsed
{
  unsigned long startus, startms;

public:

  // constructor resets time
  Elapsed () {
    reset ();
  };

  // reset time to now
  void reset () {
    startus = micros ();
    startms = millis ();
  };

  // return Elapsed time in milliseconds
  unsigned long intervalMs () {
    return millis () - startms;
  };

  // return Elapsed time in microseconds
  unsigned long intervalUs () {
    return micros () - startus;
  };

};    // end of class Elapsed

void setup()
  {               
  pinMode (2, OUTPUT);     
  pinMode (3, OUTPUT);     
  }  // end of setup

static Elapsed t2, t3;

void loop() {

  if (t2.intervalMs () > 200)
    {
    digitalWrite(2, !digitalRead (2));
    t2.reset ();
    }

 if (t3.intervalMs () > 500)
    {
    digitalWrite(3, !digitalRead (3));
    t3.reset ();
    }
 
}  // end of loop

The example code flashes two LEDs at different intervals. The variables t2 (for pin 2) and t3 (for pin 3) are used to see if the time is up to toggle the LED state.

Note that the millisecond interval wraps around after about 50 days, and the microsecond interval wraps around after about 70 minutes. The wrap-around itself is handled correctly, but if you need to time an interval longer than 70 minutes you would need to check intervalMs rather than intervalUs.

As with millis() and micros() if you turn off interrupts the times may be out.

Warning: If you try to move the variables inside a function you will probably need to make them static (or they will reset each time the function is entered), like this:

Code:
void loop() {
static Elapsed t2, t3;

However if you do, you will probably get these compiler errors:

Code:
Elapsed_class_test.cpp.o: In function `loop':
Elapsed_class_test.cpp:62: undefined reference to `__cxa_guard_acquire'
Elapsed_class_test.cpp:62: undefined reference to `__cxa_guard_release'
Elapsed_class_test.cpp:62: undefined reference to `__cxa_guard_acquire'
Elapsed_class_test.cpp:62: undefined reference to `__cxa_guard_release'

To fix that you need to provide these missing functions, as follows (put this code near the start of your sketch):

Code:
// this blathering on is in case you want to make the class a static variable in a function
extern "C" {
  __extension__ typedef int __guard __attribute__((mode (__DI__)));
  int __cxa_guard_acquire(__guard *g) {
    return !*(char *)(g);
  };
  void __cxa_guard_release (__guard *g) {
    *(char *)g = 1;
  };
  void __cxa_guard_abort (__guard *) {
  };
}
« Last Edit: May 11, 2011, 05:10:33 am by Nick Gammon » Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

This class is now incorporated into a library which you can download from:

http://www.gammon.com.au/Arduino/Elapsed.zip  (4 Kb)

Now the example can be simplified to:

Code:
#include <Elapsed.h>

void setup()
  {                
  pinMode (2, OUTPUT);    
  pinMode (3, OUTPUT);    
  }  // end of setup

static Elapsed t2, t3;

void loop() {

  if (t2.intervalMs () > 200)
    {
    digitalWrite(2, !digitalRead (2));
    t2.reset ();
    }

 if (t3.intervalMs () > 500)
    {
    digitalWrite(3, !digitalRead (3));
    t3.reset ();
    }
 
}  // end of loop

Another, simpler, example:


Code:
#include <Elapsed.h>

void setup()
  {                
  Serial.begin (115200);  
  }  // end of setup

static Elapsed time;

void loop() {
  // if time is up, print "tick" and reset timer
  if (time.intervalMs () > 500)
    {
    Serial.println ("tick");
    time.reset ();
    }
}  // end of loop
« Last Edit: May 11, 2011, 03:54:33 pm by Nick Gammon » Logged


0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 61
Occasional activity, sporadic progress
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Neat illustration of classes for a newby like me.

How did you ever track down the solution to an error message like "__cxa_guard_acquire"?
Logged

Occasional activity,
sporadic progress

Central MN, USA
Offline Offline
Tesla Member
***
Karma: 75
Posts: 7267
Phi_prompt, phi_interfaces, phi-2 shields, phi-panels
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The name "blink without delay" itself is confusing. How can you blink with delaying? You will be always on!!

I would start with finding a new name. Using class is fine but newbies likely don't know what classes are about.

I would make an example of "How delay() works".
Logged


Portugal
Offline Offline
God Member
*****
Karma: 6
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, it will only eat 8Bytes of RAM for each new method or wathever you call to creating a new thing, it could be worse, but it seems an unnecessary waste of ram
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The original uses 4 bytes for previousMillis anyway, so I don't know that you need to worry about that too much.
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

How did you ever track down the solution to an error message like "__cxa_guard_acquire"?

Google was my friend there. Someone on one of the AVR forums worked out how to do it.
Logged


Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 642
Posts: 50366
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice job there, and a good explanation.

But...

Class names typically start with an upper case letter - Serial, Ethernet, Client, Server, LiquidCrystal, etc. Yours should, too.

Why did you declare the global instances static?
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The name "blink without delay" itself is confusing. How can you blink with delaying? You will be always on!!

It is really "how to blink without using delay ()".

Some sort of delay is required to make things blink, but the idea I'm trying to convey is "use a timer interval, not the delay() function".
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice job there, and a good explanation.

But...

Class names typically start with an upper case letter - Serial, Ethernet, Client, Server, LiquidCrystal, etc. Yours should, too.

I have modified the original post to incorporate your suggestion (ie. changed elapsed to Elapsed). Also the downloadable library has been amended. May as well be consistent, eh?

Quote
Why did you declare the global instances static?

I know it's not necessary, but does no harm. The real reason was that if you copied and pasted it into a function, you retain the "static" part. So if you copy it into "loop" it won't really work unless it is static.

I'm inclined to prefer this version:

Code:
void loop() {
static Elapsed t2, t3;
...

The reason is that the scope of the variable is inside loop, where it belongs. But the lifetime is global, so it continues to accrue elapsed time, even after you leave loop and go back into it. But without the static keyword, it won't behave as expected.
« Last Edit: May 11, 2011, 06:05:54 am by Nick Gammon » Logged


Global Moderator
The Netherlands
Offline Offline
Sr. Member
*****
Karma: 1
Posts: 287
don't panic...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

My only complaint for this class is that it hides one of the most useful programming techniques for use on a micro processor. Unfortunately a lot of users think that a class or library is a substitute for reading or learning how something works. I do realize that that is also one of Arduino's goals, keep it simple for beginners.

Thanks for your contribution,
Jeroen.
Logged

0
Offline Offline
Full Member
***
Karma: 2
Posts: 156
It was all digital
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Nick

First of all - nice and simple class.

Quote
One thing that seems to trip up beginners to the Arduino is the idea of implementing delays without using delay().

I think one of the main issues with going from blink with delay() to blink without delay() is that the mindset of the beginner has to be changed.

Blink with delay():
You turn on the light, you waite, you turn off the light, you waite. The loop() executes once. Plain simple and most beginners get it fast.

Blink without delay():
Change of mindset - the loop() executes alot of times every second and we do not want the light to go on/off each time it loops. Turning light on/off is done by comparing time.

I think a destinction beween the two should be made verry clear in the preface to the Blink without delay() example.

Comments to your class:
How about a functional overview in the header:
Code:
// reset();    Reset time
// intervalMs();   Return numbers of milliseconds since last reset
// intervalUs();   Return the numbers of microseconds since last reset

I think the example is a bit too complex for a beginner. I would have made one with with Serial.println, one every second and one every 1.3 sec. The beginner does not need to fittle with diodes and recistors to see the example working.

Jyst my two cent.

-Fletcher

« Last Edit: May 11, 2011, 06:57:20 am by Fletcher Chr » Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 503
Posts: 19090
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I agree about the mindset, and one could blather on at some length about the difference between "blocking" and "non-blocking" code which is fundamentally what the issue is.

I've amended an earlier post to incorporate a simpler example. Also added extra example to downloadable library.
« Last Edit: May 11, 2011, 04:30:09 pm by Nick Gammon » Logged


Pages: [1]   Go Up
Jump to: