Atmega Microcontroller interrupt delay function on button press

atmegaavrdelayinterrupts

I am using Atmega32A and I have 2 LEDs. Here's step by step what I want to do

  1. When the MCU is powered, I have one LED lit (PC0)
  2. When click on a button (PD6) the second LED at (PC1) is lit and the other one is turned off.
  3. A delay function is delaying this state for 10 seconds. After that the first LED (PC0) is lit, too, for 5 seconds. At this time both the LEDs are lit.
  4. Then 3. repeats.

What I want to do is, when I click on the button at (PD6) to be able to interrupt / break the delay.

Here's the code

#include <avr/io.h>
#include <util/delay.h>

int main(void){
    DDRC = 0b11111111;  //Makes PORTB as Output
    DDRD = 0b00000000;  //Makes PORTD as Input

    PORTC = 0b11111110; //LEDs
    PORTD = 0b11111111; //Button

    int pressed = 0;
    int Pressed_Confidence_Level = 0;
    int Released_Confidence_Level = 0;

    while(1){
        if(bit_is_clear(PIND, 6)){  //If button is pressed (PD6)
            Pressed_Confidence_Level++;
            if(Pressed_Confidence_Level > 500){
                if(pressed == 0){
                    if(PINC & (1<<PC0)){
                        PORTC = 0b11111111; //PC0 LED ON, PC1 LED ON
                    }
                    else{
                        while(1){
                            PORTC = 0b00000001; //PC0 LED ON, PC1 LED OFF
                            _delay_ms(600000);  //10min
                            PORTC = 0b11111111; //PC0 LED ON, PC1 LED ON
                            _delay_ms(300000);  //5min
                            /* How to exit this loop on button press? */
                            }
                        }
                    }
                    pressed = 1;
                }
                Pressed_Confidence_Level = 0;
            }
        }
        else{
            Released_Confidence_Level++;
            if(Released_Confidence_Level > 500){
                pressed = 0;
                Released_Confidence_Level = 0;
            }
        }
    }
}

Best Answer

Atmega micros have hardware interrupts that can be used to accomplish this. Interrupts can be used with interrupt handlers (short functions that are called when an interrupt is triggered) called "Interrupt Service Routine" (ISR) in atmel speak to accomplish short tasks that you would like to be called asynchronously with primary control "thread".

_delay_ms is a busy-wait delay so there is no harm in calling shorter spans of it (say every 100ms and decrementing a counter). If you write a short interrupt handler to set a global flag you can have the loop check the flag every iteration and break out of it sooner. Otherwise you can also continuously check the button state in your loop with a finer grain delay without interrupts if the project will stay as simple as it already is.

Interrupts can be disabled and enabled dynamically, so there is no concern that your button press will change program state outside of the loop of interest. Its pretty straight forward (one memory address write) to enable and disable interrupt vectors before and after the delay loop runs.

see for example:

this external forum post

this tutorial

official atmel clib documentation on atmega interrupt service routines

this technical application note from atmel

a short tutorial note from atmel

Review the doc 'sheet' for your micro for even more detailed implementation notes.