Short and Long button press

To continue a discussion from chat:

// example irq callback handler, copy for your own uses
void button_irq_callback(uint gpio, uint32_t events){
    gpio_acknowledge_irq(gpio, events);   
    gpio_set_irq_enabled(gpio, events, true);
    button_pressed=true;
}
// enable the irq for button button_id
void button_irq_enable(uint8_t button_id, void *callback){
    button_pressed=false;
    gpio_set_irq_enabled_with_callback(EXT1, GPIO_IRQ_EDGE_RISE, true, callback);
} 
// disable the irq for button button_id
void button_irq_disable(uint8_t button_id){
    gpio_set_irq_enabled(EXT1, GPIO_IRQ_EDGE_RISE, false);
    button_pressed=false;
}

To add a timer for short/long presses, I’d start something like this:

// enable the irq for button button_id
void button_irq_enable(uint8_t button_id, void *callback){
    button_pressed=false;
    gpio_set_irq_enabled_with_callback(EXT1, GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL, true, callback);
} 

Adding GPIO_IRQ_EDGE_FALL gives us interrupts on rise and falling edges now.

// example irq callback handler, copy for your own uses
void button_irq_callback(uint gpio, uint32_t events){

//is button pressed? rising edge i guess
if(events&GPIO_IRQ_EDGE_RISE){
//start a timer here
}else if (events&GPIO_IRQ_EDGE_FALL){
  //falling edge, button released. Stop timer, determine short or long
   if(elapsed_time<=BP_BUTTON_SHORT_PRESS_MS){
      button_pressed=BP_BUTTON_SHORT;
   }else{
      button_pressed=BP_BUTTON_LONG;
   }
}
//clear interrupts
    gpio_acknowledge_irq(gpio, events);   
    gpio_set_irq_enabled(gpio, events, true);

}

I’m not sure this is exactly how the events variable is evaluated, but something in the neighborhood of this should work.

Thank you for the guidance. I have successfully made the separate rise and fall IRQ’s work in button.c, with current src printing debug information for testing:

#include <stdio.h>
#include "pico/stdlib.h"
#include "pirate.h"

#define BP_BUTTON_SHORT_PRESS_MS 500 // Threshold for short button press

static bool button_pressed = false;
static absolute_time_t press_start_time;

// poll the value of button button_id
bool button_get(uint8_t button_id){
    return gpio_get(EXT1);
} 
bool button_check_irq(uint8_t button_id){
    if(button_pressed){
        button_pressed=false;
        return true;
    }
    return false;
}
// example irq callback handler, copy for your own uses
void button_irq_callback(uint gpio, uint32_t events){
    // Check if button was pressed (rising edge)
    if (events & GPIO_IRQ_EDGE_RISE) {
        printf("Button pressed (rising edge detected)... ");
        press_start_time =  get_absolute_time();
    }

    // Check if button was released (falling edge)
    if (events & GPIO_IRQ_EDGE_FALL) {
        absolute_time_t press_end_time = get_absolute_time();
        int64_t duration_ms = absolute_time_diff_us(press_start_time, press_end_time) / 1000;
        printf("Button released (falling edge detected) ");
        printf("Press duration: %lld ms ", duration_ms);
    
    if (duration_ms <= BP_BUTTON_SHORT_PRESS_MS) {
        printf("Short press detected\r\n");
    } else {
        printf("Long press detected\r\n");
    }
    
    button_pressed = true;
}

    gpio_acknowledge_irq(gpio, events);   
    gpio_set_irq_enabled(gpio, events, true);
//    button_pressed=true;
}

// enable the irq for button button_id
void button_irq_enable(uint8_t button_id, void *callback){
    button_pressed=false;
    gpio_set_irq_enabled_with_callback(EXT1, GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL, true, callback);
} 
// disable the irq for button button_id
void button_irq_disable(uint8_t button_id){
    gpio_set_irq_enabled(EXT1, GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL, false);
    button_pressed=false;
}
// initialize all buttons
void button_init(void){
    gpio_set_function(EXT1, GPIO_FUNC_SIO);
    gpio_set_dir(EXT1, GPIO_IN);
    gpio_pull_down(EXT1);
}

—>
I have it print when the RISE IRQ is triggered, and then on release print when the FALL IRQ is triggered. i then have to print out the duration of the hold, and whether it was detected as a short of long press
—>

HiZ> Button pressed (rising edge detected)... Button released (falling edge detected) Press duration: 1190 ms Long press detected
Using default 'button.scr'

HiZ> 

HiZ> Button pressed (rising edge detected)... Button released (falling edge detected) Press duration: 92 ms Short press detected

# reset power
HiZ> w
Command has no effect in HiZ mode, press 'm' to choose a mode
HiZ> W 3.3 20
Command has no effect in HiZ mode, press 'm' to choose a mode

HiZ> 
1 Like

Hi Ian,

Do you have a hardware-based debounce circuit (e.g., RC filter, ) for the BP5 physical button?

If not, there may need to be additional complexity wrapped into the button handling. I’ve not put a scope to my REV10 board, and won’t have time for at least a couple weeks, so thought I might raise the question early, in case it saves later frustration.

P.S. – I realize this type of issue is well-known to many; For readers new to this, here is one decent write-up with scope showing visually what’s happening.

1 Like

I’m interested to see Ian’s respond on henrygabs comment. very interesting.
i have a branch with working long press logic:

can you let me know if you are interested in merging this?

1 Like

Nice work. Let’s get this integrated.

To future proof this, I feel like maybe it should return an value from an enumerated list? Also to keep pirate.c clean:

                if(button_check_irq(0, &long_press)){
                    button_irq_disable(0);
                    if (long_press) {
                        button_long_exec(); // over long press threshold execute bulong.scr
                    } else {
                        button_exec(); // short press execute button.scr
                    }
                    bp_state=BP_SM_COMMAND_PROMPT;  //return to command prompt
                }

How about moving this logic into the button.c lib and the button_scr.c commands?

                button_code=button_check_irq(0);
               if(button_code) {
                    button_irq_disable(0);
                    button_exec(button_code);
                    bp_state=BP_SM_COMMAND_PROMPT;  //return to command prompt
                }

Could even move the irq disable to button_exec.
With an enum of codes:

enum button_codes{
  BP_BUTT_NO_PRESS=0,
 BP_BUTT_SHORT_PRESS,
BP_BUTT_LONG_PRESS,
BP_BUTT_DOUBLE_TAP,
};

This gives the flexibility to add more button presses (BP_BUTT_DOUBLE_TAP) in the future without expanding on the code in pirate.c.

No, there is no hardware debugging, but the RP2040 pins have a schmitt trigger, and it seems to do ok along with the lazy servicing in the shared multitasking loop.

ok i think i accomplished what you wanted:

Added enum button_codes to button.h to define button press types.
Declared button_check_press function in button.h.
Added button press detection logic to button_irq_callback.
Implemented button_check_press to return the button press type.
Used a static variable button_code to track the current press type.
Moved button script execution logic to button_scr.c.
Added button_exec function in button_scr.c to handle short and long press script execution.
Integrated button_check_press and button_exec(press)code) into the main loop in pirate.c.