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:
parent
2503670bf3
commit
e9c75448b5
5 changed files with 108 additions and 107 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
@ -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
|
||||||
|
|
|
@ -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) >;
|
||||||
|
|
Loading…
Add table
Reference in a new issue