Skip to content

Commit 57cf054

Browse files
authored
Merge pull request adafruit#9912 from patricksurry/demux-keymatrix-delay
fix: Add delay to DemuxKeyMatrix along with columns_to_anodes and transpose options
2 parents fda920a + e185c28 commit 57cf054

File tree

6 files changed

+72
-19
lines changed

6 files changed

+72
-19
lines changed

ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ void cardputer_keyboard_init(void) {
110110
row_addr_pins, // row_addr_pins
111111
7, // num_column_pins
112112
column_pins, // column_pins
113+
true, // columns_to_anodes
114+
false, // transpose
113115
0.01f, // interval
114116
20, // max_events
115117
2 // debounce_threshold

shared-bindings/keypad_demux/DemuxKeyMatrix.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
//| self,
3737
//| row_addr_pins: Sequence[microcontroller.Pin],
3838
//| column_pins: Sequence[microcontroller.Pin],
39+
//| columns_to_anodes: bool = True,
40+
//| transpose: bool = False,
3941
//| interval: float = 0.020,
4042
//| max_events: int = 64,
4143
//| debounce_threshold: int = 1,
@@ -51,7 +53,18 @@
5153
//| An `keypad.EventQueue` is created when this object is created and is available in the `events` attribute.
5254
//|
5355
//| :param Sequence[microcontroller.Pin] row_addr_pins: The pins attached to the rows demultiplexer.
56+
//| If your columns are multiplexed, set ``transpose`` to ``True``.
5457
//| :param Sequence[microcontroller.Pin] column_pins: The pins attached to the columns.
58+
//| :param bool columns_to_anodes: Default ``True``.
59+
//| If the matrix uses diodes, the diode anodes are typically connected to the column pins
60+
//| with the cathodes connected to the row pins. This implies an inverting multiplexer that drives
61+
//| the selected row pin low. If your diodes are reversed, with a non-inverting multiplexer
62+
//| that drives the selected row high, set ``columns_to_anodes`` to ``False``.
63+
//| If ``transpose`` is ``True`` the sense of columns and rows are reversed here.
64+
//| :param bool transpose: Default ``False``.
65+
//| If your matrix is multiplexed on columns rather than rows, set ``transpose`` to ``True``.
66+
//| This swaps the meaning of ``row_addr_pins`` to ``column_addr_pins``;
67+
//| ``column_pins`` to ``row_pins``; and ``columns_to_anodes`` to ``rows_to_anodes``.
5568
//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing.
5669
//| ``interval`` is in float seconds. The default is 0.020 (20 msecs).
5770
//| :param int max_events: maximum size of `events` `keypad.EventQueue`:
@@ -67,10 +80,12 @@
6780

6881
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) {
6982
keypad_demux_demuxkeymatrix_obj_t *self = mp_obj_malloc(keypad_demux_demuxkeymatrix_obj_t, &keypad_demux_demuxkeymatrix_type);
70-
enum { ARG_row_addr_pins, ARG_column_pins, ARG_interval, ARG_max_events, ARG_debounce_threshold };
83+
enum { ARG_row_addr_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_transpose, ARG_interval, ARG_max_events, ARG_debounce_threshold };
7184
static const mp_arg_t allowed_args[] = {
7285
{ MP_QSTR_row_addr_pins, MP_ARG_REQUIRED | MP_ARG_OBJ },
7386
{ MP_QSTR_column_pins, MP_ARG_REQUIRED | MP_ARG_OBJ },
87+
{ MP_QSTR_columns_to_anodes, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
88+
{ MP_QSTR_transpose, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
7489
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
7590
{ MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
7691
{ MP_QSTR_debounce_threshold, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
@@ -107,7 +122,7 @@ static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type,
107122
column_pins_array[column] = pin;
108123
}
109124

110-
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);
125+
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);
111126
return MP_OBJ_FROM_PTR(self);
112127
}
113128

shared-bindings/keypad_demux/DemuxKeyMatrix.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
extern const mp_obj_type_t keypad_demux_demuxkeymatrix_type;
1313

14-
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);
14+
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);
1515

1616
void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self);
1717

shared-bindings/keypad_demux/__init__.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@
1111

1212
//| """Support for scanning key matrices that use a demultiplexer
1313
//|
14-
//| The `keypad_demux` module provides native support to scan sets of keys or buttons,
15-
//| connected in a row-and-column matrix.
14+
//| The `keypad_demux` module provides native support to scan a matrix of keys or buttons
15+
//| where either the row or column axis is controlled by a demultiplexer or decoder IC
16+
//| such as the 74LS138 or 74LS238. In this arrangement a binary input value
17+
//| determines which column (or row) to select, thereby reducing the number of input pins.
18+
//| For example the input 101 would select line 5 in the matrix.
19+
//| Set ``columns_to_anodes`` to ``False`` with a non-inverting demultiplexer
20+
//| which drives the selected line high.
21+
//| Set ``transpose`` to ``True`` if columns are multiplexed rather than rows.
1622
//|
1723
//| .. jinja
1824
//| """

shared-module/keypad_demux/DemuxKeyMatrix.c

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <string.h>
88

99
#include "py/gc.h"
10+
#include "py/mphal.h"
1011
#include "py/runtime.h"
1112
#include "shared-bindings/digitalio/DigitalInOut.h"
1213
#include "shared-bindings/keypad/EventQueue.h"
@@ -26,11 +27,15 @@ static keypad_scanner_funcs_t keymatrix_funcs = {
2627
};
2728

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

32-
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) {
34+
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) {
3335

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

49+
// the column pins are always inputs, with default state based on columns_to_anodes
4450
mp_obj_t column_dios[num_column_pins];
4551
for (size_t column = 0; column < num_column_pins; column++) {
4652
digitalio_digitalinout_obj_t *dio =
4753
mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type);
4854
dio->base.type = &digitalio_digitalinout_type;
4955
common_hal_digitalio_digitalinout_construct(dio, column_pins[column]);
50-
common_hal_digitalio_digitalinout_switch_to_input(dio, PULL_UP);
56+
common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN);
5157
column_dios[column] = dio;
5258
}
5359
self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios);
5460

61+
self->columns_to_anodes = columns_to_anodes;
62+
self->transpose = transpose;
5563
self->funcs = &keymatrix_funcs;
5664

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

8088
size_t common_hal_keypad_demux_demuxkeymatrix_get_row_count(keypad_demux_demuxkeymatrix_obj_t *self) {
81-
return 1 << self->row_addr_digitalinouts->len;
89+
return self->transpose
90+
? self->column_digitalinouts->len
91+
: (size_t)(1 << self->row_addr_digitalinouts->len);
8292
}
8393

8494
size_t common_hal_keypad_demux_demuxkeymatrix_get_column_count(keypad_demux_demuxkeymatrix_obj_t *self) {
85-
return self->column_digitalinouts->len;
95+
return self->transpose
96+
? (size_t)(1 << self->row_addr_digitalinouts->len)
97+
: self->column_digitalinouts->len;
8698
}
8799

88100
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) {
@@ -102,22 +114,38 @@ static size_t demuxkeymatrix_get_key_count(void *self_in) {
102114

103115
static void demuxkeymatrix_scan_now(void *self_in, mp_obj_t timestamp) {
104116
keypad_demux_demuxkeymatrix_obj_t *self = self_in;
105-
106-
for (size_t row = 0; row < common_hal_keypad_demux_demuxkeymatrix_get_row_count(self); row++) {
107-
// Set the row address on demultiplexer
117+
// We always scan through the multiplexed lines using the array sizes directly
118+
// and apply transposition only when translating to key number.
119+
for (int row = 0; row < (1 << self->row_addr_digitalinouts->len); row++) {
120+
// Set the row address on the demultiplexer.
121+
// When columns_to_anodes is True (default) we've got an inverting demux
122+
// so the selected line is pulled low, and we look for rows that get pulled low.
123+
// When columns_to_anodes is False we do the reverse.
108124
size_t mask = 0b00000001;
109125
for (size_t row_addr_pin = 0; row_addr_pin < self->row_addr_digitalinouts->len; row_addr_pin++) {
110126
digitalio_digitalinout_obj_t *dio = self->row_addr_digitalinouts->items[row_addr_pin];
111127
common_hal_digitalio_digitalinout_set_value(dio, (mask & row) != 0);
112128
mask = mask << 1;
113129
}
114130

115-
for (size_t column = 0; column < common_hal_keypad_demux_demuxkeymatrix_get_column_count(self); column++) {
116-
mp_uint_t key_number = row_column_to_key_number(self, row, column);
117-
118-
// Get the current state, by reading whether the column got pulled to the row value or not.
119-
// If low, the key is pressed.
120-
const bool current = !common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]);
131+
// Wait a moment to let the columns settle.
132+
// The normal KeyMatrix uses a 1us delay but that still gave echoes on my
133+
// nullbitsco nibble 65% (16 x 5 matrix). For example when key (row, col) is pressed
134+
// both (row, col) and (row+1, col) (and sometimes row+2) are registered,
135+
// especially when row+1 is a power of 2 (all mux bits change) and col is 0.
136+
// The QMK implementation for this keyboard uses a 5us delay which works here too
137+
mp_hal_delay_us(5);
138+
139+
for (size_t column = 0; column < self->column_digitalinouts->len; column++) {
140+
mp_uint_t key_number = self->transpose
141+
? row_column_to_key_number(self, column, row)
142+
: row_column_to_key_number(self, row, column);
143+
144+
// Get the current state, by reading whether the column got pulled to the row value or not,
145+
// which is the opposite of columns_to_anodes.
146+
const bool current =
147+
common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]) !=
148+
self->columns_to_anodes;
121149

122150
// Record any transitions.
123151
if (keypad_debounce((keypad_scanner_obj_t *)self, key_number, current)) {

shared-module/keypad_demux/DemuxKeyMatrix.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ typedef struct {
1717
KEYPAD_SCANNER_COMMON_FIELDS;
1818
mp_obj_tuple_t *row_addr_digitalinouts;
1919
mp_obj_tuple_t *column_digitalinouts;
20+
bool columns_to_anodes;
21+
bool transpose;
2022
} keypad_demux_demuxkeymatrix_obj_t;
2123

2224
void keypad_demux_demuxkeymatrix_scan(keypad_demux_demuxkeymatrix_obj_t *self);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy