From d35a95c7afc9e5ef6f087034dbf4cbb03048f9cd Mon Sep 17 00:00:00 2001 From: Pete Johanson Date: Wed, 24 Jun 2020 10:06:35 -0400 Subject: Initial working Clueboard California Macropad w/ proton-c fixes * Basic Clueboard Californai Macropad shield definition. * New "direct" KSCAN driver that supports non-matrix direct wiring for switches, needed for macropad that doesn't have a matrix at all. * Some renames for existing KSCAN GPIO driver to make the implied "matrix" part explicit. --- app/boards/arm/planck/planck_rev6.dts | 2 +- app/boards/arm/proton_c/board.cmake | 5 +- .../shields/clueboard_california/Kconfig.defconfig | 14 ++ .../shields/clueboard_california/Kconfig.shield | 5 + .../clueboard_california/clueboard_california.conf | 0 .../clueboard_california.overlay | 34 +++ .../clueboard_california/keymap/keymap.overlay | 24 ++ app/boards/shields/clueboard_california/readme.md | 24 ++ app/boards/shields/kyria/kyria.overlay | 4 +- .../petejohanson_handwire.overlay | 4 +- .../petejohanson_proton_handwire.overlay | 2 +- app/drivers/zephyr/CMakeLists.txt | 3 +- app/drivers/zephyr/Kconfig | 8 + app/drivers/zephyr/dts/bindings/gpio-kscan.yaml | 25 -- .../zephyr/dts/bindings/zmk,kscan-gpio-matrix.yaml | 25 ++ app/drivers/zephyr/kscan_gpio.c | 256 -------------------- app/drivers/zephyr/kscan_gpio_matrix.c | 260 +++++++++++++++++++++ app/include/zmk/matrix.h | 3 + 18 files changed, 408 insertions(+), 290 deletions(-) create mode 100644 app/boards/shields/clueboard_california/Kconfig.defconfig create mode 100644 app/boards/shields/clueboard_california/Kconfig.shield create mode 100644 app/boards/shields/clueboard_california/clueboard_california.conf create mode 100644 app/boards/shields/clueboard_california/clueboard_california.overlay create mode 100644 app/boards/shields/clueboard_california/keymap/keymap.overlay create mode 100644 app/boards/shields/clueboard_california/readme.md delete mode 100644 app/drivers/zephyr/dts/bindings/gpio-kscan.yaml create mode 100644 app/drivers/zephyr/dts/bindings/zmk,kscan-gpio-matrix.yaml delete mode 100644 app/drivers/zephyr/kscan_gpio.c create mode 100644 app/drivers/zephyr/kscan_gpio_matrix.c (limited to 'app') diff --git a/app/boards/arm/planck/planck_rev6.dts b/app/boards/arm/planck/planck_rev6.dts index ef23bfd..ae002cd 100644 --- a/app/boards/arm/planck/planck_rev6.dts +++ b/app/boards/arm/planck/planck_rev6.dts @@ -55,7 +55,7 @@ }; kscan { - compatible = "gpio-kscan"; + compatible = "zmk,kscan-gpio-matrix"; label = "Keyscan Matrix"; row-gpios = <&gpioa 10 GPIO_ACTIVE_HIGH>, <&gpioa 9 GPIO_ACTIVE_HIGH>, diff --git a/app/boards/arm/proton_c/board.cmake b/app/boards/arm/proton_c/board.cmake index 4843c41..9da8ea9 100644 --- a/app/boards/arm/proton_c/board.cmake +++ b/app/boards/arm/proton_c/board.cmake @@ -1,6 +1,7 @@ # SPDX-License-Identifier: MIT -board_runner_args(jlink "--device=STM32F303VC" "--speed=4000") +board_runner_args(dfu-util "--pid=0483:df11" "--alt=0" "--dfuse") +board_runner_args(jlink "--device=STM32F303CC" "--speed=4000") -include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/dfu-util.board.cmake) include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/app/boards/shields/clueboard_california/Kconfig.defconfig b/app/boards/shields/clueboard_california/Kconfig.defconfig new file mode 100644 index 0000000..2408f9f --- /dev/null +++ b/app/boards/shields/clueboard_california/Kconfig.defconfig @@ -0,0 +1,14 @@ + +if SHIELD_CLUEBOARD_CALIFORNIA + +config ZMK_KEYBOARD_NAME + default "Clueboard California Macropad" + +# Unable to use interrupts as the same pin number is used +# across A & B controllers, and STM32F303CCT6 can't enable +# interrutps for multiple controllers for the same "line" +# for the external interrupts. +config ZMK_KSCAN_GPIO_POLLING + default y + +endif diff --git a/app/boards/shields/clueboard_california/Kconfig.shield b/app/boards/shields/clueboard_california/Kconfig.shield new file mode 100644 index 0000000..eca025d --- /dev/null +++ b/app/boards/shields/clueboard_california/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2020 Pete Johanson +# SPDX-License-Identifier: MIT + +config SHIELD_CLUEBOARD_CALIFORNIA + def_bool $(shields_list_contains,clueboard_california) diff --git a/app/boards/shields/clueboard_california/clueboard_california.conf b/app/boards/shields/clueboard_california/clueboard_california.conf new file mode 100644 index 0000000..e69de29 diff --git a/app/boards/shields/clueboard_california/clueboard_california.overlay b/app/boards/shields/clueboard_california/clueboard_california.overlay new file mode 100644 index 0000000..9f52d0d --- /dev/null +++ b/app/boards/shields/clueboard_california/clueboard_california.overlay @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Pete Johanson + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zmk,kscan = &kscan0; + }; + + kscan0: kscan_0 { + compatible = "zmk,kscan-gpio-direct"; + + label = "KSCAN"; + + input-gpios + = <&gpioa 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpioa 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpiob 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpiob 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpiob 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpioa 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpioa 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpiob 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&gpiob 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; + + }; + + // TODO: Per-key LED node(s) +}; + diff --git a/app/boards/shields/clueboard_california/keymap/keymap.overlay b/app/boards/shields/clueboard_california/keymap/keymap.overlay new file mode 100644 index 0000000..f2f49ea --- /dev/null +++ b/app/boards/shields/clueboard_california/keymap/keymap.overlay @@ -0,0 +1,24 @@ +#include +#include +#include + +/ { + chosen { + zmk,keymap = &keymap0; + }; + + keymap0: keymap { + compatible = "zmk,keymap"; + label ="Default Kyria Keymap"; + layers = <&default>; + }; + + layers { + compatible = "zmk,layers"; + + default: layer_0 { + label = "DEFAULT"; + keys = ; + }; + }; +}; diff --git a/app/boards/shields/clueboard_california/readme.md b/app/boards/shields/clueboard_california/readme.md new file mode 100644 index 0000000..17855e1 --- /dev/null +++ b/app/boards/shields/clueboard_california/readme.md @@ -0,0 +1,24 @@ +# Clueboard California Macropad + +A small 10-key macropad in the shape of the US state of California, + +## Features + +* Per key LED. +* Proton-C compatible, including space for the piezo speaker. +* Direct GPIO wiring, no GPIO matrix at all. + +Will need to set them to be PULL HIGH, and ACTIVE LOW + +| Switch | Switch Pin | LED Pin | +| ------------ | ---------- | ------- | +| shasta1 | a10 | a2 | +| modoc1 | a9 | b5 | +| mendocino1 | a0 | a1 | +| lassen1 | b8 | b4 | +| eldorado1 | b11 | b12 | +| big_sur1 | b9 | b10 | +| sierra1 | a8 | a15 | +| los_padres1 | a7 | a6 | +| mojave1 | b1 | b0 | +| joshua_tree1 | b2 | b3 | diff --git a/app/boards/shields/kyria/kyria.overlay b/app/boards/shields/kyria/kyria.overlay index af4e591..e9074f6 100644 --- a/app/boards/shields/kyria/kyria.overlay +++ b/app/boards/shields/kyria/kyria.overlay @@ -29,7 +29,7 @@ }; kscan_left: kscan_left { - compatible = "gpio-kscan"; + compatible = "zmk,kscan-gpio-matrix"; label = "KSCAN_LEFT"; diode-direction = "row2col"; @@ -49,7 +49,7 @@ }; kscan_right: kscan_right { - compatible = "gpio-kscan"; + compatible = "zmk,kscan-gpio-matrix"; label = "KSCAN_RIGHT"; diode-direction = "row2col"; diff --git a/app/boards/shields/petejohanson_handwire/petejohanson_handwire.overlay b/app/boards/shields/petejohanson_handwire/petejohanson_handwire.overlay index d65b3b1..95b0835 100644 --- a/app/boards/shields/petejohanson_handwire/petejohanson_handwire.overlay +++ b/app/boards/shields/petejohanson_handwire/petejohanson_handwire.overlay @@ -21,7 +21,7 @@ }; left_hand: kscan_1 { - compatible = "gpio-kscan"; + compatible = "zmk,kscan-gpio-matrix"; label = "KSCAN_LEFT"; diode-direction = "row2col"; @@ -32,7 +32,7 @@ }; right_hand: kscan_2 { - compatible = "gpio-kscan"; + compatible = "zmk,kscan-gpio-matrix"; label = "KSCAN_RIGHT"; diode-direction = "row2col"; diff --git a/app/boards/shields/petejohanson_proton_handwire/petejohanson_proton_handwire.overlay b/app/boards/shields/petejohanson_proton_handwire/petejohanson_proton_handwire.overlay index 5deaa42..65753fd 100644 --- a/app/boards/shields/petejohanson_proton_handwire/petejohanson_proton_handwire.overlay +++ b/app/boards/shields/petejohanson_proton_handwire/petejohanson_proton_handwire.overlay @@ -5,7 +5,7 @@ }; kscan0: kscan { - compatible = "gpio-kscan"; + compatible = "zmk,kscan-gpio-matrix"; label = "KSCAN"; diode-direction = "row2col"; diff --git a/app/drivers/zephyr/CMakeLists.txt b/app/drivers/zephyr/CMakeLists.txt index 03a97c6..b40498f 100644 --- a/app/drivers/zephyr/CMakeLists.txt +++ b/app/drivers/zephyr/CMakeLists.txt @@ -4,6 +4,7 @@ if(CONFIG_ZMK_KSCAN_GPIO_DRIVER) zephyr_library() zephyr_library_sources( - kscan_gpio.c + kscan_gpio_matrix.c + kscan_gpio_direct.c ) endif() diff --git a/app/drivers/zephyr/Kconfig b/app/drivers/zephyr/Kconfig index 2dcb3a8..cd526e8 100644 --- a/app/drivers/zephyr/Kconfig +++ b/app/drivers/zephyr/Kconfig @@ -3,6 +3,14 @@ config ZMK_KSCAN_GPIO_DRIVER default y select GPIO +if ZMK_KSCAN_GPIO_DRIVER + +config ZMK_KSCAN_GPIO_POLLING + bool "Poll for key event triggers instead of using interrupts" + default n + +endif + config ZMK_KSCAN_INIT_PRIORITY int "Keyboard scan driver init priority" default 40 diff --git a/app/drivers/zephyr/dts/bindings/gpio-kscan.yaml b/app/drivers/zephyr/dts/bindings/gpio-kscan.yaml deleted file mode 100644 index 748d3fa..0000000 --- a/app/drivers/zephyr/dts/bindings/gpio-kscan.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2020, Pete Johanson -# SPDX-License-Identifier: MIT - -description: GPIO keyboard matrix controller - -compatible: "gpio-kscan" - -include: kscan.yaml - -properties: - row-gpios: - type: phandle-array - required: true - col-gpios: - type: phandle-array - required: true - debounce-period: - type: int - default: 5 - diode-direction: - type: string - default: row2col - enum: - - row2col - - col2row diff --git a/app/drivers/zephyr/dts/bindings/zmk,kscan-gpio-matrix.yaml b/app/drivers/zephyr/dts/bindings/zmk,kscan-gpio-matrix.yaml new file mode 100644 index 0000000..5ebcbdd --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/zmk,kscan-gpio-matrix.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Pete Johanson +# SPDX-License-Identifier: MIT + +description: GPIO keyboard matrix controller + +compatible: "zmk,kscan-gpio-matrix" + +include: kscan.yaml + +properties: + row-gpios: + type: phandle-array + required: true + col-gpios: + type: phandle-array + required: true + debounce-period: + type: int + default: 5 + diode-direction: + type: string + default: row2col + enum: + - row2col + - col2row diff --git a/app/drivers/zephyr/kscan_gpio.c b/app/drivers/zephyr/kscan_gpio.c deleted file mode 100644 index 76606d8..0000000 --- a/app/drivers/zephyr/kscan_gpio.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (c) 2020 Peter Johanson - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT gpio_kscan - -#include -#include -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -struct kscan_gpio_item_config -{ - char *label; - gpio_pin_t pin; - gpio_flags_t flags; -}; - -#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \ - { \ - .label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \ - .pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \ - .flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \ - }, - -static int kscan_gpio_config_interrupts(struct device **devices, - const struct kscan_gpio_item_config *configs, - size_t len, gpio_flags_t flags) -{ - for (int i = 0; i < len; i++) - { - struct device *dev = devices[i]; - const struct kscan_gpio_item_config *cfg = &configs[i]; - - int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags); - - if (err) - { - LOG_ERR("Unable to enable matrix GPIO interrupt"); - return err; - } - } - - return 0; -} -#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios) -#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios) -#define INST_OUTPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_COLS(n)), (INST_MATRIX_ROWS(n))) -#define INST_INPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), (INST_MATRIX_COLS(n))) - -#define GPIO_INST_INIT(n) \ - struct kscan_gpio_irq_callback_##n \ - { \ - struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \ - struct gpio_callback callback; \ - }; \ - static struct kscan_gpio_irq_callback_##n \ - irq_callbacks_##n[INST_INPUT_LEN(n)]; \ - struct kscan_gpio_config_##n \ - { \ - struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \ - struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \ - }; \ - struct kscan_gpio_data_##n \ - { \ - kscan_callback_t callback; \ - struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \ - bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \ - struct device *rows[INST_MATRIX_ROWS(n)]; \ - struct device *cols[INST_MATRIX_COLS(n)]; \ - struct device *dev; \ - }; \ - static struct device **kscan_gpio_input_devices_##n(struct device *dev) \ - { \ - struct kscan_gpio_data_##n *data = dev->driver_data; \ - return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), (data->rows))); \ - } \ - static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n(struct device *dev) \ - { \ - const struct kscan_gpio_config_##n *cfg = dev->config_info; \ - return ((COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \ - } \ - static struct device **kscan_gpio_output_devices_##n(struct device *dev) \ - { \ - struct kscan_gpio_data_##n *data = dev->driver_data; \ - return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), (data->cols))); \ - } \ - static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n(struct device *dev) \ - { \ - const struct kscan_gpio_config_##n *cfg = dev->config_info; \ - return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \ - } \ - static int kscan_gpio_enable_interrupts_##n(struct device *dev) \ - { \ - return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \ - GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH); \ - } \ - static int kscan_gpio_disable_interrupts_##n(struct device *dev) \ - { \ - return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \ - GPIO_INT_DISABLE); \ - } \ - static void kscan_gpio_set_output_state_##n(struct device *dev, int value) \ - { \ - for (int i = 0; i < INST_OUTPUT_LEN(n); i++) \ - { \ - struct device *in_dev = kscan_gpio_output_devices_##n(dev)[i]; \ - const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \ - gpio_pin_set(in_dev, cfg->pin, value); \ - } \ - } \ - static void kscan_gpio_set_matrix_state_##n(bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], u32_t input_index, u32_t output_index, bool value) \ - { \ - state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), (input_index))][COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), (output_index))] = value; \ - } \ - static int kscan_gpio_read_##n(struct device *dev) \ - { \ - struct kscan_gpio_data_##n *data = dev->driver_data; \ - static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \ - LOG_DBG("Scanning the matrix for updated state"); \ - /* Disable our interrupts temporarily while we scan, to avoid */ \ - /* re-entry while we iterate columns and set them active one by one */ \ - /* to get pressed state for each matrix cell. */ \ - kscan_gpio_disable_interrupts_##n(dev); \ - kscan_gpio_set_output_state_##n(dev, 0); \ - for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \ - { \ - struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \ - const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \ - gpio_pin_set(out_dev, out_cfg->pin, 1); \ - for (int i = 0; i < INST_INPUT_LEN(n); i++) \ - { \ - struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \ - const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \ - kscan_gpio_set_matrix_state_##n(read_state, i, o, gpio_pin_get(in_dev, in_cfg->pin) > 0); \ - } \ - gpio_pin_set(out_dev, out_cfg->pin, 0); \ - } \ - /* Set all our outputs as active again, then re-enable interrupts, */ \ - /* so we can trigger interrupts again for future press/release */ \ - kscan_gpio_set_output_state_##n(dev, 1); \ - kscan_gpio_enable_interrupts_##n(dev); \ - for (int r = 0; r < INST_MATRIX_ROWS(n); r++) \ - { \ - for (int c = 0; c < INST_MATRIX_COLS(n); c++) \ - { \ - bool pressed = read_state[r][c]; \ - if (pressed != data->matrix_state[r][c]) \ - { \ - LOG_DBG("Sending event at %d,%d state %s", \ - r, c, (pressed ? "on" : "off")); \ - data->matrix_state[r][c] = pressed; \ - data->callback(dev, r, c, pressed); \ - } \ - } \ - } \ - return 0; \ - } \ - static void kscan_gpio_work_handler_##n(struct k_work *work) \ - { \ - struct kscan_gpio_data_##n *data = \ - CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \ - kscan_gpio_read_##n(data->dev); \ - } \ - static void kscan_gpio_irq_callback_handler_##n(struct device *dev, \ - struct gpio_callback *cb, gpio_port_pins_t pin) \ - { \ - struct kscan_gpio_irq_callback_##n *data = \ - CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \ - COND_CODE_0(DT_INST_PROP(n, debounce_period), \ - ({ k_work_submit(data->work); }), \ - ({ \ - k_delayed_work_cancel(data->work); \ - k_delayed_work_submit(data->work, K_MSEC(DT_INST_PROP(n, debounce_period))); })) \ - } \ - static struct kscan_gpio_data_##n kscan_gpio_data_##n; \ - static int kscan_gpio_configure_##n(struct device *dev, kscan_callback_t callback) \ - { \ - struct kscan_gpio_data_##n *data = dev->driver_data; \ - if (!callback) \ - { \ - return -EINVAL; \ - } \ - data->callback = callback; \ - return 0; \ - } \ - static int kscan_gpio_init_##n(struct device *dev) \ - { \ - struct kscan_gpio_data_##n *data = dev->driver_data; \ - int err; \ - struct device **input_devices = kscan_gpio_input_devices_##n(dev); \ - for (int i = 0; i < INST_INPUT_LEN(n); i++) \ - { \ - const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \ - input_devices[i] = device_get_binding(in_cfg->label); \ - if (!input_devices[i]) \ - { \ - LOG_ERR("Unable to find input GPIO device"); \ - return -EINVAL; \ - } \ - err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \ - if (err) \ - { \ - LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \ - return err; \ - } \ - irq_callbacks_##n[i].work = &data->work; \ - gpio_init_callback(&irq_callbacks_##n[i].callback, kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \ - err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \ - if (err) \ - { \ - LOG_ERR("Error adding the callback to the column device"); \ - return err; \ - } \ - } \ - struct device **output_devices = kscan_gpio_output_devices_##n(dev); \ - for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \ - { \ - const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \ - output_devices[o] = device_get_binding(out_cfg->label); \ - if (!output_devices[o]) \ - { \ - LOG_ERR("Unable to find output GPIO device"); \ - return -EINVAL; \ - } \ - err = gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT_ACTIVE | out_cfg->flags); \ - if (err) \ - { \ - LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, out_cfg->label); \ - return err; \ - } \ - } \ - data->dev = dev; \ - (COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))(&data->work, kscan_gpio_work_handler_##n); \ - return 0; \ - } \ - static const struct kscan_driver_api gpio_driver_api_##n = { \ - .config = kscan_gpio_configure_##n, \ - .enable_callback = kscan_gpio_enable_interrupts_##n, \ - .disable_callback = kscan_gpio_disable_interrupts_##n, \ - }; \ - static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \ - .rows = { \ - IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, row_gpios, 0, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, 0))) \ - IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, row_gpios, 1, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, 1)))}, \ - .cols = {IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, col_gpios, 0, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, 0))) IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, col_gpios, 1, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, 1)))}}; \ - DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \ - &kscan_gpio_data_##n, &kscan_gpio_config_##n, \ - POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \ - &gpio_driver_api_##n); - -DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT) diff --git a/app/drivers/zephyr/kscan_gpio_matrix.c b/app/drivers/zephyr/kscan_gpio_matrix.c new file mode 100644 index 0000000..22217ef --- /dev/null +++ b/app/drivers/zephyr/kscan_gpio_matrix.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020 Peter Johanson + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_kscan_gpio_matrix + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +struct kscan_gpio_item_config +{ + char *label; + gpio_pin_t pin; + gpio_flags_t flags; +}; + +#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \ + { \ + .label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \ + .pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \ + .flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \ + }, + +static int kscan_gpio_config_interrupts(struct device **devices, + const struct kscan_gpio_item_config *configs, + size_t len, gpio_flags_t flags) +{ + for (int i = 0; i < len; i++) + { + struct device *dev = devices[i]; + const struct kscan_gpio_item_config *cfg = &configs[i]; + + int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags); + + if (err) + { + LOG_ERR("Unable to enable matrix GPIO interrupt"); + return err; + } + } + + return 0; +} +#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios) +#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios) +#define INST_OUTPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_COLS(n)), (INST_MATRIX_ROWS(n))) +#define INST_INPUT_LEN(n) COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), (INST_MATRIX_COLS(n))) + +#define GPIO_INST_INIT(n) \ + struct kscan_gpio_irq_callback_##n \ + { \ + struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \ + struct gpio_callback callback; \ + }; \ + static struct kscan_gpio_irq_callback_##n \ + irq_callbacks_##n[INST_INPUT_LEN(n)]; \ + struct kscan_gpio_config_##n \ + { \ + struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \ + struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \ + }; \ + struct kscan_gpio_data_##n \ + { \ + kscan_callback_t callback; \ + struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \ + bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \ + struct device *rows[INST_MATRIX_ROWS(n)]; \ + struct device *cols[INST_MATRIX_COLS(n)]; \ + struct device *dev; \ + }; \ + static struct device **kscan_gpio_input_devices_##n(struct device *dev) \ + { \ + struct kscan_gpio_data_##n *data = dev->driver_data; \ + return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), (data->rows))); \ + } \ + static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n(struct device *dev) \ + { \ + const struct kscan_gpio_config_##n *cfg = dev->config_info; \ + return ((COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \ + } \ + static struct device **kscan_gpio_output_devices_##n(struct device *dev) \ + { \ + struct kscan_gpio_data_##n *data = dev->driver_data; \ + return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), (data->cols))); \ + } \ + static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n(struct device *dev) \ + { \ + const struct kscan_gpio_config_##n *cfg = dev->config_info; \ + return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \ + } \ + static int kscan_gpio_enable_interrupts_##n(struct device *dev) \ + { \ + return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \ + GPIO_INT_DEBOUNCE | GPIO_INT_EDGE_BOTH); \ + } \ + static int kscan_gpio_disable_interrupts_##n(struct device *dev) \ + { \ + return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), INST_INPUT_LEN(n), \ + GPIO_INT_DISABLE); \ + } \ + static void kscan_gpio_set_output_state_##n(struct device *dev, int value) \ + { \ + for (int i = 0; i < INST_OUTPUT_LEN(n); i++) \ + { \ + struct device *in_dev = kscan_gpio_output_devices_##n(dev)[i]; \ + const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \ + gpio_pin_set(in_dev, cfg->pin, value); \ + } \ + } \ + static void kscan_gpio_set_matrix_state_##n(bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], u32_t input_index, u32_t output_index, bool value) \ + { \ + state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), (input_index))][COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), (output_index))] = value; \ + } \ + static int kscan_gpio_read_##n(struct device *dev) \ + { \ + struct kscan_gpio_data_##n *data = dev->driver_data; \ + static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \ + LOG_DBG("Scanning the matrix for updated state"); \ + /* Disable our interrupts temporarily while we scan, to avoid */ \ + /* re-entry while we iterate columns and set them active one by one */ \ + /* to get pressed state for each matrix cell. */ \ + kscan_gpio_disable_interrupts_##n(dev); \ + kscan_gpio_set_output_state_##n(dev, 0); \ + for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \ + { \ + struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \ + const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \ + gpio_pin_set(out_dev, out_cfg->pin, 1); \ + for (int i = 0; i < INST_INPUT_LEN(n); i++) \ + { \ + struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \ + const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \ + kscan_gpio_set_matrix_state_##n(read_state, i, o, gpio_pin_get(in_dev, in_cfg->pin) > 0); \ + } \ + gpio_pin_set(out_dev, out_cfg->pin, 0); \ + } \ + /* Set all our outputs as active again, then re-enable interrupts, */ \ + /* so we can trigger interrupts again for future press/release */ \ + kscan_gpio_set_output_state_##n(dev, 1); \ + kscan_gpio_enable_interrupts_##n(dev); \ + for (int r = 0; r < INST_MATRIX_ROWS(n); r++) \ + { \ + for (int c = 0; c < INST_MATRIX_COLS(n); c++) \ + { \ + bool pressed = read_state[r][c]; \ + if (pressed != data->matrix_state[r][c]) \ + { \ + LOG_DBG("Sending event at %d,%d state %s", \ + r, c, (pressed ? "on" : "off")); \ + data->matrix_state[r][c] = pressed; \ + data->callback(dev, r, c, pressed); \ + } \ + } \ + } \ + return 0; \ + } \ + static void kscan_gpio_work_handler_##n(struct k_work *work) \ + { \ + struct kscan_gpio_data_##n *data = \ + CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \ + kscan_gpio_read_##n(data->dev); \ + } \ + static void kscan_gpio_irq_callback_handler_##n(struct device *dev, \ + struct gpio_callback *cb, gpio_port_pins_t pin) \ + { \ + struct kscan_gpio_irq_callback_##n *data = \ + CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \ + COND_CODE_0(DT_INST_PROP(n, debounce_period), \ + ({ k_work_submit(data->work); }), \ + ({ \ + k_delayed_work_cancel(data->work); \ + k_delayed_work_submit(data->work, K_MSEC(DT_INST_PROP(n, debounce_period))); })) \ + } \ + static struct kscan_gpio_data_##n kscan_gpio_data_##n; \ + static int kscan_gpio_configure_##n(struct device *dev, kscan_callback_t callback) \ + { \ + struct kscan_gpio_data_##n *data = dev->driver_data; \ + if (!callback) \ + { \ + return -EINVAL; \ + } \ + data->callback = callback; \ + return 0; \ + } \ + static int kscan_gpio_init_##n(struct device *dev) \ + { \ + struct kscan_gpio_data_##n *data = dev->driver_data; \ + int err; \ + struct device **input_devices = kscan_gpio_input_devices_##n(dev); \ + for (int i = 0; i < INST_INPUT_LEN(n); i++) \ + { \ + const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \ + input_devices[i] = device_get_binding(in_cfg->label); \ + if (!input_devices[i]) \ + { \ + LOG_ERR("Unable to find input GPIO device"); \ + return -EINVAL; \ + } \ + err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \ + if (err) \ + { \ + LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \ + return err; \ + } \ + irq_callbacks_##n[i].work = &data->work; \ + gpio_init_callback(&irq_callbacks_##n[i].callback, kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \ + err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \ + if (err) \ + { \ + LOG_ERR("Error adding the callback to the column device"); \ + return err; \ + } \ + } \ + struct device **output_devices = kscan_gpio_output_devices_##n(dev); \ + for (int o = 0; o < INST_OUTPUT_LEN(n); o++) \ + { \ + const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \ + output_devices[o] = device_get_binding(out_cfg->label); \ + if (!output_devices[o]) \ + { \ + LOG_ERR("Unable to find output GPIO device"); \ + return -EINVAL; \ + } \ + err = gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT_ACTIVE | out_cfg->flags); \ + if (err) \ + { \ + LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, out_cfg->label); \ + return err; \ + } \ + } \ + data->dev = dev; \ + (COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))(&data->work, kscan_gpio_work_handler_##n); \ + return 0; \ + } \ + static const struct kscan_driver_api gpio_driver_api_##n = { \ + .config = kscan_gpio_configure_##n, \ + .enable_callback = kscan_gpio_enable_interrupts_##n, \ + .disable_callback = kscan_gpio_disable_interrupts_##n, \ + }; \ + static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \ + .rows = { \ + IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, row_gpios, 0, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, 0))) \ + IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, row_gpios, 1, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, 1)))}, \ + .cols = {IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, col_gpios, 0, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, 0))) IF_ENABLED(DT_INST_PHA_HAS_CELL_AT_IDX(n, col_gpios, 1, pin), (_KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, 1)))}}; \ + DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \ + &kscan_gpio_data_##n, &kscan_gpio_config_##n, \ + POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \ + &gpio_driver_api_##n); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/include/zmk/matrix.h b/app/include/zmk/matrix.h index 783c98e..5397cfb 100644 --- a/app/include/zmk/matrix.h +++ b/app/include/zmk/matrix.h @@ -5,6 +5,9 @@ #if DT_NODE_HAS_PROP(ZMK_MATRIX_NODE_ID,row_gpios) #define ZMK_MATRIX_ROWS DT_PROP_LEN(ZMK_MATRIX_NODE_ID,row_gpios) #define ZMK_MATRIX_COLS DT_PROP_LEN(ZMK_MATRIX_NODE_ID,col_gpios) +#elif DT_NODE_HAS_PROP(ZMK_MATRIX_NODE_ID,input_gpios) +#define ZMK_MATRIX_ROWS 1 +#define ZMK_MATRIX_COLS DT_PROP_LEN(ZMK_MATRIX_NODE_ID,input_gpios) #else #define ZMK_MATRIX_ROWS DT_PROP(ZMK_MATRIX_NODE_ID,rows) #define ZMK_MATRIX_COLS DT_PROP(ZMK_MATRIX_NODE_ID,columns) -- cgit v1.2.3