Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add delay to DemuxKeyMatrix along with columns_to_anodes and transpose options #9912

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ void cardputer_keyboard_init(void) {
row_addr_pins, // row_addr_pins
7, // num_column_pins
column_pins, // column_pins
true, // columns_to_anodes
false, // transpose
0.01f, // interval
20, // max_events
2 // debounce_threshold
Expand Down
19 changes: 17 additions & 2 deletions shared-bindings/keypad_demux/DemuxKeyMatrix.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
//| self,
//| row_addr_pins: Sequence[microcontroller.Pin],
//| column_pins: Sequence[microcontroller.Pin],
//| columns_to_anodes: bool = True,
//| transpose: bool = False,
//| interval: float = 0.020,
//| max_events: int = 64,
//| debounce_threshold: int = 1,
Expand All @@ -51,7 +53,18 @@
//| An `keypad.EventQueue` is created when this object is created and is available in the `events` attribute.
//|
//| :param Sequence[microcontroller.Pin] row_addr_pins: The pins attached to the rows demultiplexer.
//| If your columns are multiplexed, set ``transpose`` to ``True``.
//| :param Sequence[microcontroller.Pin] column_pins: The pins attached to the columns.
//| :param bool columns_to_anodes: Default ``True``.
//| If the matrix uses diodes, the diode anodes are typically connected to the column pins
//| with the cathodes connected to the row pins. This implies an inverting multiplexer that drives
//| the selected row pin low. If your diodes are reversed, with a non-inverting multiplexer
//| that drives the selected row high, set ``columns_to_anodes`` to ``False``.
//| If ``transpose`` is ``True`` the sense of columns and rows are reversed here.
//| :param bool transpose: Default ``False``.
//| If your matrix is multiplexed on columns rather than rows, set ``transpose`` to ``True``.
//| This swaps the meaning of ``row_addr_pins`` to ``column_addr_pins``;
//| ``column_pins`` to ``row_pins``; and ``columns_to_anodes`` to ``rows_to_anodes``.
//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing.
//| ``interval`` is in float seconds. The default is 0.020 (20 msecs).
//| :param int max_events: maximum size of `events` `keypad.EventQueue`:
Expand All @@ -67,10 +80,12 @@

static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
keypad_demux_demuxkeymatrix_obj_t *self = mp_obj_malloc(keypad_demux_demuxkeymatrix_obj_t, &keypad_demux_demuxkeymatrix_type);
enum { ARG_row_addr_pins, ARG_column_pins, ARG_interval, ARG_max_events, ARG_debounce_threshold };
enum { ARG_row_addr_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_transpose, ARG_interval, ARG_max_events, ARG_debounce_threshold };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_row_addr_pins, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_column_pins, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_columns_to_anodes, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_transpose, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
{ MP_QSTR_debounce_threshold, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
Expand Down Expand Up @@ -107,7 +122,7 @@ static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type,
column_pins_array[column] = pin;
}

common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, interval, max_events, debounce_threshold);
common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold);
return MP_OBJ_FROM_PTR(self);
}

Expand Down
2 changes: 1 addition & 1 deletion shared-bindings/keypad_demux/DemuxKeyMatrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

extern const mp_obj_type_t keypad_demux_demuxkeymatrix_type;

void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], mp_float_t interval, size_t max_events, uint8_t debounce_threshold);
void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold);

void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self);

Expand Down
10 changes: 8 additions & 2 deletions shared-bindings/keypad_demux/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@

//| """Support for scanning key matrices that use a demultiplexer
//|
//| The `keypad_demux` module provides native support to scan sets of keys or buttons,
//| connected in a row-and-column matrix.
//| The `keypad_demux` module provides native support to scan a matrix of keys or buttons
//| where either the row or column axis is controlled by a demultiplexer or decoder IC
//| such as the 74LS138 or 74LS238. In this arrangement a binary input value
//| determines which column (or row) to select, thereby reducing the number of input pins.
//| For example the input 101 would select line 5 in the matrix.
//| Set ``columns_to_anodes`` to ``False`` with a non-inverting demultiplexer
//| which drives the selected line high.
//| Set ``transpose`` to ``True`` if columns are multiplexed rather than rows.
//|
//| .. jinja
//| """
Expand Down
56 changes: 42 additions & 14 deletions shared-module/keypad_demux/DemuxKeyMatrix.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <string.h>

#include "py/gc.h"
#include "py/mphal.h"
#include "py/runtime.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "shared-bindings/keypad/EventQueue.h"
Expand All @@ -26,11 +27,15 @@ static keypad_scanner_funcs_t keymatrix_funcs = {
};

static mp_uint_t row_column_to_key_number(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) {
return row * self->column_digitalinouts->len + column;
// return the key index in the user's coordinate system
return row * common_hal_keypad_demux_demuxkeymatrix_get_column_count(self) + column;
}

void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], mp_float_t interval, size_t max_events, uint8_t debounce_threshold) {
void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) {

// the multiplexed pins are outputs so we can set the address for the target row
// the sense of the address pins themselves doesn't change with columns_to_anodes
// but the value output on the selected row line will be !columns_to_anodes
mp_obj_t row_addr_dios[num_row_addr_pins];
for (size_t row = 0; row < num_row_addr_pins; row++) {
digitalio_digitalinout_obj_t *dio =
Expand All @@ -41,17 +46,20 @@ void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatri
}
self->row_addr_digitalinouts = mp_obj_new_tuple(num_row_addr_pins, row_addr_dios);

// the column pins are always inputs, with default state based on columns_to_anodes
mp_obj_t column_dios[num_column_pins];
for (size_t column = 0; column < num_column_pins; column++) {
digitalio_digitalinout_obj_t *dio =
mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type);
dio->base.type = &digitalio_digitalinout_type;
common_hal_digitalio_digitalinout_construct(dio, column_pins[column]);
common_hal_digitalio_digitalinout_switch_to_input(dio, PULL_UP);
common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN);
column_dios[column] = dio;
}
self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios);

self->columns_to_anodes = columns_to_anodes;
self->transpose = transpose;
self->funcs = &keymatrix_funcs;

keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold);
Expand All @@ -78,11 +86,15 @@ void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_o
}

size_t common_hal_keypad_demux_demuxkeymatrix_get_row_count(keypad_demux_demuxkeymatrix_obj_t *self) {
return 1 << self->row_addr_digitalinouts->len;
return self->transpose
? self->column_digitalinouts->len
: (size_t)(1 << self->row_addr_digitalinouts->len);
}

size_t common_hal_keypad_demux_demuxkeymatrix_get_column_count(keypad_demux_demuxkeymatrix_obj_t *self) {
return self->column_digitalinouts->len;
return self->transpose
? (size_t)(1 << self->row_addr_digitalinouts->len)
: self->column_digitalinouts->len;
}

mp_uint_t common_hal_keypad_demux_demuxkeymatrix_row_column_to_key_number(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) {
Expand All @@ -102,22 +114,38 @@ static size_t demuxkeymatrix_get_key_count(void *self_in) {

static void demuxkeymatrix_scan_now(void *self_in, mp_obj_t timestamp) {
keypad_demux_demuxkeymatrix_obj_t *self = self_in;

for (size_t row = 0; row < common_hal_keypad_demux_demuxkeymatrix_get_row_count(self); row++) {
// Set the row address on demultiplexer
// We always scan through the multiplexed lines using the array sizes directly
// and apply transposition only when translating to key number.
for (int row = 0; row < (1 << self->row_addr_digitalinouts->len); row++) {
// Set the row address on the demultiplexer.
// When columns_to_anodes is True (default) we've got an inverting demux
// so the selected line is pulled low, and we look for rows that get pulled low.
// When columns_to_anodes is False we do the reverse.
size_t mask = 0b00000001;
for (size_t row_addr_pin = 0; row_addr_pin < self->row_addr_digitalinouts->len; row_addr_pin++) {
digitalio_digitalinout_obj_t *dio = self->row_addr_digitalinouts->items[row_addr_pin];
common_hal_digitalio_digitalinout_set_value(dio, (mask & row) != 0);
mask = mask << 1;
}

for (size_t column = 0; column < common_hal_keypad_demux_demuxkeymatrix_get_column_count(self); column++) {
mp_uint_t key_number = row_column_to_key_number(self, row, column);

// Get the current state, by reading whether the column got pulled to the row value or not.
// If low, the key is pressed.
const bool current = !common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]);
// Wait a moment to let the columns settle.
// The normal KeyMatrix uses a 1us delay but that still gave echoes on my
// nullbitsco nibble 65% (16 x 5 matrix). For example when key (row, col) is pressed
// both (row, col) and (row+1, col) (and sometimes row+2) are registered,
// especially when row+1 is a power of 2 (all mux bits change) and col is 0.
// The QMK implementation for this keyboard uses a 5us delay which works here too
mp_hal_delay_us(5);

for (size_t column = 0; column < self->column_digitalinouts->len; column++) {
mp_uint_t key_number = self->transpose
? row_column_to_key_number(self, column, row)
: row_column_to_key_number(self, row, column);

// Get the current state, by reading whether the column got pulled to the row value or not,
// which is the opposite of columns_to_anodes.
const bool current =
common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]) !=
self->columns_to_anodes;

// Record any transitions.
if (keypad_debounce((keypad_scanner_obj_t *)self, key_number, current)) {
Expand Down
2 changes: 2 additions & 0 deletions shared-module/keypad_demux/DemuxKeyMatrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ typedef struct {
KEYPAD_SCANNER_COMMON_FIELDS;
mp_obj_tuple_t *row_addr_digitalinouts;
mp_obj_tuple_t *column_digitalinouts;
bool columns_to_anodes;
bool transpose;
} keypad_demux_demuxkeymatrix_obj_t;

void keypad_demux_demuxkeymatrix_scan(keypad_demux_demuxkeymatrix_obj_t *self);
Expand Down