PSOC 5LP + 4x4 matrix Keypad Long KeyPress Detect

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Y_r
Level 4
Level 4
50 replies posted 50 sign-ins 25 replies posted

Hello @MotooTanaka san,


Thank you for your guidance in my previous thread.

That helped me a lot in my application code.

Now, i have another question/query based on the previous thread...

How is it possible to detect a long keypress and do a certain action?

And let's say i want to use BUTTON_D for two instances:  a short keypress (go to the previous menu) and a long keypress (go to the top menu), how can I differentiate between the two and how to detect both of the instances in a single program?

Thanks in advance for the help.

Regards,
Yash

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Yash-san,

 

As I wrote in the previous discussion, there can be many ways to implement your requirement.

And in this game field called "Real", you are allowed to think by yourself.

 

Having written above, I also thought about how we can implement what you requested.

 

In the previous sample, all the action was immediately taken when a button is pushed.

But to take care of the distinction of "short" and "long" press, we need to introduce a concept of duration.

In my previous sample, key[row][col] held only pushed '1' and released '0'.

But I modified them from uint8_t to uint16_t to hold the duration.

As each keys are being scanned every 16ms, uint16_t's 65535 times represents 1048 seconds,

which I hope enough for this purpose. (May be uint8_t (4 seconds) were enough)

Now I let scan_key_matrix() to accumulate time(s) each button is pressed,

and clear the count when the button is released.

During  this I checked if 'D' button is pressed longer than the threshold, which is defined as PRESS_THRESHOLD.

And if the count exceeds the threshold, a global variable d_long_pressed is set.

void scan_key_matrix(void)
{
    switch(row_no) {
    case  0: ROW0_Write(1) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
    case  1: ROW0_Write(0) ; ROW1_Write(1) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
    case  2: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(1) ; ROW3_Write(0) ; break ;
    case  3: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(1) ; break ;
    default: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
    }

    switch(col_no) {
    case 0: if (COL0_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    case 1: if (COL1_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    case 2: if (COL2_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    case 3: if (COL3_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    }
    
    if (BUTTON_D_LENGTH > PRESS_THRESHOLD) { // 'D' pressed for long time
        d_long_pressed = 1 ;
    }
    
    col_no++ ;
    if (col_no >= NUM_COL) {
        col_no = 0 ;
        row_no = (row_no + 1) % NUM_ROW ;
    } 
    ROW0_Write(0) ; 
    ROW1_Write(0) ; 
    ROW2_Write(0) ; 
    ROW3_Write(0) ; 
}

This variable is cleared at the bottom part of the main loop, so that we have a chance to use this flag  just after 'D' key is released.

Now in the main loop, we need to check if any key is pressed or  'D' is just released.

int main(void)
{
    init_hardware() ;
    
    greeting() ;
    CyDelay(3000) ;
    
    go_menu_top() ;

    for(;;)
    {
        key_table = get_key_matrix() ;
        
        if (
            ((prev_key & BUTTON_D) && (key_table == NO_BUTTON)) // 'D' button released
            || (key_table != prev_key)) { // Any changes with button status
            switch(menu_mode) {
            case MENU_TOP: do_menu_top() ; break ;
            case MENU_10:  do_menu_10()  ; break ;
            case MENU_11:  do_menu_11()  ; break ;
            case MENU_12:  do_menu_12()  ; break ;
            case MENU_20:  do_menu_20()  ; break ;
            case MENU_21:  do_menu_21()  ; break ;
            case MENU_22:  do_menu_22()  ; break ;
            default:       menu_mode = MENU_TOP   ; break ;
            }
            CyDelay(50) ;
        }
        if (key_table == NO_BUTTON) {
            d_long_pressed = 0 ;
        }
        prev_key = key_table ;
    }
}

 

And each do_menu_xx() needs to check when no button is pressed, but 'D' was previously pushed and if d_long_pressed is set or not.

void do_menu_22(void)
{
    switch(key_table) {
    case NO_BUTTON: 
        if (d_long_pressed) {
            go_menu_top() ;
        } else if (prev_key & BUTTON_D) {
            go_menu_20() ;
        } else {
            go_menu_22() ;
        }
        break ;
    case BUTTON_1: go_menu_22() ; break ;
    case BUTTON_2: go_menu_22() ; break ;
    default:       go_menu_22() ; break ;
    }
}

 

Again, this code of mine is not the only solution nor the best possible solution.

I'd be glad this can be some hint for you, but please proceed with a grain of salt.

 

Best Regards,

16-Jul-2021

Motoo Tanaka

View solution in original post

4 Replies
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Yash-san,

 

As I wrote in the previous discussion, there can be many ways to implement your requirement.

And in this game field called "Real", you are allowed to think by yourself.

 

Having written above, I also thought about how we can implement what you requested.

 

In the previous sample, all the action was immediately taken when a button is pushed.

But to take care of the distinction of "short" and "long" press, we need to introduce a concept of duration.

In my previous sample, key[row][col] held only pushed '1' and released '0'.

But I modified them from uint8_t to uint16_t to hold the duration.

As each keys are being scanned every 16ms, uint16_t's 65535 times represents 1048 seconds,

which I hope enough for this purpose. (May be uint8_t (4 seconds) were enough)

Now I let scan_key_matrix() to accumulate time(s) each button is pressed,

and clear the count when the button is released.

During  this I checked if 'D' button is pressed longer than the threshold, which is defined as PRESS_THRESHOLD.

And if the count exceeds the threshold, a global variable d_long_pressed is set.

void scan_key_matrix(void)
{
    switch(row_no) {
    case  0: ROW0_Write(1) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
    case  1: ROW0_Write(0) ; ROW1_Write(1) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
    case  2: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(1) ; ROW3_Write(0) ; break ;
    case  3: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(1) ; break ;
    default: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
    }

    switch(col_no) {
    case 0: if (COL0_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    case 1: if (COL1_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    case 2: if (COL2_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    case 3: if (COL3_Read()) { key[row_no][col_no]++ ; } else { key[row_no][col_no] = 0 ; } break;
    }
    
    if (BUTTON_D_LENGTH > PRESS_THRESHOLD) { // 'D' pressed for long time
        d_long_pressed = 1 ;
    }
    
    col_no++ ;
    if (col_no >= NUM_COL) {
        col_no = 0 ;
        row_no = (row_no + 1) % NUM_ROW ;
    } 
    ROW0_Write(0) ; 
    ROW1_Write(0) ; 
    ROW2_Write(0) ; 
    ROW3_Write(0) ; 
}

This variable is cleared at the bottom part of the main loop, so that we have a chance to use this flag  just after 'D' key is released.

Now in the main loop, we need to check if any key is pressed or  'D' is just released.

int main(void)
{
    init_hardware() ;
    
    greeting() ;
    CyDelay(3000) ;
    
    go_menu_top() ;

    for(;;)
    {
        key_table = get_key_matrix() ;
        
        if (
            ((prev_key & BUTTON_D) && (key_table == NO_BUTTON)) // 'D' button released
            || (key_table != prev_key)) { // Any changes with button status
            switch(menu_mode) {
            case MENU_TOP: do_menu_top() ; break ;
            case MENU_10:  do_menu_10()  ; break ;
            case MENU_11:  do_menu_11()  ; break ;
            case MENU_12:  do_menu_12()  ; break ;
            case MENU_20:  do_menu_20()  ; break ;
            case MENU_21:  do_menu_21()  ; break ;
            case MENU_22:  do_menu_22()  ; break ;
            default:       menu_mode = MENU_TOP   ; break ;
            }
            CyDelay(50) ;
        }
        if (key_table == NO_BUTTON) {
            d_long_pressed = 0 ;
        }
        prev_key = key_table ;
    }
}

 

And each do_menu_xx() needs to check when no button is pressed, but 'D' was previously pushed and if d_long_pressed is set or not.

void do_menu_22(void)
{
    switch(key_table) {
    case NO_BUTTON: 
        if (d_long_pressed) {
            go_menu_top() ;
        } else if (prev_key & BUTTON_D) {
            go_menu_20() ;
        } else {
            go_menu_22() ;
        }
        break ;
    case BUTTON_1: go_menu_22() ; break ;
    case BUTTON_2: go_menu_22() ; break ;
    default:       go_menu_22() ; break ;
    }
}

 

Again, this code of mine is not the only solution nor the best possible solution.

I'd be glad this can be some hint for you, but please proceed with a grain of salt.

 

Best Regards,

16-Jul-2021

Motoo Tanaka

Hello @MotooTanaka san,


Thank you so much for the shared project. That's the implementation and the explanation that I was searching for.

I have tried out the project and have some doubts and findings as below:

1.) #define PRESS_THRESHOLD (100)  -- the 100 value would translate to how many ms or secs exactly?
(I got confused reading, " As each keys are being scanned every 16ms, uint16_t's 65535 times represents 1048 seconds, which I hope enough for this purpose. (Maybe uint8_t (4 seconds) were enough)"...)
Also, how would it behave if we use uint8_t instead of the uint16_t for the key[row][col] array?

2.) In the UART prints, there are always two prints of the same choice (menu) - button pressed and button released. And a third print of the same menu as soon as the button D is pressed (check attached log messages/pic from Termite below).  -- Is this behavior expected?

Yash_r_0-1626420446338.png

3.) Also, in the printing to the terminal, there is another rather strange issue with the longpress functionality for the do_menu_11() function....instead of calling do_menu_top() directly when BUTTON_D is longpressed, the function firstly prints the do_menu_10() on BUTTON_D press and then do_menu_top() on BUTTON_D release.
Further explanation below:
The print works okay (still there is a double instance of print as mentioned above) :
P1-Emp Details -> PRESS 1: NAME -> Employee Name Yash
But to directly go back to the top_menu() (pressing BUTTON_D) after 'Yash' is printed, the control doesn't directly go back to the "P1-Emp Details" menu, rather prints "PRESS 1: NAME PRESS 2: AGE" on the button press and then goes to top_menu() on BUTTON_D release (Check Picture below) -- is not seen in other cases.

Yash_r_1-1626421300417.png

Thanks for your wonderful support and help.

Regards,
Yash

0 Likes
Y_r
Level 4
Level 4
50 replies posted 50 sign-ins 25 replies posted

Hello @MotooTanaka san,

I have figured out why the double print instances are occurring, so please excuse the 2.) doubt above.
It is because of the else condition in the 

    case NO_BUTTON: 
        if (d_long_pressed) {
            go_menu_top() ;
        } else if (prev_key & BUTTON_D) {
            go_menu_top() ;
        } else {
            go_menu_20() ;   //the cause for the double printing.
        }
        break ;

Since the else case prints the data/string first, and when a button is pressed, it again does the same check -- causes the double printing issues.
Commenting out the else condition and function solves the issue.

Regards,
Yash

MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Yash-san,

First of all, I'm sorry that my sample was not perfect.

But I hope that you can get the "idea' of how to handle long and short press.

 

About (1)

The threshold of 100 means, the count of particular key (this time D = key[3][3]) 

needs to be more than 100 to be detected as a long press.

Since each full key scan requires 16ms, currently D should be pressed for

16x100ms = 1.6secs.

It could be shorter or please change this value to fit your purpose.

And with uint8_t probably we can count up to 4sec, so if you don't mind the case when a user keep pushing more than 4 secs, uint8_t may be enough.

 

About (2) and (3), I'm sorry that I can not afford time to debug it right away,

but since you have located a bug block, I hope that you can modify it to suite your need.

 

Best Regards,

16-Jul-2021

Motoo Tanaka