Make Charlie real

I'd dropped the `e` from `charlieplex` everywhere. Bring it back to make
the world a safe place again.
This commit is contained in:
HookyKB 2023-10-24 12:30:17 +00:00 committed by Peter Johanson
parent 2503670bf3
commit e9c75448b5
5 changed files with 108 additions and 107 deletions

View file

@ -5,7 +5,7 @@ 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_CHARLIEPLEX kscan_gpio_charlieplex.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)

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_CHARLIPLEX := zmk,kscan-gpio-charliplex DT_COMPAT_ZMK_KSCAN_GPIO_CHARLIEPLEX := zmk,kscan-gpio-charlieplex
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_CHARLIPLEX config ZMK_KSCAN_GPIO_CHARLIEPLEX
bool bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_CHARLIPLEX)) default $(dt_compat_enabled,$(DT_COMPAT_ZMK_KSCAN_GPIO_CHARLIEPLEX))
select ZMK_KSCAN_GPIO_DRIVER select ZMK_KSCAN_GPIO_DRIVER
if ZMK_KSCAN_GPIO_MATRIX if ZMK_KSCAN_GPIO_MATRIX
@ -64,9 +64,9 @@ config ZMK_KSCAN_MATRIX_WAIT_BETWEEN_OUTPUTS
endif # ZMK_KSCAN_GPIO_MATRIX endif # ZMK_KSCAN_GPIO_MATRIX
if ZMK_KSCAN_GPIO_CHARLIPLEX if ZMK_KSCAN_GPIO_CHARLIEPLEX
config ZMK_KSCAN_CHARLIPLEX_WAIT_BEFORE_INPUTS config ZMK_KSCAN_CHARLIEPLEX_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"
default 0 default 0
help help
@ -76,8 +76,8 @@ config ZMK_KSCAN_CHARLIPLEX_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_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS config ZMK_KSCAN_CHARLIEPLEX_WAIT_BETWEEN_OUTPUTS
int "Ticks to wait between each output when scanning charliplex matrix" int "Ticks to wait between each output when scanning charlieplex 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_CHARLIPLEX_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_CHARLIPLEX endif # ZMK_KSCAN_GPIO_CHARLIEPLEX
config ZMK_KSCAN_MOCK_DRIVER config ZMK_KSCAN_MOCK_DRIVER
bool bool

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_charliplex #define DT_DRV_COMPAT zmk_kscan_gpio_charlieplex
#define INST_LEN(n) DT_INST_PROP_LEN(n, gpios) #define INST_LEN(n) DT_INST_PROP_LEN(n, gpios)
#define INST_CHARLIPLEX_LEN(n) (INST_LEN(n) * INST_LEN(n)) #define INST_CHARLIEPLEX_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
@ -59,7 +59,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#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_charliplex_data { struct kscan_charlieplex_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;
@ -69,7 +69,7 @@ struct kscan_charliplex_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 zmk_debounce_state *charliplex_state; struct zmk_debounce_state *charlieplex_state;
}; };
struct kscan_gpio_list { struct kscan_gpio_list {
@ -81,7 +81,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_charliplex_config { struct kscan_charlieplex_config {
struct kscan_gpio_list cells; struct kscan_gpio_list cells;
struct zmk_debounce_config debounce_config; struct zmk_debounce_config debounce_config;
int32_t debounce_scan_period_ms; int32_t debounce_scan_period_ms;
@ -95,7 +95,8 @@ struct kscan_charliplex_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_charliplex_config *config, const int row, const int col) { static int state_index(const struct kscan_charlieplex_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);
@ -103,7 +104,7 @@ static int state_index(const struct kscan_charliplex_config *config, const int r
return (col * config->cells.len) + row; return (col * config->cells.len) + row;
} }
static int kscan_charliplex_set_as_input(const struct gpio_dt_spec *gpio) { static int kscan_charlieplex_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;
@ -117,7 +118,7 @@ static int kscan_charliplex_set_as_input(const struct gpio_dt_spec *gpio) {
return 0; return 0;
} }
static int kscan_charliplex_set_as_output(const struct gpio_dt_spec *gpio) { static int kscan_charlieplex_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;
@ -136,11 +137,11 @@ static int kscan_charliplex_set_as_output(const struct gpio_dt_spec *gpio) {
return err; return err;
} }
static int kscan_charliplex_set_all_as_input(const struct device *dev) { static int kscan_charlieplex_set_all_as_input(const struct device *dev) {
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_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_charliplex_set_as_input(&config->cells.gpios[i]); err = kscan_charlieplex_set_as_input(&config->cells.gpios[i]);
if (err) { if (err) {
return err; return err;
} }
@ -149,8 +150,8 @@ static int kscan_charliplex_set_all_as_input(const struct device *dev) {
return 0; return 0;
} }
static int kscan_charliplex_set_all_outputs(const struct device *dev, const int value) { static int kscan_charlieplex_set_all_outputs(const struct device *dev, const int value) {
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_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];
@ -170,9 +171,9 @@ static int kscan_charliplex_set_all_outputs(const struct device *dev, const int
return 0; return 0;
} }
static int kscan_charliplex_interrupt_configure(const struct device *dev, static int kscan_charlieplex_interrupt_configure(const struct device *dev,
const gpio_flags_t flags) { const gpio_flags_t flags) {
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_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);
@ -184,43 +185,43 @@ static int kscan_charliplex_interrupt_configure(const struct device *dev,
return 0; return 0;
} }
static int kscan_charliplex_interrupt_enable(const struct device *dev) { static int kscan_charlieplex_interrupt_enable(const struct device *dev) {
int err = kscan_charliplex_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE); int err = kscan_charlieplex_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_charliplex_set_all_outputs(dev, 1); return kscan_charlieplex_set_all_outputs(dev, 1);
} }
static void kscan_charliplex_irq_callback(const struct device *port, struct gpio_callback *cb, static void kscan_charlieplex_irq_callback(const struct device *port, struct gpio_callback *cb,
const gpio_port_pins_t _pin) { const gpio_port_pins_t _pin) {
struct kscan_charliplex_data *data = struct kscan_charlieplex_data *data =
CONTAINER_OF(cb, struct kscan_charliplex_data, irq_callback); CONTAINER_OF(cb, struct kscan_charlieplex_data, irq_callback);
// Disable our interrupt to avoid re-entry while we scan. // Disable our interrupt to avoid re-entry while we scan.
kscan_charliplex_interrupt_configure(data->dev, GPIO_INT_DISABLE); kscan_charlieplex_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);
} }
static void kscan_charliplex_read_continue(const struct device *dev) { static void kscan_charlieplex_read_continue(const struct device *dev) {
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_config *config = dev->config;
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_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_charliplex_read_end(const struct device *dev) { static void kscan_charlieplex_read_end(const struct device *dev) {
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_data *data = dev->data;
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_config *config = dev->config;
if (config->use_interrupt) { if (config->use_interrupt) {
// Return to waiting for an interrupt. // Return to waiting for an interrupt.
kscan_charliplex_interrupt_enable(dev); kscan_charlieplex_interrupt_enable(dev);
} else { } else {
data->scan_time += config->poll_period_ms; data->scan_time += config->poll_period_ms;
@ -229,14 +230,14 @@ static void kscan_charliplex_read_end(const struct device *dev) {
} }
} }
static int kscan_charliplex_read(const struct device *dev) { static int kscan_charlieplex_read(const struct device *dev) {
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_data *data = dev->data;
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_config *config = dev->config;
bool continue_scan = false; bool continue_scan = false;
// NOTE: RR 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_charliplex_set_all_as_input(dev); int err = kscan_charlieplex_set_all_as_input(dev);
if (err) { if (err) {
return err; return err;
} }
@ -244,13 +245,13 @@ static int kscan_charliplex_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_charliplex_set_as_output(out_gpio); err = kscan_charlieplex_set_as_output(out_gpio);
if (err) { if (err) {
return err; return err;
} }
#if CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BEFORE_INPUTS > 0 #if CONFIG_ZMK_KSCAN_CHARLIEPLEX_WAIT_BEFORE_INPUTS > 0
k_busy_wait(CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BEFORE_INPUTS); k_busy_wait(CONFIG_ZMK_KSCAN_CHARLIEPLEX_WAIT_BEFORE_INPUTS);
#endif #endif
for (int col = 0; col < config->cells.len; col++) { for (int col = 0; col < config->cells.len; col++) {
@ -260,7 +261,7 @@ static int kscan_charliplex_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 zmk_debounce_state *state = &data->charliplex_state[index]; struct zmk_debounce_state *state = &data->charlieplex_state[index];
zmk_debounce_update(state, gpio_pin_get_dt(in_gpio), config->debounce_scan_period_ms, zmk_debounce_update(state, gpio_pin_get_dt(in_gpio), config->debounce_scan_period_ms,
&config->debounce_config); &config->debounce_config);
@ -275,67 +276,67 @@ static int kscan_charliplex_read(const struct device *dev) {
continue_scan = continue_scan || zmk_debounce_is_active(state); continue_scan = continue_scan || zmk_debounce_is_active(state);
} }
err = kscan_charliplex_set_as_input(out_gpio); err = kscan_charlieplex_set_as_input(out_gpio);
if (err) { if (err) {
return err; return err;
} }
#if CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS > 0 #if CONFIG_ZMK_KSCAN_CHARLIEPLEX_WAIT_BETWEEN_OUTPUTS > 0
k_busy_wait(CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS); k_busy_wait(CONFIG_ZMK_KSCAN_CHARLIEPLEX_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_charliplex_read_continue(dev); kscan_charlieplex_read_continue(dev);
} else { } else {
// All keys are released. Return to normal. // All keys are released. Return to normal.
kscan_charliplex_read_end(dev); kscan_charlieplex_read_end(dev);
} }
return 0; return 0;
} }
static void kscan_charliplex_work_handler(struct k_work *work) { static void kscan_charlieplex_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_charliplex_data *data = CONTAINER_OF(dwork, struct kscan_charliplex_data, work); struct kscan_charlieplex_data *data = CONTAINER_OF(dwork, struct kscan_charlieplex_data, work);
kscan_charliplex_read(data->dev); kscan_charlieplex_read(data->dev);
} }
static int kscan_charliplex_configure(const struct device *dev, const kscan_callback_t callback) { static int kscan_charlieplex_configure(const struct device *dev, const kscan_callback_t callback) {
if (!callback) { if (!callback) {
return -EINVAL; return -EINVAL;
} }
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_data *data = dev->data;
data->callback = callback; data->callback = callback;
return 0; return 0;
} }
static int kscan_charliplex_enable(const struct device *dev) { static int kscan_charlieplex_enable(const struct device *dev) {
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_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_charliplex_read(dev); return kscan_charlieplex_read(dev);
} }
static int kscan_charliplex_disable(const struct device *dev) { static int kscan_charlieplex_disable(const struct device *dev) {
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_data *data = dev->data;
k_work_cancel_delayable(&data->work); k_work_cancel_delayable(&data->work);
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_config *config = dev->config;
if (config->use_interrupt) { if (config->use_interrupt) {
return kscan_charliplex_interrupt_configure(dev, GPIO_INT_DISABLE); return kscan_charlieplex_interrupt_configure(dev, GPIO_INT_DISABLE);
} }
return 0; return 0;
} }
static int kscan_charliplex_init_inputs(const struct device *dev) { static int kscan_charlieplex_init_inputs(const struct device *dev) {
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_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_charliplex_set_as_input(&config->cells.gpios[i]); int err = kscan_charlieplex_set_as_input(&config->cells.gpios[i]);
if (err) { if (err) {
return err; return err;
} }
@ -344,17 +345,17 @@ static int kscan_charliplex_init_inputs(const struct device *dev) {
return 0; return 0;
} }
static int kscan_charliplex_init_interrupt(const struct device *dev) { static int kscan_charlieplex_init_interrupt(const struct device *dev) {
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_data *data = dev->data;
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_config *config = dev->config;
const struct gpio_dt_spec *gpio = &config->interrupt; const struct gpio_dt_spec *gpio = &config->interrupt;
int err = kscan_charliplex_set_as_input(gpio); int err = kscan_charlieplex_set_as_input(gpio);
if (err) { if (err) {
return err; return err;
} }
gpio_init_callback(&data->irq_callback, kscan_charliplex_irq_callback, BIT(gpio->pin)); gpio_init_callback(&data->irq_callback, kscan_charlieplex_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);
@ -362,43 +363,43 @@ static int kscan_charliplex_init_interrupt(const struct device *dev) {
return err; return err;
} }
static int kscan_charliplex_init(const struct device *dev) { static int kscan_charlieplex_init(const struct device *dev) {
struct kscan_charliplex_data *data = dev->data; struct kscan_charlieplex_data *data = dev->data;
data->dev = dev; data->dev = dev;
kscan_charliplex_init_inputs(dev); kscan_charlieplex_init_inputs(dev);
kscan_charliplex_set_all_outputs(dev, 0); kscan_charlieplex_set_all_outputs(dev, 0);
const struct kscan_charliplex_config *config = dev->config; const struct kscan_charlieplex_config *config = dev->config;
if (config->use_interrupt) { if (config->use_interrupt) {
kscan_charliplex_init_interrupt(dev); kscan_charlieplex_init_interrupt(dev);
} }
k_work_init_delayable(&data->work, kscan_charliplex_work_handler); k_work_init_delayable(&data->work, kscan_charlieplex_work_handler);
return 0; return 0;
} }
static const struct kscan_driver_api kscan_charliplex_api = { static const struct kscan_driver_api kscan_charlieplex_api = {
.config = kscan_charliplex_configure, .config = kscan_charlieplex_configure,
.enable_callback = kscan_charliplex_enable, .enable_callback = kscan_charlieplex_enable,
.disable_callback = kscan_charliplex_disable, .disable_callback = kscan_charlieplex_disable,
}; };
#define KSCAN_CHARLIPLEX_INIT(n) \ #define KSCAN_CHARLIEPLEX_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 zmk_debounce_state kscan_charliplex_state_##n[INST_CHARLIPLEX_LEN(n)]; \ static struct zmk_debounce_state kscan_charlieplex_state_##n[INST_CHARLIEPLEX_LEN(n)]; \
static const struct gpio_dt_spec kscan_charliplex_cells_##n[] = { \ static const struct gpio_dt_spec kscan_charlieplex_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_charliplex_data kscan_charliplex_data_##n = { \ static struct kscan_charlieplex_data kscan_charlieplex_data_##n = { \
.charliplex_state = kscan_charliplex_state_##n, \ .charlieplex_state = kscan_charlieplex_state_##n, \
}; \ }; \
\ \
static struct kscan_charliplex_config kscan_charliplex_config_##n = { \ static struct kscan_charlieplex_config kscan_charlieplex_config_##n = { \
.cells = KSCAN_GPIO_LIST(kscan_charliplex_cells_##n), \ .cells = KSCAN_GPIO_LIST(kscan_charlieplex_cells_##n), \
.debounce_config = \ .debounce_config = \
{ \ { \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \ .debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \
@ -409,8 +410,8 @@ static const struct kscan_driver_api kscan_charliplex_api = {
COND_POLL_AND_INTR((.use_interrupt = INST_INTR_DEFINED(n), )) \ COND_POLL_AND_INTR((.use_interrupt = INST_INTR_DEFINED(n), )) \
COND_THIS_INTERRUPT(n, (.interrupt = KSCAN_INTR_CFG_INIT(n), ))}; \ COND_THIS_INTERRUPT(n, (.interrupt = KSCAN_INTR_CFG_INIT(n), ))}; \
\ \
DEVICE_DT_INST_DEFINE(n, &kscan_charliplex_init, NULL, &kscan_charliplex_data_##n, \ DEVICE_DT_INST_DEFINE(n, &kscan_charlieplex_init, NULL, &kscan_charlieplex_data_##n, \
&kscan_charliplex_config_##n, APPLICATION, \ &kscan_charlieplex_config_##n, APPLICATION, \
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_charliplex_api); CONFIG_APPLICATION_INIT_PRIORITY, &kscan_charlieplex_api);
DT_INST_FOREACH_STATUS_OKAY(KSCAN_CHARLIPLEX_INIT); DT_INST_FOREACH_STATUS_OKAY(KSCAN_CHARLIEPLEX_INIT);

View file

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

View file

@ -149,7 +149,7 @@ The output pins (e.g. columns for `col2row`) should have the flag `GPIO_ACTIVE_H
}; };
``` ```
## Charliplex Driver ## Charlieplex Driver
Keyboard scan driver where keys are arranged on a matrix with each GPIO used as both input and output. Keyboard scan driver where keys are arranged on a matrix with each GPIO used as both input and output.
@ -158,16 +158,16 @@ Keyboard scan driver where keys are arranged on a matrix with each GPIO used as
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_CHARLIPLEX_WAIT_BEFORE_INPUTS` | int (ticks) | How long to wait before reading input pins after setting output active | 0 | | `CONFIG_ZMK_KSCAN_CHARLIEPLEX_WAIT_BEFORE_INPUTS` | int (ticks) | How long to wait before reading input pins after setting output active | 0 |
| `CONFIG_ZMK_KSCAN_CHARLIPLEX_WAIT_BETWEEN_OUTPUTS` | int (ticks) | How long to wait between each output to allow previous output to "settle" | 0 | | `CONFIG_ZMK_KSCAN_CHARLIEPLEX_WAIT_BETWEEN_OUTPUTS` | int (ticks) | How long to wait between each output to allow previous output to "settle" | 0 |
### Devicetree ### Devicetree
Applies to: `compatible = "zmk,kscan-gpio-charliplex"` Applies to: `compatible = "zmk,kscan-gpio-charlieplex"`
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) Definition file: [zmk/app/drivers/zephyr/dts/bindings/kscan/zmk,kscan-gpio-charlieplex.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/drivers/zephyr/dts/bindings/kscan/zmk%2Ckscan-gpio-charlieplex.yaml)
| Property | Type | Description | Default | | Property | Type | Description | Default |
| ------------------------- | ---------- | ------------------------------------------------------------------------------------------- | ------- | | ------------------------- | ---------- | ------------------------------------------------------------------------------------------- | ------- |
@ -438,9 +438,9 @@ Consider a keyboard with a [duplex matrix](https://wiki.ai03.com/books/pcb-desig
}; };
``` ```
### Example: Charliplex ### Example: Charlieplex
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. Since a charlieplex 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
@ -451,7 +451,7 @@ Note that the entire addressable space does not need to be mapped.
}; };
kscan0: kscan { kscan0: kscan {
compatible = "zmk,kscan-gpio-charliplex"; compatible = "zmk,kscan-gpio-charlieplex";
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) >;