Switch naming to charliplex

This commit is contained in:
Hooky 2023-03-04 12:49:32 +08:00 committed by Peter Johanson
parent a25505a75a
commit 087774c783
5 changed files with 112 additions and 110 deletions

View file

@ -5,8 +5,8 @@ zephyr_library_amend()
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MATRIX kscan_gpio_matrix.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MATRIX kscan_gpio_matrix.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_CHARLIPLEX kscan_gpio_charliplex.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DEMUX kscan_gpio_demux.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DEMUX kscan_gpio_demux.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_MOCK_DRIVER kscan_mock.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_MOCK_DRIVER kscan_mock.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER kscan_composite.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER kscan_composite.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MULTIPLEX kscan_gpio_multiplex.c)

View file

@ -5,7 +5,7 @@ DT_COMPAT_ZMK_KSCAN_COMPOSITE := zmk,kscan-composite
DT_COMPAT_ZMK_KSCAN_GPIO_DEMUX := zmk,kscan-gpio-demux DT_COMPAT_ZMK_KSCAN_GPIO_DEMUX := zmk,kscan-gpio-demux
DT_COMPAT_ZMK_KSCAN_GPIO_DIRECT := zmk,kscan-gpio-direct DT_COMPAT_ZMK_KSCAN_GPIO_DIRECT := zmk,kscan-gpio-direct
DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX := zmk,kscan-gpio-matrix DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX := zmk,kscan-gpio-matrix
DT_COMPAT_ZMK_KSCAN_GPIO_MULTIPLEX := zmk,kscan-gpio-multiplex DT_COMPAT_ZMK_KSCAN_GPIO_CHARLIPLEX := zmk,kscan-gpio-charliplex
DT_COMPAT_ZMK_KSCAN_MOCK := zmk,kscan-mock DT_COMPAT_ZMK_KSCAN_MOCK := zmk,kscan-mock
if KSCAN if KSCAN
@ -34,9 +34,9 @@ config ZMK_KSCAN_GPIO_MATRIX
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX)) default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_MATRIX))
select ZMK_KSCAN_GPIO_DRIVER select ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_GPIO_MULTIPLEX config ZMK_KSCAN_GPIO_CHARLIPLEX
bool bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_MULTIPLEX)) default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_CHARLIPLEX))
select ZMK_KSCAN_GPIO_DRIVER select ZMK_KSCAN_GPIO_DRIVER
if ZMK_KSCAN_GPIO_MATRIX if ZMK_KSCAN_GPIO_MATRIX
@ -64,7 +64,7 @@ config ZMK_KSCAN_MATRIX_WAIT_BETWEEN_OUTPUTS
endif # ZMK_KSCAN_GPIO_MATRIX endif # ZMK_KSCAN_GPIO_MATRIX
if ZMK_KSCAN_GPIO_MULTIPLEX if ZMK_KSCAN_GPIO_CHARLIPLEX
config ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS config ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS
int "Ticks to wait before reading inputs after an output set active" int "Ticks to wait before reading inputs after an output set active"
@ -76,8 +76,8 @@ config ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS
the number of ticks to wait after setting an output active before reading the number of ticks to wait after setting an output active before reading
the inputs for their active state. the inputs for their active state.
config ZMK_KSCAN_MULTIPLEX_WAIT_BETWEEN_OUTPUTS config ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS
int "Ticks to wait between each output when scanning round robin matrix" int "Ticks to wait between each output when scanning charliplex matrix"
default 0 default 0
help help
When iterating over each output to drive it active, read inputs, then set When iterating over each output to drive it active, read inputs, then set
@ -86,7 +86,7 @@ config ZMK_KSCAN_MULTIPLEX_WAIT_BETWEEN_OUTPUTS
scenario, set this value to a positive value to configure the number of scenario, set this value to a positive value to configure the number of
usecs to wait after reading each column of keys. usecs to wait after reading each column of keys.
endif # ZMK_KSCAN_GPIO_MULTIPLEX endif # ZMK_KSCAN_GPIO_CHARLIPLEX
config ZMK_KSCAN_MOCK_DRIVER config ZMK_KSCAN_MOCK_DRIVER
bool bool
@ -97,7 +97,7 @@ if ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_MATRIX_POLLING config ZMK_KSCAN_MATRIX_POLLING
bool "Poll for key event triggers instead of using interrupts on matrix boards." bool "Poll for key event triggers instead of using interrupts on matrix boards."
config ZMK_KSCAN_MULTIPLEX_POLLING config ZMK_KSCAN_CHARLIPLEX_POLLING
bool "Poll for key event triggers instead of using interrupts on multiplex boards." bool "Poll for key event triggers instead of using interrupts on multiplex boards."
config ZMK_KSCAN_DIRECT_POLLING config ZMK_KSCAN_DIRECT_POLLING

View file

@ -17,10 +17,10 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define DT_DRV_COMPAT zmk_kscan_gpio_multiplex #define DT_DRV_COMPAT zmk_kscan_gpio_charliplex
#define INST_LEN(n) DT_INST_PROP_LEN(n, gpios) #define INST_LEN(n) DT_INST_PROP_LEN(n, gpios)
#define INST_MULTIPLEX_LEN(n) (INST_LEN(n) * INST_LEN(n)) #define INST_CHARLIPLEX_LEN(n) (INST_LEN(n) * INST_LEN(n))
#if CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS >= 0 #if CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS >= 0
#define INST_DEBOUNCE_PRESS_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS #define INST_DEBOUNCE_PRESS_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS
@ -36,17 +36,17 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_release_ms)) DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_release_ms))
#endif #endif
#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_MULTIPLEX_POLLING) #define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_CHARLIPLEX_POLLING)
#define USE_INTERRUPT (!USE_POLLING) #define USE_INTERRUPT (!USE_POLLING)
#define COND_INTERRUPT(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MULTIPLEX_POLLING, (), code) #define COND_INTERRUPT(code) COND_CODE_1(CONFIG_ZMK_KSCAN_CHARLIPLEX_POLLING, (), code)
#define KSCAN_GPIO_CFG_INIT(idx, inst_idx) \ #define KSCAN_GPIO_CFG_INIT(idx, inst_idx) \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), gpios, idx), GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), gpios, idx),
#define KSCAN_INTR_CFG_INIT(inst_idx) GPIO_DT_SPEC_GET(DT_DRV_INST(inst_idx), interrupt_gpios) #define KSCAN_INTR_CFG_INIT(inst_idx) GPIO_DT_SPEC_GET(DT_DRV_INST(inst_idx), interrupt_gpios)
struct kscan_multiplex_data { struct kscan_charliplex_data {
const struct device *dev; const struct device *dev;
kscan_callback_t callback; kscan_callback_t callback;
struct k_work_delayable work; struct k_work_delayable work;
@ -58,7 +58,7 @@ struct kscan_multiplex_data {
* Current state of the matrix as a flattened 2D array of length * Current state of the matrix as a flattened 2D array of length
* (config->cells.length ^2) * (config->cells.length ^2)
*/ */
struct debounce_state *multiplex_state; struct debounce_state *charliplex_state;
}; };
struct kscan_gpio_list { struct kscan_gpio_list {
@ -70,7 +70,7 @@ struct kscan_gpio_list {
#define KSCAN_GPIO_LIST(gpio_array) \ #define KSCAN_GPIO_LIST(gpio_array) \
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)}) ((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
struct kscan_multiplex_config { struct kscan_charliplex_config {
struct kscan_gpio_list cells; struct kscan_gpio_list cells;
struct debounce_config debounce_config; struct debounce_config debounce_config;
int32_t debounce_scan_period_ms; int32_t debounce_scan_period_ms;
@ -85,7 +85,7 @@ struct kscan_multiplex_config {
* There are effectively (n) cols and (n-1) rows, but we use the full col x row space * There are effectively (n) cols and (n-1) rows, but we use the full col x row space
* as a safety measure against someone accidentally defining a transform RC at (p,p) * as a safety measure against someone accidentally defining a transform RC at (p,p)
*/ */
static int state_index(const struct kscan_multiplex_config *config, const int row, const int col) { static int state_index(const struct kscan_charliplex_config *config, const int row, const int col) {
__ASSERT(row < config->cells.len, "Invalid row %i", row); __ASSERT(row < config->cells.len, "Invalid row %i", row);
__ASSERT(col < config->cells.len, "Invalid column %i", col); __ASSERT(col < config->cells.len, "Invalid column %i", col);
__ASSERT(col != row, "Invalid column row pair %i, %i", col, row); __ASSERT(col != row, "Invalid column row pair %i, %i", col, row);
@ -93,7 +93,7 @@ static int state_index(const struct kscan_multiplex_config *config, const int ro
return (col * config->cells.len) + row; return (col * config->cells.len) + row;
} }
static int kscan_multiplex_set_as_input(const struct gpio_dt_spec *gpio) { static int kscan_charliplex_set_as_input(const struct gpio_dt_spec *gpio) {
if (!device_is_ready(gpio->port)) { if (!device_is_ready(gpio->port)) {
LOG_ERR("GPIO is not ready: %s", gpio->port->name); LOG_ERR("GPIO is not ready: %s", gpio->port->name);
return -ENODEV; return -ENODEV;
@ -107,7 +107,7 @@ static int kscan_multiplex_set_as_input(const struct gpio_dt_spec *gpio) {
return 0; return 0;
} }
static int kscan_multiplex_set_as_output(const struct gpio_dt_spec *gpio) { static int kscan_charliplex_set_as_output(const struct gpio_dt_spec *gpio) {
if (!device_is_ready(gpio->port)) { if (!device_is_ready(gpio->port)) {
LOG_ERR("GPIO is not ready: %s", gpio->port->name); LOG_ERR("GPIO is not ready: %s", gpio->port->name);
return -ENODEV; return -ENODEV;
@ -126,11 +126,11 @@ static int kscan_multiplex_set_as_output(const struct gpio_dt_spec *gpio) {
return err; return err;
} }
static int kscan_multiplex_set_all_as_input(const struct device *dev) { static int kscan_charliplex_set_all_as_input(const struct device *dev) {
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
int err = 0; int err = 0;
for (int i = 0; i < config->cells.len; i++) { for (int i = 0; i < config->cells.len; i++) {
err = kscan_multiplex_set_as_input(&config->cells.gpios[i]); err = kscan_charliplex_set_as_input(&config->cells.gpios[i]);
if (err) { if (err) {
return err; return err;
} }
@ -139,8 +139,8 @@ static int kscan_multiplex_set_all_as_input(const struct device *dev) {
return 0; return 0;
} }
static int kscan_multiplex_set_all_outputs(const struct device *dev, const int value) { static int kscan_charliplex_set_all_outputs(const struct device *dev, const int value) {
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
for (int i = 0; i < config->cells.len; i++) { for (int i = 0; i < config->cells.len; i++) {
const struct gpio_dt_spec *gpio = &config->cells.gpios[i]; const struct gpio_dt_spec *gpio = &config->cells.gpios[i];
@ -161,8 +161,9 @@ static int kscan_multiplex_set_all_outputs(const struct device *dev, const int v
} }
#if USE_INTERRUPT #if USE_INTERRUPT
static int kscan_multiplex_interrupt_configure(const struct device *dev, const gpio_flags_t flags) { static int kscan_charliplex_interrupt_configure(const struct device *dev,
const struct kscan_multiplex_config *config = dev->config; const gpio_flags_t flags) {
const struct kscan_charliplex_config *config = dev->config;
const struct gpio_dt_spec *gpio = &config->interrupt; const struct gpio_dt_spec *gpio = &config->interrupt;
int err = gpio_pin_interrupt_configure_dt(gpio, flags); int err = gpio_pin_interrupt_configure_dt(gpio, flags);
@ -176,45 +177,46 @@ static int kscan_multiplex_interrupt_configure(const struct device *dev, const g
#endif #endif
#if USE_INTERRUPT #if USE_INTERRUPT
static int kscan_multiplex_interrupt_enable(const struct device *dev) { static int kscan_charliplex_interrupt_enable(const struct device *dev) {
int err = kscan_multiplex_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE); int err = kscan_charliplex_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE);
if (err) { if (err) {
return err; return err;
} }
// While interrupts are enabled, set all outputs active so an pressed key will trigger // While interrupts are enabled, set all outputs active so an pressed key will trigger
return kscan_multiplex_set_all_outputs(dev, 1); return kscan_charliplex_set_all_outputs(dev, 1);
} }
#endif #endif
#if USE_INTERRUPT #if USE_INTERRUPT
static void kscan_multiplex_irq_callback(const struct device *port, struct gpio_callback *cb, static void kscan_charliplex_irq_callback(const struct device *port, struct gpio_callback *cb,
const gpio_port_pins_t _pin) { const gpio_port_pins_t _pin) {
struct kscan_multiplex_data *data = CONTAINER_OF(cb, struct kscan_multiplex_data, irq_callback); struct kscan_charliplex_data *data =
CONTAINER_OF(cb, struct kscan_charliplex_data, irq_callback);
// Disable our interrupt to avoid re-entry while we scan. // Disable our interrupt to avoid re-entry while we scan.
kscan_multiplex_interrupt_configure(data->dev, GPIO_INT_DISABLE); kscan_charliplex_interrupt_configure(data->dev, GPIO_INT_DISABLE);
data->scan_time = k_uptime_get(); data->scan_time = k_uptime_get();
k_work_reschedule(&data->work, K_NO_WAIT); k_work_reschedule(&data->work, K_NO_WAIT);
} }
#endif #endif
static void kscan_multiplex_read_continue(const struct device *dev) { static void kscan_charliplex_read_continue(const struct device *dev) {
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
data->scan_time += config->debounce_scan_period_ms; data->scan_time += config->debounce_scan_period_ms;
k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time)); k_work_reschedule(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
} }
static void kscan_multiplex_read_end(const struct device *dev) { static void kscan_charliplex_read_end(const struct device *dev) {
#if USE_INTERRUPT #if USE_INTERRUPT
// Return to waiting for an interrupt. // Return to waiting for an interrupt.
kscan_multiplex_interrupt_enable(dev); kscan_charliplex_interrupt_enable(dev);
#else #else
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
data->scan_time += config->poll_period_ms; data->scan_time += config->poll_period_ms;
@ -223,14 +225,14 @@ static void kscan_multiplex_read_end(const struct device *dev) {
#endif #endif
} }
static int kscan_multiplex_read(const struct device *dev) { static int kscan_charliplex_read(const struct device *dev) {
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
bool continue_scan = false; bool continue_scan = false;
// NOTE: MULTI vs MATRIX: set all pins as input, in case there was a failure on a // NOTE: RR vs MATRIX: set all pins as input, in case there was a failure on a
// previous scan, and one of the pins is still set as output // previous scan, and one of the pins is still set as output
int err = kscan_multiplex_set_all_as_input(dev); int err = kscan_charliplex_set_all_as_input(dev);
if (err) { if (err) {
return err; return err;
} }
@ -238,13 +240,13 @@ static int kscan_multiplex_read(const struct device *dev) {
// Scan the matrix. // Scan the matrix.
for (int row = 0; row < config->cells.len; row++) { for (int row = 0; row < config->cells.len; row++) {
const struct gpio_dt_spec *out_gpio = &config->cells.gpios[row]; const struct gpio_dt_spec *out_gpio = &config->cells.gpios[row];
err = kscan_multiplex_set_as_output(out_gpio); err = kscan_charliplex_set_as_output(out_gpio);
if (err) { if (err) {
return err; return err;
} }
#if CONFIG_ZMK_KSCAN_MULTIPLEX_WAIT_BEFORE_INPUTS > 0 #if CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BEFORE_INPUTS > 0
k_busy_wait(CONFIG_ZMK_KSCAN_MULTIPLEX_WAIT_BEFORE_INPUTS); k_busy_wait(CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BEFORE_INPUTS);
#endif #endif
for (int col = 0; col < config->cells.len; col++) { for (int col = 0; col < config->cells.len; col++) {
@ -254,11 +256,11 @@ static int kscan_multiplex_read(const struct device *dev) {
const struct gpio_dt_spec *in_gpio = &config->cells.gpios[col]; const struct gpio_dt_spec *in_gpio = &config->cells.gpios[col];
const int index = state_index(config, row, col); const int index = state_index(config, row, col);
struct debounce_state *state = &data->multiplex_state[index]; struct debounce_state *state = &data->charliplex_state[index];
debounce_update(state, gpio_pin_get_dt(in_gpio), config->debounce_scan_period_ms, debounce_update(state, gpio_pin_get_dt(in_gpio), config->debounce_scan_period_ms,
&config->debounce_config); &config->debounce_config);
// NOTE: MULTI vs MATRIX: because we don't need an input/output => row/column // NOTE: RR vs MATRIX: because we don't need an input/output => row/column
// setup, we can update in the same loop. // setup, we can update in the same loop.
if (debounce_get_changed(state)) { if (debounce_get_changed(state)) {
const bool pressed = debounce_is_pressed(state); const bool pressed = debounce_is_pressed(state);
@ -269,67 +271,67 @@ static int kscan_multiplex_read(const struct device *dev) {
continue_scan = continue_scan || debounce_is_active(state); continue_scan = continue_scan || debounce_is_active(state);
} }
err = kscan_multiplex_set_as_input(out_gpio); err = kscan_charliplex_set_as_input(out_gpio);
if (err) { if (err) {
return err; return err;
} }
#if CONFIG_ZMK_KSCAN_MULTIPLEX_WAIT_BETWEEN_OUTPUTS > 0 #if CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS > 0
k_busy_wait(CONFIG_ZMK_KSCAN_MULTIPLEX_WAIT_BETWEEN_OUTPUTS); k_busy_wait(CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS);
#endif #endif
} }
if (continue_scan) { if (continue_scan) {
// At least one key is pressed or the debouncer has not yet decided if // At least one key is pressed or the debouncer has not yet decided if
// it is pressed. Poll quickly until everything is released. // it is pressed. Poll quickly until everything is released.
kscan_multiplex_read_continue(dev); kscan_charliplex_read_continue(dev);
} else { } else {
// All keys are released. Return to normal. // All keys are released. Return to normal.
kscan_multiplex_read_end(dev); kscan_charliplex_read_end(dev);
} }
return 0; return 0;
} }
static void kscan_multiplex_work_handler(struct k_work *work) { static void kscan_charliplex_work_handler(struct k_work *work) {
struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work); struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work);
struct kscan_multiplex_data *data = CONTAINER_OF(dwork, struct kscan_multiplex_data, work); struct kscan_charliplex_data *data = CONTAINER_OF(dwork, struct kscan_charliplex_data, work);
kscan_multiplex_read(data->dev); kscan_charliplex_read(data->dev);
} }
static int kscan_multiplex_configure(const struct device *dev, const kscan_callback_t callback) { static int kscan_charliplex_configure(const struct device *dev, const kscan_callback_t callback) {
if (!callback) { if (!callback) {
return -EINVAL; return -EINVAL;
} }
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
data->callback = callback; data->callback = callback;
return 0; return 0;
} }
static int kscan_multiplex_enable(const struct device *dev) { static int kscan_charliplex_enable(const struct device *dev) {
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
data->scan_time = k_uptime_get(); data->scan_time = k_uptime_get();
// Read will automatically start interrupts/polling once done. // Read will automatically start interrupts/polling once done.
return kscan_multiplex_read(dev); return kscan_charliplex_read(dev);
} }
static int kscan_multiplex_disable(const struct device *dev) { static int kscan_charliplex_disable(const struct device *dev) {
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
k_work_cancel_delayable(&data->work); k_work_cancel_delayable(&data->work);
#if USE_INTERRUPT #if USE_INTERRUPT
return kscan_multiplex_interrupt_configure(dev, GPIO_INT_DISABLE); return kscan_charliplex_interrupt_configure(dev, GPIO_INT_DISABLE);
#else #else
return 0; return 0;
#endif #endif
} }
static int kscan_multiplex_init_inputs(const struct device *dev) { static int kscan_charliplex_init_inputs(const struct device *dev) {
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
for (int i = 0; i < config->cells.len; i++) { for (int i = 0; i < config->cells.len; i++) {
int err = kscan_multiplex_set_as_input(&config->cells.gpios[i]); int err = kscan_charliplex_set_as_input(&config->cells.gpios[i]);
if (err) { if (err) {
return err; return err;
} }
@ -339,17 +341,17 @@ static int kscan_multiplex_init_inputs(const struct device *dev) {
} }
#if USE_INTERRUPT #if USE_INTERRUPT
static int kscan_multiplex_init_interrupt(const struct device *dev) { static int kscan_charliplex_init_interrupt(const struct device *dev) {
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
const struct kscan_multiplex_config *config = dev->config; const struct kscan_charliplex_config *config = dev->config;
const struct gpio_dt_spec *gpio = &config->interrupt; const struct gpio_dt_spec *gpio = &config->interrupt;
int err = kscan_multiplex_set_as_input(gpio); int err = kscan_charliplex_set_as_input(gpio);
if (err) { if (err) {
return err; return err;
} }
gpio_init_callback(&data->irq_callback, kscan_multiplex_irq_callback, BIT(gpio->pin)); gpio_init_callback(&data->irq_callback, kscan_charliplex_irq_callback, BIT(gpio->pin));
err = gpio_add_callback(gpio->port, &data->irq_callback); err = gpio_add_callback(gpio->port, &data->irq_callback);
if (err) { if (err) {
LOG_ERR("Error adding the callback to the input device: %i", err); LOG_ERR("Error adding the callback to the input device: %i", err);
@ -358,42 +360,42 @@ static int kscan_multiplex_init_interrupt(const struct device *dev) {
} }
#endif #endif
static int kscan_multiplex_init(const struct device *dev) { static int kscan_charliplex_init(const struct device *dev) {
struct kscan_multiplex_data *data = dev->data; struct kscan_charliplex_data *data = dev->data;
data->dev = dev; data->dev = dev;
kscan_multiplex_init_inputs(dev); kscan_charliplex_init_inputs(dev);
kscan_multiplex_set_all_outputs(dev, 0); kscan_charliplex_set_all_outputs(dev, 0);
#if USE_INTERRUPT #if USE_INTERRUPT
kscan_multiplex_init_interrupt(dev); kscan_charliplex_init_interrupt(dev);
#endif #endif
k_work_init_delayable(&data->work, kscan_multiplex_work_handler); k_work_init_delayable(&data->work, kscan_charliplex_work_handler);
return 0; return 0;
} }
static const struct kscan_driver_api kscan_multiplex_api = { static const struct kscan_driver_api kscan_charliplex_api = {
.config = kscan_multiplex_configure, .config = kscan_charliplex_configure,
.enable_callback = kscan_multiplex_enable, .enable_callback = kscan_charliplex_enable,
.disable_callback = kscan_multiplex_disable, .disable_callback = kscan_charliplex_disable,
}; };
#define KSCAN_MULTIPLEX_INIT(n) \ #define KSCAN_CHARLIPLEX_INIT(n) \
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \ BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \ "ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \ BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \ "ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
\ \
static struct debounce_state kscan_multiplex_state_##n[INST_MULTIPLEX_LEN(n)]; \ static struct debounce_state kscan_charliplex_state_##n[INST_CHARLIPLEX_LEN(n)]; \
static const struct gpio_dt_spec kscan_multiplex_cells_##n[] = { \ static const struct gpio_dt_spec kscan_charliplex_cells_##n[] = { \
UTIL_LISTIFY(INST_LEN(n), KSCAN_GPIO_CFG_INIT, n)}; \ UTIL_LISTIFY(INST_LEN(n), KSCAN_GPIO_CFG_INIT, n)}; \
static struct kscan_multiplex_data kscan_multiplex_data_##n = { \ static struct kscan_charliplex_data kscan_charliplex_data_##n = { \
.multiplex_state = kscan_multiplex_state_##n, \ .charliplex_state = kscan_charliplex_state_##n, \
}; \ }; \
\ \
static struct kscan_multiplex_config kscan_multiplex_config_##n = { \ static struct kscan_charliplex_config kscan_charliplex_config_##n = { \
.cells = KSCAN_GPIO_LIST(kscan_multiplex_cells_##n), \ .cells = KSCAN_GPIO_LIST(kscan_charliplex_cells_##n), \
.debounce_config = \ .debounce_config = \
{ \ { \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \ .debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
@ -403,8 +405,8 @@ static const struct kscan_driver_api kscan_multiplex_api = {
.poll_period_ms = DT_INST_PROP(n, poll_period_ms), \ .poll_period_ms = DT_INST_PROP(n, poll_period_ms), \
COND_INTERRUPT((.interrupt = KSCAN_INTR_CFG_INIT(n), ))}; \ COND_INTERRUPT((.interrupt = KSCAN_INTR_CFG_INIT(n), ))}; \
\ \
DEVICE_DT_INST_DEFINE(n, &kscan_multiplex_init, NULL, &kscan_multiplex_data_##n, \ DEVICE_DT_INST_DEFINE(n, &kscan_charliplex_init, NULL, &kscan_charliplex_data_##n, \
&kscan_multiplex_config_##n, APPLICATION, \ &kscan_charliplex_config_##n, APPLICATION, \
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_multiplex_api); CONFIG_APPLICATION_INIT_PRIORITY, &kscan_charliplex_api);
DT_INST_FOREACH_STATUS_OKAY(KSCAN_MULTIPLEX_INIT); DT_INST_FOREACH_STATUS_OKAY(KSCAN_CHARLIPLEX_INIT);

View file

@ -1,9 +1,9 @@
# Copyright (c) 2022 The ZMK Contributors # Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
description: GPIO keyboard full multiplexed matrix controller description: GPIO keyboard charliplex matrix controller
compatible: "zmk,kscan-gpio-multiplex" compatible: "zmk,kscan-gpio-charliplex"
include: kscan.yaml include: kscan.yaml

View file

@ -149,26 +149,26 @@ The output pins (e.g. columns for `col2row`) should have the flag `GPIO_ACTIVE_H
}; };
``` ```
## Multiplex Driver ## Charliplex Driver
Keyboard scan driver where keys are arranged on a matrix with each GPIO used as both input and output. This driver enables n pins to drive up to n\*(n-1) keys. Keyboard scan driver where keys are arranged on a matrix with each GPIO used as both input and output. This driver enables n pins to drive up to n\*(n-1) keys.
Definition file: [zmk/app/drivers/kscan/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/drivers/kscan/Kconfig) Definition file: [zmk/app/drivers/kscan/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/drivers/kscan/Kconfig)
| Config | Type | Description | Default | | Config | Type | Description | Default |
| ------------------------------------------------- | ----------- | ------------------------------------------------------------------------- | ------- | | --------------------------------------------------- | ----------- | ------------------------------------------------------------------------- | ------- |
| `CONFIG_ZMK_KSCAN_MULTIPLEX_POLLING` | bool | Poll for key presses instead of using interrupts | n | | `CONFIG_ZMK_KSCAN_CHARLIPLEX_POLLING` | bool | Poll for key presses instead of using interrupts | n |
| `CONFIG_ZMK_KSCAN_MULTIPLEX_WAIT_BEFORE_INPUTS` | int (ticks) | How long to wait before reading input pins after setting output active | 0 | | `CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BEFORE_INPUTS` | int (ticks) | How long to wait before reading input pins after setting output active | 0 |
| `CONFIG_ZMK_KSCAN_MULTIPLEX_WAIT_BETWEEN_OUTPUTS` | int (ticks) | How long to wait between each output to allow previous output to "settle" | 0 | | `CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS` | int (ticks) | How long to wait between each output to allow previous output to "settle" | 0 |
- With `CONFIG_ZMK_KSCAN_MULTIPLEX_POLLING` enabled this allows n pins to drive n\*(n-1) keys. - With `CONFIG_ZMK_KSCAN_CHARLIPLEX_POLLING` enabled this allows n pins to drive n\*(n-1) keys.
- With `CONFIG_ZMK_KSCAN_MULTIPLEX_POLLING` disabled n pins will drive (n-1)\*(n-2) keys, but provide much improved power handling. - With `CONFIG_ZMK_KSCAN_CHARLIPLEX_POLLING` disabled n pins will drive (n-1)\*(n-2) keys, but provide much improved power handling.
### Devicetree ### Devicetree
Applies to: `compatible = "zmk,kscan-gpio-multiplex"` Applies to: `compatible = "zmk,kscan-gpio-charliplex"`
Definition file: [zmk/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-gpio-multiplex.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/drivers/zephyr/dts/bindings/kscan/zmk%2Ckscan-gpio-multiplex.yaml) Definition file: [zmk/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-gpio-charliplex.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/drivers/zephyr/dts/bindings/kscan/zmk%2Ckscan-gpio-charliplex.yaml)
| Property | Type | Description | Default | | Property | Type | Description | Default |
| ------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------- | ------- | | ------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------- | ------- |
@ -439,9 +439,9 @@ Consider a keyboard with a [duplex matrix](https://wiki.ai03.com/books/pcb-desig
}; };
``` ```
### Example: Multiplex ### Example: Charliplex
Since a multiplex driver will never align with a keyboard directly due to the un-addressable positions, a matrix transform should be used to map the pairs to the layout of the keys. Since a charliplex driver will never align with a keyboard directly due to the un-addressable positions, a matrix transform should be used to map the pairs to the layout of the keys.
Note that the entire addressable space does not need to be mapped. Note that the entire addressable space does not need to be mapped.
```devicetree ```devicetree
@ -452,7 +452,7 @@ Note that the entire addressable space does not need to be mapped.
}; };
kscan0: kscan { kscan0: kscan {
compatible = "zmk,kscan-gpio-multiplex"; compatible = "zmk,kscan-gpio-charliplex";
label = "KSCAN"; label = "KSCAN";
interrupt-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH|GPIO_PULL_DOWN) >; interrupt-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH|GPIO_PULL_DOWN) >;