Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Example Code for 4x4 Keypad with Raspberry Pi Pico #542

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from

Conversation

jcarranz97
Copy link
Contributor

@jcarranz97 jcarranz97 commented Aug 31, 2024

Description:

Summary:

This Pull Request adds example code demonstrating how to use a 4x4 keypad with the Raspberry Pi Pico using C. The provided example aims to help users integrate and interact with a 4x4 matrix keypad in their projects.

Details:

  • Example Code:

    • Added a new example file (keypad_example.c) that illustrates how to set up and use a 4x4 keypad with the Raspberry Pi Pico.
    • The example includes code for initializing the keypad, scanning for key presses, and handling key input.
  • Code Structure:

    • The example code is organized into clear sections for initialization, key scanning, and key handling, making it easy to understand and adapt for different use cases.
    • Added comments throughout the code to explain key functions and operations.
  • Documentation:

    • Updated the README to include keypad folder

Benefits:

  • Practical Example: Offers a concrete example of how to interface with a 4x4 keypad using the Raspberry Pi Pico, facilitating quicker adoption and understanding.
  • Easy Integration: Helps users get started with keypad integration by providing ready-to-use code and clear explanations.
  • Educational Value: Serves as a useful reference for those new to working with keypads and the Raspberry Pi Pico.

Additional Notes:

  • Testing: The example code has been tested to ensure it works as intended. However, users are encouraged to test it in their own setups and provide feedback if any issues are encountered.
  • Future Improvements: Suggestions for additional features or improvements to the example are welcome.

Related Issues:

  • N/A

Thank you for reviewing this example code. I look forward to any feedback and am happy to make further adjustments based on your input.

README.md Outdated Show resolved Hide resolved
keypad/CMakeLists.txt Outdated Show resolved Hide resolved
keypad/keypad.c Outdated Show resolved Hide resolved
keypad/keypad.c Outdated Show resolved Hide resolved
keypad/keypad.c Outdated Show resolved Hide resolved
keypad/keypad.c Outdated
}


char get_keypad_value(void){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth trying to detect if multiple keys are being pressed (and signal that as an error condition), rather than just assuming that the first key detected is the only key pressed? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about the same but allow multiple keys pressed at the same time, but did not find an "simple" way of doing this ...

This is what I have in mind, let me know what you think about this.

Enhanced Keypad changes

  1. Define a Macro to Switch Between Simple and Enhanced Keypad Modes
    Define a macro that allows switching between the basic single key read mode and the enhanced mode, which supports multiple key presses
// Allow reading multiple keys at the same time.
#define ENHANCED_KEYPAD
  1. Use an #ifdef Directive to Define MAX_MULTI_KEYS and KeypadResult
    When ENHANCED_KEYPAD is defined, the code uses a structure to hold the result of the key press. The MAX_MULTI_KEYS defines the maximum number of keys that can be pressed at the same time, and KeypadResult is a structure that contains the count of pressed keys and an array of those keys.
#ifdef ENHANCED_KEYPAD
/*
 * When using the 'Enhanced Keypad' the get_keypad_result function does not
 * return a single character but a KeypadResult instead which includes the
 * number of pressed keys (Up to MAX_MULTI_KEYS) and a list of the pressed keys
 * (Up to MAX_MULTI_KEYS).
 *
 */
#define MAX_MULTI_KEYS            6  // Maximum number of pressed keys at the
                                     // same time.

typedef struct {
    int count;                   // Number of pressed keys
    char keys[MAX_MULTI_KEYS];   // Pressed Keys
} KeypadResult;
#endif  //ENHANCED_KEYPAD
  1. Use an #ifdef Directive to Choose Between the Simple and Enhanced Function Implementations
    Depending on whether the ENHANCED_KEYPAD macro is defined, the code will either use the simple function that returns a single pressed key or the enhanced function that returns a KeypadResult structure. The enhanced function iterates through the rows and columns of the keypad matrix, checking each key, and saves the number of keys pressed and their values in the KeypadResult structure.
#ifdef ENHANCED_KEYPAD
KeypadResult get_keypad_result(void) {
    KeypadResult result;        // Define the result structure.
    result.count = 0;           // Initialize count to 0.

    // Iterate over key and rows to identify which key(s) are pressed.
    for (int row=0; row < KEYPAD_NUM_ROWS; row++) {
        gpio_put(keypad_rows_pins[row], 1);
        for (int column=0; column < KEYPAD_NUM_COLUMNS; column++) {
            sleep_ms(READ_MS_DELAY);
            if(gpio_get(keypad_columns_pins[column])) {
                // If the column pin is HIGH, a key is pressed.
                // Save the key in the KeypadResult structure and increase
                // count.
                result.keys[result.count] = keypad_keys[row][column];
                result.count++;
            }
        }
        gpio_put(keypad_rows_pins[row], 0);
    }

    // Return the structure containing all pressed keys.
    return result;
}
#else
char get_keypad_result(void) {
    // Iterate over key and rows to identify which key is pressed.
    // When iterating rows, the GPIO_OUT associated to the row needs to be set
    // to HIGH, and then iterate the columns to determine the GPIO_IN.
    for (int row=0; row < KEYPAD_NUM_ROWS; row++) {
        gpio_put(keypad_rows_pins[row], 1);
        for (int column=0; column < KEYPAD_NUM_COLUMNS; column++) {
            sleep_ms(READ_MS_DELAY);
            if(gpio_get(keypad_columns_pins[column])) {
                // If the column pin is HIGH, a key is pressed.
                // Put the row pin to low and return the pressed key.
                gpio_put(keypad_rows_pins[row], 0);
                return keypad_keys[row][column];
            }
        }
        gpio_put(keypad_rows_pins[row], 0);
    }

    // Return a constant indicating no key was pressed
    return NO_KEY_PRESSED;
}
#endif  //ENHANCED_KEYPAD
  1. Modification to the main Method
    The main method is modified to handle the enhanced keypad mode. When ENHANCED_KEYPAD is defined, the program will print the list of keys pressed. If no keys are pressed, it will print a message indicating that. Otherwise, it will print the number of keys pressed and list the keys in a comma-separated format.
    while (true) {
        #ifdef ENHANCED_KEYPAD
        // Call the enhanced function to get the result structure
        // containing the number of keys pressed and the keys themselves.
        KeypadResult result = get_keypad_result();

        // Check if no keys are pressed.
        if (result.count == 0) {
            // Inform the user that no keys are pressed.
            printf("No key pressed\n");
        }
        else{
            // If one or more keys are pressed, print the number of pressed keys.
            printf("Bang!!! '%d' key(s) are pressed. Keys: ", result.count);

            // Iterate through the pressed keys and print them.
            for (int i = 0; i < result.count; i++) {
                printf("%c", result.keys[i]);

                //  If it's not the last key, print a comma separator.
                if (i != (result.count - 1)) {
                    printf(", ");
                } else {
                    // If it's the last key, move to the next line.
                    printf("\n");
                }
            }
        }
        #else
        // Call the simple function to get a single pressed key.
        char key = get_keypad_result();

        // Check if no key is pressed.
        if (key == NO_KEY_PRESSED) {
            // Inform the user that no keys are pressed.
            printf("No key pressed\n");
        }
        else{
            // If a key is pressed, print the key.
            printf("Key '%c' pressed\n", key);
        }
        #endif
        sleep_ms(500);

Summary of Changes:

  1. Enhanced Keypad Mode:

    • Introduced a new mode via the ENHANCED_KEYPAD macro, allowing the detection of multiple key presses simultaneously.
    • Defined MAX_MULTI_KEYS to specify the maximum number of keys that can be pressed at once.
    • Created a KeypadResult structure to store the count and list of pressed keys.
  2. Enhanced get_keypad_result Function:

    • Modified the function to return a KeypadResult structure when ENHANCED_KEYPAD is defined.
    • Iterated through the keypad matrix to detect multiple key presses and store them in the KeypadResult structure.
    • Implemented conditional compilation to maintain the original single key detection functionality when ENHANCED_KEYPAD is not defined.
  3. Updated main Method:

    • Modified the main method to handle the enhanced keypad mode:
      • If no keys are pressed, the program prints "No key pressed".
      • If multiple keys are pressed, the program prints the number of keys and lists them in a comma-separated format.
    • Ensured backward compatibility by retaining the original behavior for single key presses when ENHANCED_KEYPAD is not defined.
  4. Improved Code Documentation:

    • Added detailed comments throughout the code to explain the purpose of each change.
    • Documented the new structure and function behavior for both the simple and enhanced keypad modes.

For a raw diff of the changes, you can view this link.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let me know if you agree with this implementation. I have created the README.adoc but this is including the 'Enhanced Keypad`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that actually work? I thought that for these simple matrix keypads, if more than one key is pressed at a time you can't actually reliably detect which keys have been pressed? 🤷
That's why I suggested that if you detect that more than one key has been pressed, you return this as an error-condition.
(although I'm more of a software-person than a hardware-person, so I might be wrong?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this method is also working.

A keypad is a matrix of buttons arranged in rows and columns, typically used in devices like old phones or security systems. Each button on the keypad connects a unique row and column. When a key is pressed, it completes a circuit between a specific row and column, which can be detected by a microcontroller like the Raspberry Pi Pico.

To detect which key is pressed, the microcontroller sequentially sets each row to a high voltage (1) and checks the voltage on each column. If pressing a key connects the active row to a column, the microcontroller detects the column as high (1). By knowing which row and column are connected, the microcontroller can determine which key was pressed.

I went ahead and merged this enhanced code. I think it is a more complete example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this method is also working.

I'm afraid I'm still not convinced, see e.g. https://arduino.stackexchange.com/questions/50869/4x4-matrix-keypad-multiple-inputs-cause-extra-readings

@peterharperuk
Copy link
Contributor

I'm not sure this is something we want in pico-examples. It's not clear what extra hardware is required or how to setup circuits to run with this code.

@lurch
Copy link
Contributor

lurch commented Sep 2, 2024

I'm not sure this is something we want in pico-examples. It's not clear what extra hardware is required or how to setup circuits to run with this code.

I was assuming it was something like https://www.adafruit.com/product/3844 ?

@jcarranz97 If you can write up a README.adoc for this with a parts-list, wiring diagram, etc. (see https://github.com/raspberrypi/pico-examples/tree/master/gpio/hello_7segment for an example) that'd be really good!

@jcarranz97
Copy link
Contributor Author

Thanks @lurch and @peterharperuk for your comments! Let me document this better in a README.adoc file and makes the recommended modifications :)

@peterharperuk
Copy link
Contributor

Unless I've missed it, you still haven't said what hardware is required and how to wire up the hardware to run this code.
I'm still doubtful this is something that needs to be in pico-examples.

* Implemented the enhanced keypad changes

* Create README.adoc file (#4)

* Change wiring to make it easier

* Include first version of README.adoc

* Fix image syntax

* Fix number of columns in table

* Remove 'Installation' section

* Put terminal output as a note

* Remove NOTE

* Use plain text Code Block instead

* Fix URLs to LICENSE and CONTRIBUTING

* Another attempt of fixing contributing and licensing

* Put links in line
@jcarranz97
Copy link
Contributor Author

Unless I've missed it, you still haven't said what hardware is required and how to wire up the hardware to run this code. I'm still doubtful this is something that needs to be in pico-examples.

Hi @peterharperuk,

I have merged some recent changes that include the updated README.adoc file. When you have a moment, please review the documentation and let me know if it meets the necessary standards or if any adjustments are needed.

Thank you!

@jcarranz97
Copy link
Contributor Author

Unless I've missed it, you still haven't said what hardware is required and how to wire up the hardware to run this code. I'm still doubtful this is something that needs to be in pico-examples.

Hi @peterharperuk,

I have merged some recent changes that include the updated README.adoc file. When you have a moment, please review the documentation and let me know if it meets the necessary standards or if any adjustments are needed.

Thank you!

I am not sure why the image in the adoc looks as broken in the PR, but you can see the file directly in my fork https://github.com/jcarranz97/pico-examples/tree/add-keypad-example/gpio/keypad

@lurch
Copy link
Contributor

lurch commented Sep 5, 2024

This part of your diagram looks a bit confusing! Perhaps you meant to use a background-colour other than black?
Screenshot from 2024-09-05 09-16-25

gpio/keypad/README.adoc Outdated Show resolved Hide resolved
@jcarranz97
Copy link
Contributor Author

This part of your diagram looks a bit confusing! Perhaps you meant to use a background-colour other than black? Screenshot from 2024-09-05 09-16-25

Just fixed this as part of 7324b2a (I had to change to fritzzing)

@WillerZ
Copy link

WillerZ commented Sep 19, 2024

I recommend using PIO rather than bit-banging if you need to solve this key matrix scanning problem. You can use this 2-instruction PIO program:

pio_encode_out(pio_src_dest::pio_pins, kPushBlockSize) |  pio_encode_delay(31))
pio_encode_in(pio_src_dest::pio_pins, kPullBlockSize))

Turn on autowrap, autopush, autopull, and then you can use pio_sm_put to write the scan-out pattern and pio_sm_get to read the scanned-in pattern. You will need to set the clock divider for the PIO because at full-rate it'll be cycling faster than electrical reality can cope with on every matrix I tested, which leads to ghost keypresses.

If your matrix has diodes in the right places you can correctly detect multiple concurrent keypresses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants