4 Long and Short Button Press

This section explains how to detect whether a button press is long or short, resulting in two different scenarios.

The GPIO interface can be used to sense external digital signals in order to make certain decisions. In this regard, the application range is vast, so the AVR controllers can be configured several of ways to match as many use cases as the user might need. In this section, the focus will be on the push of a button.

First, PB2 will be configured as input by writing a ‘1’ on bit 2 of the Data Direction Clear (PORTB.DIRCLR) register.

Figure 4-1. Bit 2 Data Direction Clear Register
PORTB.DIRCLR = PIN2_bm;

For a button connected between the GPIO pin and GND to work correctly, connect a pull-up resistor between the GPIO pin and VDD. This configuration will make sure the pin reads a default value of logic ‘1’ when the button is not pressed. It is possible to activate an internal pull-up resistor from the Pin 2 Control (PORTB.PIN2CTRL) register.

Figure 4-2. Internal Pull-Up Resistor Pin 2 Control Register
PORTB.PIN2CTRL = PORT_PULLUPEN_bm;

For a better user experience, an LED-connected PB5 will blink with different frequency based on the type of press. Thus, PB5 will be configured as output by writing a ‘1’ on bit 5 of the Data Direction Set (PORTB.DIRSET) register.

PORTB.DIRSET = PIN5_bm;

Then, in each iteration of the main loop, bit 2 of the Input Value (PORTB.IN) register will be checked. If the value is logic ‘0’, the program will wait until the value is back to logic ‘1’. By doing this, the program will sense when a press-and-release action on the button was performed. While waiting, a counter will be incremented every few milliseconds.

If the value of the counter passes a certain threshold, the program will decide there was a long press. If the button is released before passing the threshold, the program will decide there was a short press. Depending on the press type, the LED will blink with different frequencies.

Figure 4-3. Bit 2 Input Value Register
if(~PORTB.IN & PIN2_bm) /* check if PB2 is pulled to GND */
    {
        while(~PORTB.IN & PIN2_bm) /* wait until PB2 is pulled to VDD */
        {
            _delay_ms(STEP_DELAY);
            counter++;
            if(counter >= THRESHOLD)
            {
                LED_blink(LONG_DELAY);
                while (~PORTB.IN & PIN2_bm) /* wait until PB2 is pulled to VDD */
                }
                    ;
                }
                break;
            }
        }    
        if(counter < THRESHOLD)
        {
            LED_blink(SHORT_DELAY);
        }
        counter = 0;
}
Note: The delay macro can be found in the <util/delay.h> header file.

The LED_blink function takes the blinking period in milliseconds divided by two as a parameter. The function must be declared “inline” because the _delay_ms macro must take a parameter that is already known at the compilation stage.

inline void LED_blink(uint32_t time_ms)
{
        for(uint8_t i = 0; i < NUMBER_OF_BLINKS ; i++)
        {
            PORTB.OUT |= PIN5_bm;
            _delay_ms(time_ms);
            PORTB.OUT &= ~PIN5_bm;
            _delay_ms(time_ms);
        }
}

An MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here: