A Matrix Keypad on a Raspberry Pi done right

There are quite a few articles to be found on the Internet on how to use a matrix keyboard on a Raspberry Pi.

Surprisingly none of them seems to use the method documented here.

Instead most of them seem to use some handcrafted Raspberry Pi and python-only solution with debounce logic implemented in userland.

As with my article on Setting up a GPIO-Button “keyboard” on a Raspberry Pi this uses a device tree based approach and will not require any driver code in userland.

As with the solution above an application will be able to use this keyboard just in the same way as any other keyboard (e.g. a standard USB keyboard) connected to a computer running Linux.

So here is how to do it:

To check if the driver is available modinfo matrix-keypad should show you something like this:

pi@raspberrypi:~$ sudo modinfo matrix-keypad
alias:          platform:matrix-keypad
license:        GPL v2
description:    GPIO Driven Matrix Keypad Driver
author:         Marek Vasut <marek.vasut@gmail.com>
srcversion:     54E6656500995BD553F6CA4
alias:          of:N*T*Cgpio-matrix-keypadC*
alias:          of:N*T*Cgpio-matrix-keypad
depends:        matrix-keymap
intree:         Y
vermagic:       4.9.35+ mod_unload modversions ARMv6 p2v8 
</marek.vasut@gmail.com>

To enable the driver we need to create a device tree overlay file suitable for a given matrix keyboard.

As an example I use the following device available at your favorite china shop.

4x5matrix

Here is what the corresponding device tree overlay file 4x5matrix.dts looks like:

    /dts-v1/;
    /plugin/;
    / {
           compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709";

           fragment@0 {
              target-path = "/";
              __overlay__ {
                 keypad: MATRIX4x5 {
                    compatible = "gpio-matrix-keypad";
                    debounce-delay-ms = <10>;
                    col-scan-delay-us = <10>;
                    /* 
		       try to use GPIO only lines
                       to keep SPI and I2C usable
                    */
                    row-gpios = <&gpio 27 0    // 1
                                 &gpio 22 0    // 2
                                 &gpio 10 0    // 3
                                 &gpio 9 0>;   // 4

                    col-gpios = <&gpio 13 0    // 5
                                 &gpio 26 0    // 6
                                 &gpio 16 0    // 7
                                 &gpio 20 0    // 8
                                 &gpio 21 0>;  // 9
                    /*
                      Keycodes from /usr/include/linux/input-event-codes.h
                      converted to hex using printf '%02x\n'
                    */

                    linux,keymap = <
                                    // col0 row0 KEY_LEFT
                                    0x00000069
                                    // col0 row1 KEY_KP0
                                    0x01000052
                                    // col0 row2 KEY_RIGHT
                                    0x0200006a
                                    // col0 row3 KEY_KPENTER
                                    0x03000060
				    // col1 row0 KEY_KP7
                                    0x00010047
                                    // col1 row1 KEY_KP8
                                    0x01010048
                                    // col1 row2 KEY_KP9
                                    0x02010049
                                    // col1 row3 KEY_ESC
                                    0x03010001
                                    // col2 row0 KEY_KP4
                                    0x0002004b
                                    // col2 row1 KEY_KP5
                                    0x0102004c
                                    // col2 row2 KEY_KP6
                                    0x0202004d
                                    // col2 row3 KEY_DOWN
                                    0x0302006c
                                    // col3 row0 KEY_KP1
                                    0x0003004f
                                    // col3 row1 KEY_KP2
                                    0x01030050
                                    // col3 row2 KEY_KP3
                                    0x02030051
                                    // col3 row3 KEY_UP
                                    0x03030067
                                    // col4 row0 KEY_F1
                                    0x0004003b
                                    // col4 row1 KEY_F2
                                    0x0104003c
                                    // col4 row2 KEY_KPSLASH there is no KP_#
                                    0x02040062
                                    // col4 row3 KEY_KPASTERISK
                                    0x03040037>;

                 };
              };
           };
      };

Further documentation can be found in the file Documentation/devicetree/bindings/input/matrix-keymap.txt inside the Linux kernel source tree. Feel free to ask if it does not work for you.

Now to enable our keyboard there are only four steps left:

  1. Connect the keyboard to the GPIO lines as defined in the dts file
  2. Compile the dts file to the binary dtbo format. This is done using the device tree compiler of your kernel tree:
    ./scripts/dtc/dtc -W no-unit_address_vs_reg -I dts -O dtb -o 4x5matrix.dtbo 4x5matrix.dts
    
  3. Copy the resulting dtbo file to /boot/overlays/4x5matrix.dtbo on the Raspberry Pi
  4. Add the following to /boot/config.txt:
    dtoverlay=4x5matrix
    

Now after rebooting the Pi the lsinput command should show us a new keyboard connected to the device. You may need to install a package called input-utils first if this command ist not available on your Pi.

Here is what this looks like after pressing the Enter Key on the matrix keyboard:

pi@raspberrypi:~$ sudo -s
root@raspberrypi:~# lsinput
/dev/input/event0
   bustype : BUS_HOST
   vendor  : 0x0
   product : 0x0
   version : 0
   name    : "MATRIX4x5"
   bits ev : EV_SYN EV_KEY EV_MSC EV_REP

root@raspberrypi:~# input-events 0
/dev/input/event0
   bustype : BUS_HOST
   vendor  : 0x0
   product : 0x0
   version : 0
   name    : "MATRIX4x5"
   bits ev : EV_SYN EV_KEY EV_MSC EV_REP

waiting for events
19:56:28.727096: EV_MSC MSC_SCAN 24
19:56:28.727096: EV_KEY KEY_KPENTER (0x60) pressed
19:56:28.727096: EV_SYN code=0 value=0
19:56:28.797104: EV_MSC MSC_SCAN 24
19:56:28.797104: EV_KEY KEY_KPENTER (0x60) released
19:56:28.797104: EV_SYN code=0 value=0

Happy hacking!