// 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>
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.
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.
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.
T he long/short functionality works well. Your changes are spot on.
In button_scr->button_script_handler:
In this function the user can assign a custom script to the button. It seems with the latest changes that assignment is tested, but not copied to the button file variable. Ideally, we need some way to assign both the long and short button scripts, with a verb or flag:
button short filename.scr
button long filename.scr
I believe the constant char file names will need to go back to, letās say char[13] (8.3 file name, no folders).
Iām going to roll it back for now to restore the original functionality. I can make some changes, but want to discuss with you first.
i literally woke up this morning and saw you merged and started thinking about stuff. i didnt catch the button assignment, but i was pondering
a) the help message needs to be updated
b) w eshould comment out double tab option for now.
enum button_codes {
BP_BUTT_NO_PRESS = 0,
BP_BUTT_SHORT_PRESS,
BP_BUTT_LONG_PRESS,
BP_BUTT_DOUBLE_TAP,
};
c) sidenote: the first time you do a short or long press, it checks if LONG_BUTTON_FLAG_FILE_CONFIGURED flag is NOT set in button_flags with the if (!(button_flags & BUTTON_FLAG_FILE_CONFIGURED))
, and if not copies it sets the flag. on subsequent long presses it would not print this message. it does the same for short. these 2 things could happen at different times though. i feel like maybe both variables should be set at the time either one is run.
d) and now you bring up the point about the assignment command
sorry i just woke up, but im brainstorming this now. it definitely needs some more minor considerations for the overall usability. we need to make sure it is clear inside of the software what is going on. not everyone is going to read new docs or diffs. we may also want to raise the long press threshold to 1000ms.
you are welcome to make changes or we can talk this out and i can make a new push sometime today.
i assigned these as const in button_scr.c so that will need to be changed
one confusion i have is where is 8.3 being enforced? we have static char button_script_file[32]; which should be able to hold a much larger filename. but oddly enough i tried a longer filename at first and had errors. i changed to bulong.scr because i figured thats how you wanted it, but didnt actually see why longer filenames didnt work.
EDIT:
and to be clear you had it this way at first, just not const
static char button_script_file[32];
you are saying now to change to char[13] for 8.3
ā
seriously though if you know exactly how we should add the new button assignment, feel free
I just need to come up with a game plan. maybe you can help me get my head on straight
1.) Do we want the new script to be named bulong.scr?
2.) how should we setup the strings to hold the script name? (im a bit confused if we are doing this correct with char[32] and we definitely cant have it defined as a const)
3.) do we add a new command, identical to button command but for bulong?
4.) i guess we can just leave how it sets the flags and is verbose on first press of each one. that will be fine as it will indicate wat script is being used if they changed it
EDIT: just let me know when you can talk in realtime. im thinking we should have a separate bulong_scr.c now?
my problem was i got focused on the changes i needed to make in button_exec in button_scr.c, but didnt consider how intertwined button_scr.c is with the button command
One of my biggest questions and where I got confused is i havenāt dealt much with the CLI commands. i did read your thread the other day on how to add one, and iāve dug through the source today.
But here are some points:
commands.c includes button_scr.c and defines the button command of course:
{"button", true, &button_scr_handler, 0x00},
using the button_scr_handler which resides in button_scr.c
this all makes sense now. the button_scr.c is FOR the cli button command.
But where my confusion comes in, is this is intertwined with the actual handling of button presses by including button_exec. The original:
if(button_check_irq(0)){
button_irq_disable(0);
button_exec(); //if button pressed, run button.scr script
bp_state=BP_SM_COMMAND_PROMPT; //return to command prompt
}
break;
yes button_exec is what sets the script files, but its not necessarily related to the CLI button command. see what im getting at? shouldnt we move button_exec to button.c and only use button_scr.c (and bulong_scr.c) for the command line handling?
The flash media is using the FAT file system, and to save RAM, has explicitly disabled the long file name (LFN) functionality. Therefore, you are limited to filenames that would be accepted by pre-Windows95 FAT file system.
Those filenames are limited to eight characters + . + three letter file extension. The period character cannot be in any other location.
ahh ok. something i did figure out today while digging thru the src, the button_scr_handler uses cmdln_args_string_by_position(uint32_t pos, uint32_t max_len, char *str) , called with sizeof(script_file) as the max_len param
I Added bulong command with bulong_scr.{h,c}
Moved button_exec to button.c as this would be used by both so didnt make sense to be in button_scr.c
I defined BP_FILENAME_MAX 12 in button.h and started using it as henrygab suggested.
I defined many other things in button.h to consolidate.
button_check_irq was replaced with button_check_press (using the new enum button_codes method)
i think i cleaned up pretty good, but i had to get rid of some warnings and make sure declarations were in the right place, and adding some includes to button.cā¦ so yeh please check
here is the diff. i didnt put in a pull request yet:
let me know if any changes needed, but i think overall this accomplished some better structure for the button stuff, and the long press functionality is working. soā¦ its getting there
I like the idea of using a define for the file length. Sometimes I add some extra (32 bytes) thinking āit might be in a folderā, but thatās just wasted memory for a minor convenience. maybe 8.3 + max 8 char folder + /?> 21 bytes? or just 12 is probably fine 99% of the time.
For those who havenāt used an analog scope to look at what really happens when a button if pushed, the following is an easy to read primer, with graphs showing what an oscilliscope sees for a random selection of buttons ā¦ very enlightening!
The second page (linked at bottom of above) includes both hardware and software debounce options ā¦ including the simplest timer-based debouncing function (that works well) Iāve seen.
nice. i actually wrote up some code the other day for button_irq_callback to handle debouncing with with ātime since last pressā checks of 20ms or so. I think Ian said the react time in the loop naturally debounces it thoughtā¦
I assume in a lot of hardware with more complicated considerations like your articles, you would want to actually measure with a scope and even build a large dataset to see if it changes over time. If you were in a lab, measuring this for a large companies projects, you might want to see if it changes after 10,000 presses. or different environmental situations even
thanks for the infos. this skillbank article is super interesting.
ā
Ian: it took me a good few minutes to wrap my head around how you were using the button_script_files and button_flags arrays, but it makes perfect sense now.
I tried to clean up, and after some research, i reduced the long press threshold to 550ms. this seems to match the consensus of other projects.
I left all your comments in. There was a multiline one i think you probably dont intend to leave, but you can just remove on next commit if you merge.