1
votes

I need to execute a procedure at constant time intervals. The procedure takes a long to execute and during that time other interrupt must be active. Also, it is critical that the procedure is executed at each timer overflow.

Take a look at this pseudo-code:

ISR(timer_overflow)
{
  timer_flag = 1;
}

main_loop()
{
  if (timer_flag)
  {
     long_time_consuming_procedure();
     timer_flag = 0;
  }
  (* if a timer interrupt fires here, will the procedure be executed? *)
  sleep();
}

If the above won't work, will the below code make things rock-solid?

main_loop()
{
  cli();
  if (timer_flag)
  {
     sei();
     long_time_consuming_procedure();
     timer_flag = 0;
  }
  sei();
  sleep();
}

Or maybe this will be better, as other interrupt are handled very quickly:

ISR(timer_overflow)
{
  sei();
  long_time_consuming_procedure();
}

main_loop()
{
  sleep();
}

I'm using avr-gcc

EDIT

Looks like I shared too little detail. I'm afraid of this worst-case scenario:

  • some interrupt (other than timer overflow) wakes up the uc
  • long_time_consuming_procedure is not called as there was no timer overflow
  • just before the moment cpu goes back to sleep (betwen if (timerflag) and sleep()) the timer overflows
  • timer interrupt is executed correctly
  • after returning from ISR cpu goes to sleep without executing long_time_consuming_procedure, because we've already passed if (timerflag)
  • there are no other interrupts in the following timer cycle, thus cpu is woken up after the next overflow

This way there are two timer interrupts and only one long_time_consuming_procedure execution. There is a very small chance for that to happen, but if something can go wrong it'll go even worse.

4

4 Answers

0
votes

In general number 1 is the most common solution. As it allows for your procedure to execute while other interrupts are still handled.

Number 2 should be avoided because turning interrupts on and off is not great practice. There are fringe cases but would require a little more detail as to why number 1 would not work.

Number 3 would not allow for other interrupts to be active while executing your long running procedure so it should be avoided as other interrupts will not be handled from within an ISR.

0
votes

You don't provide enough information for a specific answer. If the requirement is, that your long_time_consuming_procedure() is just executed once per interval but it is irrelevant when exactly it finishes during that time, I would go for solution #1.

Make sure that the execution time of long_time_consuming_procedure() + execution time of all interrupts that can possibly occur during the long_time_consuming_procedure() processing is shorter than your timer interval.

Even if the general concepts are pretty straight forward, its sometimes non-trivial to make interrupt based code work exactly as desired. You have to carefully evaluate each interrupt and the individual requirements.

0
votes

If you want to execute procedure at each timer overflow and there is probability that interrupt can occur during executing procedure, use this:

ISR(timer_overflow)
{
  timer_flag = 1;
}

main_loop()
{
  if (timer_flag)
  {
     cli();
     timer_flag = 0;
     sei();

     long_time_consuming_procedure();
  }
  sleep();
}
0
votes

In the AVR Libc user manual, in detailed description of sleep.h I found an example:

set_sleep_mode(<mode>); cli(); if (some_condition) { sleep_enable(); sei(); sleep_cpu(); sleep_disable(); } sei();

This sequence ensures an atomic test of some_condition with interrupts being disabled. If the condition is met, sleep mode will be prepared, and the SLEEP instruction will be scheduled immediately after an SEI instruction. As the intruction right after the SEI is guaranteed to be executed before an interrupt could trigger, it is sure the device will really be put to sleep.

It looks that the winner is #2 with a little help from sleep_enable() and friends