1

I have a working program and I wish to improve its efficiency as much as possible.

It will blink slow, then faster and faster until it restarts.

It works, but my main concern is, currently, I am using a if statement in the void loop(), and I am certain that, using an interrupt when a specific variable has reached 0, would improve efficiency.

byte amp = 0;

void setup() {
  pinMode(13, OUTPUT);
  reset();
}

void loop() {            // The following
  digitalWrite(13,HIGH); // code is for the sake of example
  delay(amp*100);        // Simplified to focus on the loop
  digitalWrite(13, LOW);
  delay(amp*100);
  amp--;
  if (!amp) {reset();}     // <-This right here
}

void reset() {amp = 10;}

My understanding is, the if statement is called every time the loop restarts.

  • Is it possible to set up an interrupt for when a certain variable reaches 0?
  • Which option would require less computing power, a continually called if loop, or an interrupt?
5
  • 1
    For what are you trying to save computing time? The first step would be to ditch all the delay() calls (since with them you have up to 2s, where the Arduino is just twiddling its thumbs) and use millis() like in the BlinkWithoutDelay example for a non-blocking coding style. The if statement only takes so little time, that it doesn't matter for 99,9% of cases. What do you want to do in your code besides the blinking, that you need this optimization?
    – chrisl
    Commented Dec 11, 2020 at 23:36
  • Thanks for your answer @chrisl, actually this is not my regular code and only for the sake of simplified example.
    – B7th
    Commented Dec 12, 2020 at 0:20
  • 1
    you can't really use an interrupt to replace an if statement ... the proper way to service an interrupt is to only set a flag in the ISR ... then test the flag in loop(), usually by using an if statement
    – jsotola
    Commented Dec 12, 2020 at 0:59
  • 1
    look at the assembly code created from the sketch if you want to understand advanced things.
    – Juraj
    Commented Dec 12, 2020 at 5:56
  • 1
    @B7th I already thought so, but you still didn't write about what your regular code does besides blinking. I think, that you might look into a totally unnecessary direction for optimizing your code out of missing knowledge. To actually know that, we would need to know, for what you are trying to save "computation power". Only then we can give you an answer, that really gets you further.
    – chrisl
    Commented Dec 12, 2020 at 13:18

2 Answers 2

1

You did not specify the kind of Arduino you are using but, based on another recent question of yours, I will assume it is an AVR-based Arduino.

You cannot have an interrupt automatically triggered by a variable reaching zero. What you can do, instead, is trigger an interrupt by software, by toggling a pin that is configured both as an output and as an interrupt source:

const uint8_t int_pin = 2;  // pin used for interrupt

void setup() {
    digitalWrite(int_pin, LOW);
    pinMode(int_pin, OUTPUT);
    attachInterrupt(digitalPinToInterrupt(int_pin, reset, RISING));
}

static inline void trigger_interrupt() {
    digitalWrite(int_pin, HIGH);
    digitalWrite(int_pin, LOW);
}

// Within loop():
if (!amp) trigger_interrupt();

However, if you do that, you will discover that interrupts are slow; awfully slow compared with a simple if test:

  1. digitalWrite() itself is awfully slow. This can be overcome by using direct port access, but that is still two cycles for toggling the pin in each direction.

  2. You will lose an extra cycle in the pin synchronizer.

  3. Once the IRQ is raised, the CPU needs four cycles to prepare itself for servicing it.

  4. The interrupt vector is a jmp instruction, which takes 3 cycles.

  5. The ISR then has to save every single register it is going to use, including the status register. That takes two cycles per register. And there are quite a lot of registers to save...

  6. This ISR, which is provided by the Arduino core, will then look for the interrupt handler you provided with attachInterrupt(). This also involves checking that the pointer to the handler is not zero (the kind of test you seem to be worried about). It then has to call (4 cycles) your handler, which will have to return (4 cycles) to the ISR. You could avoid this indirection by defining the ISR yourself instead of relying on attachInterrupt().

  7. Once the job is done, all the saved registers have to be restored (2 cycles times many register), and the reti instruction (4 cycles) is issued in order to restore control to the interrupted program.

  8. Last but not least, since amp will be modified in interrupt context, you will have to qualify it as volatile. This keyword prevents the compiler from perform optimizations that are unsafe in this situation, and you will loose a lot of time to these missed optimization opportunities.

In contrast, this line:

if (--amp == 0) amp = 10;

would be translated by the compiler into something like this:

   dec  amp     ; --amp, as amp is likely already in a register
   brne 1f      ; if (amp != 0) skip the following
   ldi  amp, 10 ; amp = 10
1:

The whole sequence takes 3 cycles, irrespective of whether the if condition was true or false. That is at least one order of magnitude faster than the interrupt-based solution.

1
  • Very complete explanation of how and why. Thank you Edgar this helps make a lot of sense of how the assembly works!
    – B7th
    Commented Dec 12, 2020 at 16:52
1

Simple if statements are very fast and won't affect performance much even when used in every looping of the loop.

Using an interrupt to implement something simple like if (!amp) amp = 10; will probably make things slower:

  • something will have to keep track of the counter to generate an interrupt, and this "something" takes MCU time, unless it is a peripheral;
  • interrupts have overhead;
  • on single-thread MCUs, interrupts stop regular code execution until they are done;
  • amp = 10; will still have to be executed, in the regular code, or, if you must, in the interrupt.

You would gain more by getting rid of the function call to reset(), but the compiler probably does that for you.

Also, I wouldn't know how to replace a regular if with an interrupt, unless the counter is kept by a (timer) peripheral.

2
  • 1
    Re “amp = 10; will still have to be executed, in the regular code“: Why would you do that in the regular code? This statement would not take longer that setting a boolean. Commented Dec 12, 2020 at 13:50
  • 1
    @Edgar-Bonet: Yes, in this simple example it is just one statement, so it doesn't matter here.. I'm not of the school that says you should always only set a flag in an interrupt and must do everything else in the loop, in case you were worried
    – ocrdu
    Commented Dec 12, 2020 at 15:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.