NRF_Desktop keyboard example with 4x4 keypad

Good day Everyone

Im trying to incorporate a 4x4 keypad matrix with the nrf_desktop Keyboard.conf example in Zephyr. I would like to send membrane keystrokes via USB and BLE as per original example. I have made the following changes to incorporate the keypad.

 activate CAF

CONFIG_CAF_BUTTONS_EVENT_LIMIT=16

developer.nordicsemi.com/.../buttons.html

In the Buttons_def.h I added my rows and coulombs

#include <caf/gpio_pins.h>

/* This configuration file is included only once from button module and holds
 * information about pins forming keyboard matrix.
 */

/* This structure enforces the header file is included only once in the build.
 * Violating this requirement triggers a multiple definition error at link time.
 */
const struct {} buttons_def_include_once;

static const struct gpio_pin col[] = {
	//4x4 keypad col GPIO (1,4 | 1,3 | 1,2 | 1,1) as per overlay file
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(col1), gpios) },
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(col2), gpios) },
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(col3), gpios) },
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(col4), gpios) },

};

static const struct gpio_pin row[] = {
	//dk buttons - original
	{ .port = 0, .pin = DT_GPIO_PIN(DT_NODELABEL(button0), gpios) },
	{ .port = 0, .pin = DT_GPIO_PIN(DT_NODELABEL(button1), gpios) },
	{ .port = 0, .pin = DT_GPIO_PIN(DT_NODELABEL(button2), gpios) },
	{ .port = 0, .pin = DT_GPIO_PIN(DT_NODELABEL(button3), gpios) },
	//4x4 keypad rows GPIO (1,8 | 1,7 | 1,6 | 1,5) as per overlay file
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(row1), gpios) },
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(row2), gpios) },
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(row3), gpios) },
	{ .port = 1, .pin = DT_GPIO_PIN(DT_NODELABEL(row4), gpios) },

};

In the buttons_sim_def.h I have added my keystroke array.

/*
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <caf/key_id.h>
#include "usb_hid_codes.h"
/* This configuration file is included only once from buttons_sim module
 * and holds information about generated button presses sequence.
 */

/* This structure enforces the header file is included only once in the build.
 * Violating this requirement triggers a multiple definition error at link time.
 */
const struct {} buttons_sim_def_include_once;

const static uint16_t simulated_key_sequence[] = {
	KEY_ID(0x00, 0x11), /* N */
	KEY_ID(0x00, 0x12), /* O */
	KEY_ID(0x00, 0x15), /* R */
	KEY_ID(0x00, 0x07), /* D */
	KEY_ID(0x00, 0x0C), /* I */
	KEY_ID(0x00, 0x06), /* C */
	KEY_ID(0x00, 0x2C), /* spacebar */
};

const static uint16_t membrane_key_stroke_to_send[] = {
	//below keys come from USB_HID_codes.h
    KEY_KP8,       
    KEY_KP2,      
    KEY_KP4,        
    KEY_KP6,       
    KEY_KP1,      
    KEY_KP7,
    KEY_KP3,
    KEY_KPSLASH,
    KEY_KPMINUS,
    KEY_KPPLUS,
    KEY_KPASTERISK,
    KEY_KPENTER,       
};

In the App.overlay I have added the rows and column pin configuration

{
	chosen {
		nordic,pm-ext-flash = &mx25r64;
	};
};

/ {
	chosen {
		/*
		 * In some default configurations within the nRF Connect SDK,
		 * e.g. on nRF52840 and nRF9160, the chosen zephyr,entropy node
		 * is &cryptocell. This devicetree overlay ensures that default
		 * is overridden wherever it is set, as this application uses
		 * the RNG node for entropy exclusively.
		 */
		zephyr,entropy = &rng;
	};

	rows {
        compatible = "gpio-leds";
        row1: row_1 {
            gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
            label = "keypadrow1";
        };
        row2: row_2 {
            gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
            label = "keypadrow2";
        };
       row3: row_3 {
            gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
            label = "keypadrow3";
        };
       row4: row_4 {
            gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
            label = "keypadrow4";
        };
     };

	 col {
		compatible = "gpio-keys";
        col1: col_1 {
            gpios = <&gpio1 4 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "keypadcol1";
        };
        col2: col_2 {
            gpios = <&gpio1 3 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "keypadcol2";
        };
        col3: col_3 {
            gpios = <&gpio1 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "keypadcol3";
        };
        col4: col_4 {
            gpios = <&gpio1 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "keypadcol4";
        };
    };

	pwmleds1 {
		compatible = "pwm-leds";
		status = "okay";

		pwm_led1: led_pwm_1 {
			status = "okay";
			pwms = <&pwm1 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
			label = "LED Conn State";
		};
	};

	pwmleds2 {
		compatible = "pwm-leds";
		status = "okay";

		pwm_led2: led_pwm_2 {
			status = "okay";
			pwms = <&pwm2 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
			label = "LED Caps Lock";
		};
	};

	pwmleds3 {
		compatible = "pwm-leds";
		status = "okay";

		pwm_led3: led_pwm_3 {
			status = "okay";
			pwms = <&pwm3 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
			label = "LED Num Lock";
		};
	};
};

&pwm0 {
	status = "okay";
	pinctrl-0 = <&pwm0_default_alt>;
	pinctrl-1 = <&pwm0_sleep_alt>;
	pinctrl-names = "default", "sleep";
};

&pwm1 {
	status = "okay";
	pinctrl-0 = <&pwm1_default_alt>;
	pinctrl-1 = <&pwm1_sleep_alt>;
	pinctrl-names = "default", "sleep";
};

&pwm2 {
	status = "okay";
	pinctrl-0 = <&pwm2_default_alt>;
	pinctrl-1 = <&pwm2_sleep_alt>;
	pinctrl-names = "default", "sleep";
};

&pwm3 {
	status = "okay";
	pinctrl-0 = <&pwm3_default_alt>;
	pinctrl-1 = <&pwm3_sleep_alt>;
	pinctrl-names = "default", "sleep";
};

&pwm_led0 {
	status = "okay";
	pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_INVERTED>;
	label = "LED System State";
};

&qspi {
	status = "okay";
};

&pinctrl {
	pwm0_default_alt: pwm0_default_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 13)>;
			nordic,invert;
		};
	};

	pwm0_sleep_alt: pwm0_sleep_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 13)>;
			low-power-enable;
		};
	};

	pwm1_default_alt: pwm1_default_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 14)>;
			nordic,invert;
		};
	};

	pwm1_sleep_alt: pwm1_sleep_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 14)>;
			low-power-enable;
		};
	};

	pwm2_default_alt: pwm2_default_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 15)>;
			nordic,invert;
		};
	};

	pwm2_sleep_alt: pwm2_sleep_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 15)>;
			low-power-enable;
		};
	};

	pwm3_default_alt: pwm3_default_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 16)>;
			nordic,invert;
		};
	};

	pwm3_sleep_alt: pwm3_sleep_alt {
		group1 {
			psels = <NRF_PSEL(PWM_OUT0, 0, 16)>;
			low-power-enable;
		};
	};
};
 

Im a bit stuck now as I need to now scan the matrix return the value from the array and send as per example to BLE and USB.

I was thinking of using a timer to scan the keypad every 150ms return. 

char keypad[ROWS][COLS] = {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'}
};

char read_keypad(){

    for (int row_no = 0; row_no < 4; row_no++) {
      
	    gpio_pin_set_dt(&row1, row_no == 0 ? 0 : 1);
	    gpio_pin_set_dt(&row2, row_no == 1 ? 0 : 1);
        gpio_pin_set_dt(&row3, row_no == 2 ? 0 : 1);
		gpio_pin_set_dt(&row4, row_no == 3 ? 0 : 1);

		
		if (gpio_pin_get_dt(&col1)) return keypad[row_no][0];
		if (gpio_pin_get_dt(&col2)) return keypad[row_no][1];
        if (gpio_pin_get_dt(&col3)) return keypad[row_no][2];
        if (gpio_pin_get_dt(&col4)) return keypad[row_no][3];
    }
	
	return '\0';
}

from the timer

static void mytimer_cb(struct k_timer *dummy){

	char key = read_keypad();
	static uint8_t * p_key = keypadkey;

			if (key != '\0' ){
				 printk (" %c\n ", key);
				 
				switch (key){

                case '8': **send this somhow**(membrane_key_stroke_to_send);
                break;

                case '2' : **send this somhow**(membrane_key_stroke_to_send+1);
                break;

                case '4' : **send this somhow**(membrane_key_stroke_to_send+2);
                break;

                case '6' : **send this somhow**( membrane_key_stroke_to_send+3);
                break;

                case '1' : **send this somhow**(membrane_key_stroke_to_send+4);
                break;

                case '7' : **send this somhow**(membrane_key_stroke_to_send+5);
                break;

                case '3' : **send this somhow**(membrane_key_stroke_to_send+6);
                break;

                case 'A' : **send this somhow**(membrane_key_stroke_to_send+7);
                break;

                case 'B' : **send this somhow**(membrane_key_stroke_to_send+8);
                break;

                case 'C' : **send this somhow**(membrane_key_stroke_to_send+9);
                break;

                case '*' : **send this somhow**(membrane_key_stroke_to_send+10);
                break;
       
                case '#' : **send this somhow**(membrane_key_stroke_to_send+11);
                break;     
            }
					
	}	
}

If I look at https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/libraries/caf/caf_overview.html#c.button_event.key_id

It would seem the 4x4 keypad module could work out the box without me adding anything.  It may also be worth mentioning, that the 1st column on the keypad when pressed does result in the keys 'a', 'b' , 'c', d' being transmitted. Not sure where that comes from. the other columns do not respond. 

Thanks in advance as always

  • I have managed to sort it out. :) but for completeness

    in hid_keymap_def_keyboard.h

    KEY_ID the first no is the COL , the 2nd no the ROW, the 3rd the value to be sent as defined in the hut1_12.pdf

    bear in mind the DK buttons take up row 0-3. so all rows need to start at 4

    	//col 1
    	{ KEY_ID(0x00, 0x04), 0x001E, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x00, 0x05), 0x0021, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x00, 0x06), 0x0024, REPORT_ID_KEYBOARD_KEYS },
    	{ KEY_ID(0x00, 0x07), 0x0024, REPORT_ID_KEYBOARD_KEYS }, 
    	
    	//col 2
    	{ KEY_ID(0x01, 0x04), 0x001E, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x01, 0x05), 0x001F, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x01, 0x06), 0x0020, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x01, 0x07), 0x0021, REPORT_ID_KEYBOARD_KEYS }, 
    	//col3
    	{ KEY_ID(0x02, 0x04), 0x0022, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x02, 0x05), 0x0023, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x02, 0x06), 0x0024, REPORT_ID_KEYBOARD_KEYS },
    	{ KEY_ID(0x02, 0x07), 0x0025, REPORT_ID_KEYBOARD_KEYS }, 
    	//col 4
    	{ KEY_ID(0x03, 0x04), 0x0026, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x03, 0x05), 0x0027, REPORT_ID_KEYBOARD_KEYS },
    	{ KEY_ID(0x03, 0x06), 0x0026, REPORT_ID_KEYBOARD_KEYS }, 
    	{ KEY_ID(0x03, 0x07), 0x0027, REPORT_ID_KEYBOARD_KEYS }, 

Related