Pages: [1]   Go Down
Author Topic: timing without using delay  (Read 1042 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 29
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I wrote the code for a piece of art I just built in a big hurry (i.e. just hours before the gallery doors opened).  The easiest way for me to create the timings I wanted between events was with delay statements.  Trouble is I really need to implement interrupts in the code, and I can't use delay in my interrupt handling functions.  So what I am looking for is general advice in the ways one could create the timing they desire without the use of the delay command.

Thanks in advance for any advice.

-CTP
Logged

Greenwood, Indiana
Offline Offline
God Member
*****
Karma: 0
Posts: 508
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I can point here http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

You may get better results if you post your code.
Logged

If it was designed by man it can be repaired by man.

0
Offline Offline
Newbie
*
Karma: 0
Posts: 29
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

yea, but I'm looking more for how to fish than people re-writing my fish.  How's that for a mixed metaphor? :-D

It's more a generic question that's been on my mind, i.e. learning all the possible ways to create delays without using delay(), than just this one case on this one piece.

Oh and in that example you link to - it uses millis...Can't use millis with interrupts either :-(
« Last Edit: December 05, 2007, 08:30:49 pm by CTP » Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The arduino atmega chip has a mind bending number of permutations for using  timers and interrupts to create delays. The following tutorial may help you see some of the possibilities: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106

Knowing a little more about the kind of application you want would help pointing you in the right direction. If you say a little more about the functionality you require, it would be easier to provide relevant guidance.
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 106
Posts: 6373
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Can't use millis with interrupts either
Why do you think that?
Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 9
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

When you're an interrupt handler (i.e. one you registered with attachInterrupt()) other interrupts are disabled (since the core doesn't enable nested interrupts) so the timer 0 overflow interrupt that increments the value returned by millis() stops.  You can still call millis(), but it will always give the same answer as long as you're in the interrupt handler.  I think.  smiley
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Because well behaved interrupt handler code should only be active for a relatively short period of time, the other interrupts will still function. In the case cited, as long as other interrupt handlers use less then a millisecond, the millis() handler will be ready to do its stuff when the timer0 reaches one millisecond.

Typically, interrupt handlers used for timing will do little more then checking and perhaps changing a few clock registers and updating a variable or two that is shared with the non interrupt code. Think of the interrupt as little more then a trigger with most of the time intensive work done outside the handler.

If the OP wants to do a lot timed processing that overlaps, then he may want to look at using something like a state machine that is sequenced by timer events.
Logged

0
Offline Offline
Faraday Member
**
Karma: 7
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Interrupt service routines (ISRs) should be very short chunks of code, to avoid problems like those being discussed now.

For example, I use interrupts in my DTMF decoder sketch.  The entire ISR is

Code:
void dtmf_isr()
{
  tone_detected = 1;
}

In my loop() function, I check this variable each time through and act on it if needed.  This way, other interrupts will still function correctly.

-j

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 29
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You all make some good points, so here is my code (up until now).  Before you read it let me describe what is going on - the viewer approaches the piece (a "genie") and places their beverage into its hand.  The weight of the drink activates a lever switch and the Arduino runs the main routine.  Because void loop is running in a loop the piece doesn't react immediately - that is where I want to use the first interrupt.  Once the "main show" function is running, I want the removal of the drink to send execution back to void loop.  The code you see here is what I wrote in the gallery with little time to spare before the show opening - and having been awake for 28 hours to boot.  The eventual program will have more action - but I want to try to incorporate those interrupts before I head down that path.  Thanks for looking!  

Pix to help explain the thing - http://www.flickr.com/photos/shifzr/2055471345/ - http://www.flickr.com/photos/hackaday/2055635032/


int inPin = 7;   // lever switch connected to pin 7
int val = 0;     // switch read value

/*
Lynxmotion channels:
2 - fortune card servo
7 - eyes servo
11 - head servo
15 - jaw servo
16 - chamber LED
17 - status check LED
19 - status check LED
22 - panel LED
24 - panel LED
26 - panel LED
28 - hand LED green
29 - hand LED red
serial Rx

Arduino channels:
1 - serial Tx to Lynxmotion
7 - arm switch
*/


void setup()
{
  Serial.begin(9600);
  pinMode(inPin, INPUT);      // sets the digital pin 7 as input
}
 
 
 void loop()  //loop contains default resting behavior
{
  if (!digitalRead(inPin))   // read the arm switch, if not down run default
  {
  Serial.println("#17H #19L #24L #22L #25H #26L #28H #29L"); //initialize LEDs
  Serial.println("#2 P1900 S350"); //slowly return fortune servo
  Serial.println("#7 P1500 #15 P1500 #16L"); //eyes slow, mouth closed
  Serial.println("#11 P600 S200 #17H #19L"); //head slow
  delay (2000);
  Serial.println("#11 P1600 S150"); //head slow
  delay (2000);
  }
  else //if arm is down run the following function
  {
  run_main_show();
  }
}


void run_main_show()
  {
  Serial.println("#7 P1520 #15 P600 #11 P600 #17L #19H #28L #29H");  //eyes stop, head back, mouth closed
  delay (3000);
  Serial.println("#7 P1400"); //move eyes fast
  Serial.println("#11 P700 S300"); //head faster
  //Serial.println("#15 P600"); //mouth open
  delay (2000);
  Serial.println("#11 P1300 S300"); //head faster
  Serial.println("#15 P1500"); //mouth closed
  delay (2000);
  Serial.println("#7 P1520 #15 P1500 #11 P600 #24H");  //eyes stop, head back, mouth closed
  delay (4000);
  Serial.println("#15 P600"); //mouth open quick
  delay (150);
  Serial.println("#15 P1500"); //mouth closed quick
  delay (150);
  Serial.println("#15 P600"); //mouth open quick
  delay (150);
  Serial.println("#15 P1500"); //mouth closed quick
  delay (150);
  Serial.println("#15 P600"); //mouth stays open 2 seconds
  delay (2000);
  Serial.println("#16H #24L"); //chamber lights on, panel light off
  Serial.println("#15 P1500"); //mouth closed
  delay (6000);
  Serial.println("#2 P600 S500 #22H #16L"); //fortune kicks first time, fortune panel light on, chamber light off
  delay (1500);
  Serial.println("#2 P1500"); //slowly return fortune servo part way back
  delay (1500);
  Serial.println("#2 P600 S500 #22H"); //fortune kicks second time, fortune panel light on
  delay (2000);
  Serial.println("#2 P1900 S350"); //slowly return card servo
  Serial.println("#26H #22L"); //remove drink light on, fortune light off
  delay (10000);
  
}




« Last Edit: December 06, 2007, 06:24:53 pm by CTP » Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 9
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You could replace the calls to delay() with something like:

start = milliis();
while (millis() - start < 2000) {
  if (digitalRead(drinkPin) == LOW) {
    return;
  }
}

which you could #define to something like:

delayReturningIfDrinkRemoved(2000);

What you really want, though, is to define the actions in data, not code, e.g.:

char *main_show_actions[] = {
  "#7 P1520 #15 P600 #11 P600 #17L #19H #28L #29H",
  "#7 P1400",
  // ...
  NULL
};

int main_show_delays[] = {
  3000,
  1000,
  // ...
};

char *loop_actions[] = {
  // ...
};

int loop_times[] = {
  // ...
};

And then have a pointer:

char ***current_actions = &loop_actions;
int **current_times = &loop_times;
int index = 0;

Then, your loop is something like:

void loop() {
  Serial.println((*current_actions)[index]);
  start = millis();
  while (millis() - start < (*current_times)[index]) {
    drinkPinReading = digitalRead(drinkPin);
    if (drinkPinReading != previousDrinkPinReading) {
       previousDrinkPinReading = drinkPinReading;
       index = -1;
       if (drinkPinReading == LOW) {
         current_actions = &loop_actions;
         current_times = &loop_times;
       } else {
         current_actions = &main_show_actions;
         current_times = & main_show_times;
       }
       break;
    }
  }
  index++;
  if ((*current_actions)[index] == NULL) {
    index = 0;
  }
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 29
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

wow....well that there is a *WHOLE* lot of Arduino for me to chew on, that's for sure.

Good thing it's nearly the weekend :-)

As I read through some of the other responses I get the feeling that I was misinterpreting how attachInterrupt and delay and millis may or may not interact...I will check those assumptions this weekend.


btw - I'm starting to have flashbacks to 20 years ago when I decided to be a sculptor instead of a programmer - and now they are merging...merging like an egg and the sidewalk from 10 stories up ;-)
« Last Edit: December 06, 2007, 08:20:59 pm by CTP » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 29
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay, after reading thru I get how (and why) to keep my various actions and such in arrays.

What do these lines do and what is the significance of the splats and ampersands?
 
char ***current_actions = &loop_actions;
int **current_times = &loop_times;
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 106
Posts: 6373
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

"char ***current_actions" is a pointer to the currently used array of actions.  Each action is a string (char *), and an array is pointer to strings (char **), so the pointer to the array is a triple-removed pointer (char ***)  current_actions points to an array, array members point to string, strings point to chars.   This is about the level of indirection where I give up an start defining more complex data structures (action_table *current_actions), but that'll add an additional level of coding complexity if you're not fully C-literate.

Similarly , current_times is a point to an array of ints.

I think the amperstands are redundant (perhaps not in C++); they ensure that you pass the address of the arrays rather than something else.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 29
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay - first off this is great stuff, and thanks for all the advice.  

Secondly, though, I have to mention that my niche in teaching microcontrollers is teaching beginners how to program them.  While some of my students could be shown arrays, and how they work, I don;t have the majority of my students for a long enough stretch of time to go there.  Any chance any of you feel like having a crack at this one from other angles?  And not necessarily the tersest or most efficient ways, but some that are easier for the relative newcomer to get.  I'll keep all the examples together to show them of course...options are important.

Thanks again for the help btw!
Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 9
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yea, I think westfw is right and I added one level of indirection that didn't need to be there.  Still, this would probably still look confusing.  There's a String library that I've been hoping to add to the distribution that might simplify things a bit.
Logged

Pages: [1]   Go Up
Jump to: