Electrical – Long and short button press with stm32f0

armcstm32timer

I'm trying to understand timer of stm32f0 without using the stm library, in particular I'm using the stmf051 discovery board.

What I'm trying to do is a simple program that detect when a button is short pressed or long pressed.

So the program would be simply start a counter when the microcontroller, detect a button is pressed and if the ARR (auto reload value) reach the limit, light the green led in the board otherwise, turn on the blue led in the board.

What came up so far looking at the example in the datasheet and in this web site, is this piece of code, that it works but there are some problem of debouncing (I've update the code and now the problem of debouncing is solved), and I'm not satisfied with it.

So please can you help me to improve my code, or show me a better way to do it, or simply point me in the right direction.

Thank you for your time, and sorry for my bad english.

    #include "stm32f0xx.h"

    short buttonPressed(void);
    void delay(int a);

    int main(void)
    {

        /* Enable the GPIO A,C,B */
        RCC->AHBENR |=
                (RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN);

        // Enable Timer TIM2
        RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

        TIM2->PSC = 23999;          // Set prescaler to 24 000 (PSC + 1)
        TIM2->ARR = 800;            // Auto reload value 800, for activate the green 
                                    // led you need to push the botton for at least 800 ms

        TIM2->CR1 = TIM_CR1_CEN;    // Enable timer

        /* Configure PC8 and PC9 in output  mode (led green and blue) */
        GPIOC->MODER |= (GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0);

        /* Button Pin PA0 in alternate function (user button in the board)*/
        GPIOC->OTYPER &= ~(GPIO_OTYPER_OT_8 | ~GPIO_OTYPER_OT_9);

        //Ensure maximum speed setting (even though it is unnecessary)
        GPIOC->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR8 | GPIO_OSPEEDER_OSPEEDR9);

        //Ensure all pull up pull down resistors are disabled
        GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPDR8 | GPIO_PUPDR_PUPDR9);

        while (1)
        {
            short answer = buttonPressed();

            if (answer == 1)
            {

                GPIOC->BSRR = (1 << 9); // Blink the led in PC9 if the button is
                delay(300000);          //  long pressed
                GPIOC->BRR = (1 << 9);  //
            }
            if (answer == 2)
            {
                GPIOC->BSRR = (1 << 8); // Blink the led in PC9 if the button is
                delay(300000);          // short pressed
                GPIOC->BRR = (1 << 8);  //
            }

        }

        return 0;
    }
    short buttonPressed()
{
    short return_value = 0;

    if (GPIOA->IDR & GPIO_IDR_0)        // If the button is pressed
    {
        delay(80000);
        if (GPIOA->IDR | GPIO_IDR_0)
        {
            TIM2->EGR |= TIM_EGR_UG;       // For an update generation with UG=1
            TIM2->SR &= ~TIM_SR_UIF;       // Clear TIM_ENV update interrupt so the ARR start from 0
            while (GPIOA->IDR & GPIO_IDR_0)
            {
            }               // Don't do anything until the button is released

            if (TIM2->SR & TIM_SR_UIF) // If ARR reached 800 it means that it the button was long pressed
            {
                return_value = 1;
                TIM2->SR &= ~TIM_SR_UIF;    // Clear the ENV register

            }
            else                    // otherwise the button was short pressed
            {
                return_value = 2;
                TIM2->SR &= ~TIM_SR_UIF;    // Clear the ENV register
            }
        }

    }
    return return_value;
}


    void delay(int a)
    {
        volatile int i, j;

        for (i = 0; i < a; i++)
        {
            j++;
        }

        return;
    }

Best Answer

The code could be improved greatly.

First, delays are bad, very bad, don't use delays. It slows your code down and holds it still. The code you have right now can only do one thing. If you ever wanted to code to do anything else it would be difficult.

There are better ways to write time dependent events.

  1. Set up a timer that counts some nice amount of time such as a millisecond.
  2. In the main loop service the timer and set flags for time events. For example; 1 second, 500 milliseconds, 10 milliseconds and so on.
  3. In the main loop after the timer service have IF statements that trigger depending on time flags. For example blinking a light; every time the 500 millisecond flag is set the IF statement goes into work and inverts the light then resets the flag.
  4. For the debounce. Store a main variable that keeps track of the button. Every millisecond check the button. If the button does not match the stored state then start a debounce. For the debounce start a counter at 50. Keep checking the button every millisecond. If the button is high add 1 to the counter. If the button is low subtract one from the counter. If the counter reaches 0 the button is low. If the counter reaches 100 then the button is high. Store that value in the main button variable. Finally reset the debounce flag, that way you aren't always debouncing.

This is just one way of doing it, there are others. This creates a limber operating system that can handle timed events.