5 Wake-Up On Button Press
This example demonstrates the usage of interrupts and sleep modes. In this use case, the microcontroller exits sleep on button press, turns on an LED, and goes back to sleep. On button release, it exits sleep, turns off the LED, and goes back to sleep. The LED is ON while the button is pressed, but the microcontroller can be in sleep mode.
Pins can detect transitions from ‘0’ to ‘1’ (rising
            edge) and from ‘1’ to ‘0’ (falling edge). An interrupt
            can be triggered on one or both transitions. This is configured using the ISC bit field
            in the PORTx.PINnCTRL register. 
These interrupts wake the device from all sleep modes. In this example, on button press/release, the microcontroller will wake from sleep and turn an LED on/off and then go back to sleep. In this way, the LED will stay on only while the button is pressed.
First, the B2 pin, where the button is attached, is configured as input, and its interrupt is activated.
PORTB.DIR &= ~ PIN2_bm; PORTB.PIN2CTRL |= PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
0’. Therefore, whenever the
            button is not pressed, it must be connected to VDD using a pull-up resistor.
            The pull-up resistor is activated using the PULLUPEN bit field in the PORTx.PINnCTRL
            register.Second, the sleep mode is selected using the macros defined in the
                <avr/sleep.h> header. The modes of sleep are Idle, Standby
            and Power-Down. In this example, the deepest sleep mode is used.
set_sleep_mode(sleep_MODE_PWR_DOWN);
This only selects which sleep mode to be used. The following macro makes the microcontroller go to sleep.
sleep_mode();
sleep_mode() macro
            also enables sleep before sending the CPU sleep instruction and
            disables sleep when the microcontroller wakes up.When going to sleep, the global interrupts must be enabled. Otherwise, there is no way to
            wake up. Using the sei() macro defined in the
                <avr/interrupt.h> header enables global interrupts. In this
            example, this is done in the initialization code.
Sometimes, there are blocks of instructions that need to execute without
            interruption. They are called atomic blocks. This is achieved by disabling interrupts at
            the beginning of the block and re-enabling them at the end of the block. For this, the
                ATOMIC_BLOCK macro in <avr/atomic.h> is
            used.
Finally, inside the Interrupt Service Routine (ISR), a flag is set to indicate a transition (rising or falling edge) happened. Because it is a good practice to keep the ISR short, the transition type check and LED toggling are handled in the main-line code.
ISR(PORTB_PORT_vect)
{
    if(PB2_INTERRUPT)
    {
        pb2Ioc = 1;
        PB2_CLEAR_INTERRUPT_FLAG;
    }
}
        Because the same interrupt is used for all the pins in a port, the ISR code must check what pin triggered the interrupt. This is done by checking its flag in PORTx.INTFLAGS. In this example, to check whether the pin B2 triggered the interrupt, the following macro is used.
#define PB2_INTERRUPT PORTB.INTFLAGS & PIN2_bm
Clearing the interrupt flag is done by writing a ‘1’ to its
            location in PORTn.INTFLAGS.
#define PB2_CLEAR_INTERRUPT_FLAG PORTB.INTFLAGS &= PIN2_bm
The main-line code checks the pin value ten milliseconds after the interrupt
            to decide whether it was a falling edge or a rising edge. If the pin is low (value
                ‘0’), then it must have been a falling edge, otherwise it was a
            rising edge. The ten milliseconds delay is a denouncing method.
#define PB2_LOW !(PORTB.IN & PIN2_bm)
An MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here:
