Introduction: Hack the Delay() Function in Arduino

Hi. I made my ewer first program in C++ for Arduino a couple of month’s ago.
One of them commands: delay(), got me to trouble… untill I found out that the ”program” actually ”halts” during delay(), it won’t do anything else during that time???. Take a look at my site http://www.teksel.net and there the Bilgepump to see where I need them delays in the functions, (alarm delay, them blinking’s etc.)++ Then I thought about how to get rid of that annoying behavour of delay(). OK. I built a TIME-generator of my own. It’s actually a kind of a ”stop-watch”, not a real-time clock, (keep that in mind all the way). I’ll ”catch” the millis() that’s generated by the processor, ( guess it’s as a part of in the ”bootloader”?), You could as well ”catch” the micros(), but I don’t need that accuaricy in my schedule here now. Of course You can use the delay(), but I recommend to use it with short delays only. Ewen my time-generator has to be considered in the whole program timewise. It’s good with the generator when the timeframes are in seconds and up…. Actually you can build it up to allmost infinity of time. True! Just get familiar to it and build on to …weeks..months.. of delay. Just keep in mind: WHENEWER THE POWER IS CUT OFF the timers are reset , (you could take a backup ewery now and then to the flash memory), but thats another story.

The accuarity of my time-generator isn’t ”top-exact”. I tested it with a delay of 25 minutes, and the ”off” was less than 1second. You can easyly calibrate your generator. I’ll get to that sometimes later on. 1’st make your whole program to run, (consider them serialPrint’s() allso, because they allso ”halt” the program). When done, test your time-generator by putting there a ”looo..ng” delay and time it with a clock.

I have written many programs in Assambler, for another type of processor with it’s own architechture). In that assam. program I built up a time-generator of my own, and now I transcripted it to C++. In part 1. I’ll discribe the function in the assam. in some details. You can jump right on to part 2. if you don’t wanna know this assambler part, (actually you don’t need it).

Step 1: The Assambler Part (you Actually Don't Need This), It's a Reference Only

Here are the some parts for the in assam. written program, Them ”labels” and comments are mostly in Finnish, but don’t mind them, I’ll explane. The processor generates an dynamic interrupt in it’s software, (depending on the prescalers/ - counters set by you in the program). That is: where ewer you are running in your program there will be a dynamic timer IRQ .
The processor jumps to the timer interrupt routine executing that and then return and continue from where it stopped. In the setup I load the basic times for 10mS; 100mS etc. I didnt have a need of 1mS or less in this application

;*************************************************************** ;

THE MAIN PROGRAM ;

***************************************************************

ldi time10,0ah ; Here I load variables for the timer

ldi time100,0ah

ldi time1s,07h

ldi time10s,06h

ldi time1m,0ah

ldi aputime,06h

ldi pa_aika,pa ;Switch debounce-time . . .

Here’s the timer interrup itself

;=============================================================== ;

TIMER interrupt ;

===============================================================

tim_int ld save_a,a ;Save the present acku value

call aika ;CALL THE TIMER SUBROUTINE!!!!!

ldi wdr,11111110b ;Reload the prescaler

ldi tcr,pre_tcr ;Preset the register

res 7,tscr ;Zero the TMZ bit, new count

ld a,save_a ;restore accu value

reti ;return from interrupt

Here’s a part of the time-generator subroutine that is called from the timer iterrupt

;=============================================================== ;

TIMER subroutine ;

===============================================================

aika dec time10 ;YOU COME HERE EWERY 1mS

jrz sata ;Check if time10 counted to zero

jp timeend ;Not yet, jump to end

sata ldi time10,0ah ;10ms reached

dec tahti1_a ;Some routines to do in the time window

jrnz sata_a ;within the 10mS, like here I use

ld a,tahti1 ;a FLIP-FLOP function with a

com a ;200mS frequence

ld tahti1,a ;Flop

ldi tahti1_a,ta1 ;200ms

sata_a dec tahti2_a ;Another FLIP-FLOP in the window

jrnz sata_b ; but this time a 500mS

ld a,tahti2 ;Flip

com a ;

ld tahti2,a ;Flop

ldi tahti2_a,ta2 ;500ms ;

sekunti ldi time100,0ah ;1second reached reload the 100mS

dec time1s

jrz sek_a

jp timeend . .

sek10 ldi time1s,06h ;10seconds reached reload the 1second

dec time10s ;Note 1second is loaded with 6, because

jrz sek10_a ;in 1minute there are 6 of 10seconds

jp timeend sek10_a

nop ;

yksimin ldi time10s,06h ;1minute reached reload the 10seconds

timeend ret

You can load within your program where ewer a variable with some value and do the decrementing of it to zero, (or to some pre-defined value), within the time-generator routine. To decrement is much more powerful to do instead of increment.. because it’s easyer to check if a count is zero than to do a comperasion to a given value.

Step 2: The Hack of Delay() in C++

Making a TIME-Generator in C++ for ex. Arduino. (I have to explane some same things again here that was in part 1. because some people jumps ower the part 1.).
Again: this generator acts like a ”count down” clock. It seems to act wery ”odd” because it actually ”counts UP”. I’ll come to that later on. Don’t copy from my example ”right off” because I wrote the program in Finnish and then translated these parts shown here, so there may be errors? I just wanna show the ”princibles” of the concept. The basic idea is that I ”grab” the millisecond generated by the processor, at ewery ”loop around” thus giving it a name ”nyky_time” = actual time.

Here are the variables

//------------ VARIABLES (will change)---------------------

unsigned long vanha_milli; // old_time in timegen.

unsigned long nyky_milli; // actual_time = millis()

unsigned long aika_1ms = 1; // time for 1ms

unsigned long tahti_1 = 0; // Count of 1ms

unsigned long tahti_10= 0 ; // Count of 10ms

unsigned long tahti_100= 0 ;// Count of 100ms

int tahti_1s = 0; // Count of 1second

int tahti_10s = 0 ; // Count of 10seconds

int tahti_1m ; // Count of 1minute

int tahti_10m ; // Count of 10minutes

Note: because the millis() is a unsigned long, so must the actual_time, old_time and time_1ms allso to be. In the mainprogram I call the timegenerator in ewery loop. There I ”Grab” the millis() to be nyky_milli and use it in the generator

//=============================================================

void loop() {

nyky_milli = millis(); // Grab the actual time to nyky_milli

// Call subroutines

timeout_tila(); // call timeout

reset_painike(); // call reset

aikagen(); // call the timegenerator

}

//======================== END of LOOP ===========================

Here is an example of using the timer. I want the reset_painike, (it’s a switch), to be read with a conciderition of a debounce time. Here I use a debounce time of 100mS, (reset_tahti), At first I complement the input (nyky_reset, LOW to HIGH and vice vers). Because I use in the setup them switch inputs to be with ”pull-up”, and therefore an active input is LOW, howewer it’s more logical to work with logical HIGH when active.

//================================================================

void reset_painike() { // Debounce

nyky_reset = digitalRead(sw_reset); //Read the switch

nyky_reset =! nyky_reset ; // Because active is = 0, so invert it

if ((nyky_reset == HIGH ) && (reset_tahti <=1))

{reset_apu1 = HIGH ;

} else { reset_apu1 = LOW ;}

if (nyky_reset == LOW) // If NOT active then

reset_tahti = 100 ; // RELOAD THE DEBOUNCE TIME

//================================================================

Here is the actual TIME-GENERATOR

//================================================================

void aikagen() { // The base count is 1ms

if (nyky_milli - vanha_milli > (aika_1ms /100)) //New time – Old Time

{ ++tahti_1 ; } // Increment until 1ms is reached

// Count 10ms

vanha_milli = nyky_milli; // The old_time becomes the new_time

if (tahti_1 >= 10 ) // Reached 1ms?

{ tahti_1 = 0 ;

++tahti_10 ; }

// HERE I RUN A DEBOUNCE CHECK OF A SWITCH (NYKY_RESET) //

if (reset_tahti >= 1) //Note: it will still decrement when 1

--reset_tahti; //Note: the reset_tahti is decremented

// Count 100ms

if (tahti_10 >= 10) // Reached 10ms?

{ (tahti_10 = 0);

++tahti_100 ;

//HERE WE DO THE TASKS WE WANT TO DELAY IN 100mS WINDOW

//I HAVE THIS ”TIMEOUT” FUNCTION IN MY PROGRAM, AND HERE I

//”TRY” TO COUNT IT DOWN TO ZERO, AND IF IT REACHES ZERO,

//I NOTICE THAT IN THE ”TIMEOUT” ROUTINE.

//THE VARIABLE timeout_1 WILL BE RELOADED WITH A VALUE

//(WHAT SO EWER IT IS)AGAIN and AGAIN IN THE TIMEOUT ROUTINE

//UNTIL THE PROGRAM SAYS TO ”START” THE COUNT DOWN

if (timeout_1 >= 1) // Test: value setted: 25,5sek.

-- timeout_1 ;} // Measured time: 25,6sek.

//Note: the timeout_1 is decremented

// Count 1sek

if (tahti_100 >= 10) // Reached 100ms?

{ (tahti_100 = 0);

++tahti_1s ;} // Count 10sek

// if (tahti_1s >= 10)

// Reached 1sek?

// { (tahti_1s = 0);

// ++tahti_10s; }

// Count 1min

if (tahti_1s >= 60) // Reached 10sek?

{ (tahti_1s = 0);

++tahti_1m;}

// Count 10min

if (tahti_1m >= 10) // Reached 1minute?

{ (tahti_1m = 0);

++tahti_10m ;}

} // Return //============================================================

The subroutine TIMEOUT

//================================================================

void timeout_tila() {

// timeout_1 "is loaded" again when MANU is NOT "active"

if (timeout_1 >= 1 ) //Has the "Count down" in timegen. //reached zero?

{digitalWrite (led_time, LOW);No

} else { time_alarm = HIGH ; // Hälytys

bitSet ((alarm_rek),0); // Alarm 0= TIME,blinkroutine

digitalWrite (led_time, HIGH); }

}

//================================================================

I’ll attach the complete scetch Bilge_X.ino , but notice that it’s in FINNISH, hope it gives you some ideas anyway.