I'll just do some coming out with the code I use whenever I need buttons.
It is based on keeping a simple FSM for each button.
The debouncing time, the minimum time to register a short or a long press are defined by the constants CLICKDEBOUNCE, CLICKSHORT, and CLICKLONG.
A short press will register when the button is released, a long one when its time has elapsed.
The ServeButton() routine must be invoked for each button at a sensible interval (some ms).
It returns a key value (enum) associated with the defined events (long and short press) or NoKey if nothing happened.
The routine can be called in the main loop, or in a periodic ISR (it's lightweight enough, if the number of key is limited, but YMMV).
In the last case, the key press can be passed to main loop in the usual ways (e.g. in a volatile variable, or enqueued in an input queue if needed).
The specific example is from my PSU controller, for encoders' shaft clicks, but the code is generic enough: change the Key enum to suit your needs (hence not shown here).
It uses the ST HAL, but it's quite portable: the only requirements are reading a GPIO pin (duh!) and having a tick counter.
typedef enum
{
Idle = 0,
Debouncing,
Pressed,
Waiting
} BtnState;
typedef struct
{
uint16_t pin; /* GPIO pin */
GPIO_TypeDef *port; /* GPIO port */
GPIO_PinState pol; /* polarity: SET active high, RESET active low */
BtnState state; /* current state */
uint32_t tick; /* time of press */
Key kShort; /* key enum value for short press */
Key kLong; /* key enum value for long press */
} Button;
#define CLICKDEBOUNCE 30 /* 30ms for debouncing */
#define CLICKLONG 1000 /* 1s for long click */
#define CLICKSHORT 75 /* 75ms for short click */
#define BUTTONS 2
Button buttons[BUTTONS] =
{
{
.pin = ENC1_BTN,
.port = ENC1_BTN_GPIO,
.pol = GPIO_PIN_SET,
.kShort = ClickVKey,
.kLong = LongVKey
},
{
.pin = ENC2_BTN,
.port = ENC2_BTN_GPIO,
.pol = GPIO_PIN_SET,
.kShort = ClickIKey,
.kLong = LongIKey
}
};
Key serveButton( Button *b )
{
Key key = NoKey;
uint32_t now = HAL_GetTick(); // What's the time?
bool btn = b->pol == HAL_GPIO_ReadPin( b->port, b->pin );
switch( b->state )
{
case Idle:
if( btn )
{
/* The button has been pressed */
/* Remember when */
b->tick = now;
/* and move to Debouncing state */
b->state = Debouncing;
}
break;
case Debouncing:
if( btn )
{
if( now - b->tick > CLICKDEBOUNCE )
/* Real press */
b->state = Pressed;
}
else
{
/* It was a bounce, start over */
b->state = Idle;
}
break;
case Pressed:
if( btn )
{ /* Still pressed, is this a long press? */
if( now - b->tick > CLICKLONG )
{
/* A long press has been detected */
key = b->kLong;
/* Wait for release */
b->state = Waiting;
}
}
else
{ /* Released, is it a short press? */
if( now - b->tick > CLICKSHORT )
{
/* A short press has been detected */
key = b->kShort;
}
/* Return to idle */
b->state = Idle;
}
break;
case Waiting:
if( !btn )
{
b->state = Idle;
}
break;
}
return key;
}