Go Down

Topic: Class to simplify "blink without delay" (Read 1 time) previous topic - next topic

Nick Gammon

May 11, 2011, 01:02 am Last Edit: May 11, 2011, 12:10 pm by Nick Gammon Reason: 1
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: [Select]
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: [Select]
// 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: [Select]
void loop() {
static Elapsed t2, t3;


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

Code: [Select]
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: [Select]
// 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 *) {
  };
}
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

#1
May 11, 2011, 03:55 am Last Edit: May 11, 2011, 10:54 pm by Nick Gammon Reason: 1
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: [Select]
#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: [Select]
#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

Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

theepdinker

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"?
Occasional activity,
sporadic progress

liudr

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".

Senso

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

Nick Gammon

The original uses 4 bytes for previousMillis anyway, so I don't know that you need to worry about that too much.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon


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.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PaulS

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?

Nick Gammon


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".
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

#9
May 11, 2011, 12:15 pm Last Edit: May 11, 2011, 01:05 pm by Nick Gammon Reason: 1

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: [Select]
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.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Yot

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.

Fletcher Chr

#11
May 11, 2011, 01:51 pm Last Edit: May 11, 2011, 01:57 pm by Fletcher Chr Reason: 1
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: [Select]
// 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


Nick Gammon

#12
May 11, 2011, 10:56 pm Last Edit: May 11, 2011, 11:30 pm by Nick Gammon Reason: 1
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.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up