Skip to content

Commit

Permalink
[Core] Bidirectional and Duplex Matrix Code
Browse files Browse the repository at this point in the history
This is an attempt to rebase Elfmimi's Bidirectional and Duplex Matrix code to the January 2024 master.
Further documentation to be added next commit.
  • Loading branch information
NoOne2246 committed Jan 31, 2024
1 parent b7468f4 commit 883e34f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 69 deletions.
6 changes: 5 additions & 1 deletion docs/config_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ This is a C header file that is one of the first things included, and will persi
* `#define MATRIX_UNSELECT_DRIVE_HIGH`
* On un-select of matrix pins, rather than setting pins to input-high, sets them to output-high.
* `#define DIODE_DIRECTION COL2ROW`
* COL2ROW or ROW2COL - how your matrix is configured. COL2ROW means the black mark on your diode is facing to the rows, and between the switch and the rows.
* `COL2ROW`, `ROW2COL`, `EITHER` or `DUPLEX_SCAN` - how your matrix is configured.
* `COL2ROW` means the black mark on your diode is facing to the rows or facing away from the cols. This is considered the preferred choice in QMK when you are planning a new design.
* `ROW2COL` means the opposite. The black mark on your diode is facing to the cols or facing away from the rows.
* `EITHER` is useful as compensation when a matrix has diodes messed up with their directions. This works properly even when arbitrary number of diodes are soldered wrong way. That means this setting is compatible with `COL2ROW` matrix and `ROW2COL` matrix at the same time. Please note that there is a chance of ghosting depending on the type of diodes used and configuration.
* `DUPLEX_SCAN` is a new strategy to reduce number of pins required to drive a matrix. Compared to a conventional matrix twice the number of keys can be handled. See the section below on using `DUPLEX_SCAN`.
* `#define DIRECT_PINS { { F1, F0, B0, C7 }, { F4, F5, F6, F7 } }`
* pins mapped to rows and columns, from left to right. Defines a matrix where each switch is connected to a separate pin and ground.
* `#define AUDIO_VOICES`
Expand Down
3 changes: 2 additions & 1 deletion docs/custom_quantum_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,11 @@ This is useful for setting up stuff that you may need elsewhere, but isn't hardw
### Low-level Matrix Overrides Function Documentation :id=low-level-matrix-overrides
* GPIO pin initialisation: `void matrix_init_pins(void)`
* This needs to perform the low-level initialisation of all row and column pins. By default this will initialise the input/output state of each of the GPIO pins listed in `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, or `DIRECT_PINS`. Should the keyboard designer override this function, no initialisation of pin state will occur within QMK itself, instead deferring to the keyboard's override.
* This needs to perform the low-level initialisation of all row and column pins. By default this will initialise the input/output state of each of the GPIO pins listed in `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, `DIRECT_PINS`, or `DUPLEX_SCAN`. Should the keyboard designer override this function, no initialisation of pin state will occur within QMK itself, instead deferring to the keyboard's override.
* `COL2ROW`-based row reads: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)`
* `ROW2COL`-based column reads: `void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter)`
* `DIRECT_PINS`-based reads: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)`
* `DUPLEX_SCAN`-based reads: `void matrix_read`
* These three functions need to perform the low-level retrieval of matrix state of relevant input pins, based on the matrix type. Only one of the functions should be implemented, if needed. By default this will iterate through `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, configuring the inputs and outputs based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, or `DIRECT_PINS`. Should the keyboard designer override this function, no manipulation of matrix GPIO pin state will occur within QMK itself, instead deferring to the keyboard's override.
## Keyboard Post Initialization code
Expand Down
146 changes: 81 additions & 65 deletions quantum/matrix.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2012-2018 Jun Wako, Jack Humbert, Yiancar
Copyright 2012-2018 Jun Wako, Jack Humbert, Yiancar, Ein Terakawa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -53,18 +53,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

#ifdef DIRECT_PINS
static SPLIT_MUTABLE pin_t direct_pins[ROWS_PER_HAND][MATRIX_COLS] = DIRECT_PINS;
#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
# ifdef MATRIX_ROW_PINS
#else
static SPLIT_MUTABLE_ROW pin_t row_pins[ROWS_PER_HAND] = MATRIX_ROW_PINS;
# endif // MATRIX_ROW_PINS
# ifdef MATRIX_COL_PINS
static SPLIT_MUTABLE_COL pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
# endif // MATRIX_COL_PINS
/* Consistency checking of the size of the matrix and the number of pins */
// clang-format off
# if (DIODE_DIRECTION == DUPLEX_SCAN)
# define NUM_ROW_PINS (ROWS_PER_HAND / 2)
_Static_assert(NUM_ROW_PINS * 2 == ROWS_PER_HAND, "Must be exactly divisible");
_Static_assert(NUM_ROW_PINS == sizeof(row_pins)/sizeof(row_pins[0]), \
"Number of elements in MATRIX_ROW_PINS * 2 must be equal to ROWS_PER_HAND");
# else
# define NUM_ROW_PINS ROWS_PER_HAND
_Static_assert(NUM_ROW_PINS == sizeof(row_pins)/sizeof(row_pins[0]), \
"Number of elements in MATRIX_ROW_PINS must be equal to ROWS_PER_HAND");
# endif

_Static_assert(MATRIX_COLS == sizeof(col_pins)/sizeof(col_pins[0]), \
"Number of elements in MATRIX_COL_PINS must be equal to MATRIX_COLS");
// clang-format on
#endif

/* matrix state(1:on, 0:off) */
extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
extern matrix_row_t raw_matrix[ROWS_PER_HAND]; // raw values
extern matrix_row_t matrix[ROWS_PER_HAND]; // debounced values

#ifdef SPLIT_KEYBOARD
// row offsets for each hand
Expand Down Expand Up @@ -109,7 +121,7 @@ static inline uint8_t readMatrixPin(pin_t pin) {
#ifdef DIRECT_PINS

__attribute__((weak)) void matrix_init_pins(void) {
for (int row = 0; row < ROWS_PER_HAND; row++) {
for (int row = 0; row < NUM_ROW_PINS; row++) {
for (int col = 0; col < MATRIX_COLS; col++) {
pin_t pin = direct_pins[row][col];
if (pin != NO_PIN) {
Expand All @@ -133,10 +145,8 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[]
current_matrix[current_row] = current_row_value;
}

#elif defined(DIODE_DIRECTION)
#else
# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
# if (DIODE_DIRECTION == COL2ROW)

static bool select_row(uint8_t row) {
pin_t pin = row_pins[row];
if (pin != NO_PIN) {
Expand All @@ -158,48 +168,11 @@ static void unselect_row(uint8_t row) {
}

static void unselect_rows(void) {
for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
for (uint8_t x = 0; x < NUM_ROW_PINS; x++) {
unselect_row(x);
}
}

__attribute__((weak)) void matrix_init_pins(void) {
unselect_rows();
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
if (col_pins[x] != NO_PIN) {
setPinInputHigh_atomic(col_pins[x]);
}
}
}

__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
// Start with a clear matrix row
matrix_row_t current_row_value = 0;

if (!select_row(current_row)) { // Select row
return; // skip NO_PIN row
}
matrix_output_select_delay();

// For each col...
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
uint8_t pin_state = readMatrixPin(col_pins[col_index]);

// Populate the matrix row with the state of the col pin
current_row_value |= pin_state ? 0 : row_shifter;
}

// Unselect row
unselect_row(current_row);
matrix_output_unselect_delay(current_row, current_row_value != 0); // wait for all Col signals to go HIGH

// Update the matrix
current_matrix[current_row] = current_row_value;
}

# elif (DIODE_DIRECTION == ROW2COL)

static bool select_col(uint8_t col) {
pin_t pin = col_pins[col];
if (pin != NO_PIN) {
Expand Down Expand Up @@ -227,13 +200,40 @@ static void unselect_cols(void) {
}

__attribute__((weak)) void matrix_init_pins(void) {
unselect_rows();
unselect_cols();
for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
if (row_pins[x] != NO_PIN) {
setPinInputHigh_atomic(row_pins[x]);
}
}

# if (DIODE_DIRECTION != ROW2COL)

__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
// Start with a clear matrix row
matrix_row_t current_row_value = 0;

if (!select_row(current_row)) { // Select row
return; // skip NO_PIN row
}
matrix_output_select_delay();

// For each col...
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
uint8_t pin_state = readMatrixPin(col_pins[col_index]);

// Populate the matrix row with the state of the col pin
current_row_value |= pin_state ? 0 : row_shifter;
}

// Unselect row
unselect_row(current_row);
matrix_output_unselect_delay(current_row, current_row_value != 0); // wait for all Col signals to go HIGH

// Update the matrix
current_matrix[current_row] = current_row_value;
}
# endif

# if (DIODE_DIRECTION != COL2ROW)

__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) {
bool key_pressed = false;
Expand All @@ -245,15 +245,21 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[]
matrix_output_select_delay();

// For each row...
for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
# if(DIODE_DIRECTION == DUPLEX_SCAN)
for (uint8_t row_index = NUM_ROW_PINS; row_index < NUM_ROW_PINS * 2; row_index++) {
# else
for (uint8_t row_index = 0; row_index < NUM_ROW_PINS; row_index++) {
# endif
// Check row pin state
if (readMatrixPin(row_pins[row_index]) == 0) {
// Pin LO, set col bit
current_matrix[row_index] |= row_shifter;
key_pressed = true;
} else {
# if (DIODE_DIRECTION != EITHERWAY)
// Pin HI, clear col bit
current_matrix[row_index] &= ~row_shifter;
# endif
}
}

Expand All @@ -262,12 +268,9 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[]
matrix_output_unselect_delay(current_col, key_pressed); // wait for all Row signals to go HIGH
}

# else
# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
# endif
# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
#else
# error DIODE_DIRECTION is not defined!

# endif
# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
#endif

void matrix_init(void) {
Expand Down Expand Up @@ -323,17 +326,30 @@ __attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_mat
uint8_t matrix_scan(void) {
matrix_row_t curr_matrix[MATRIX_ROWS] = {0};

#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
#if !defined(DIRECT_PINS) && (DIODE_DIRECTION == ROW2COL)
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
// Set col, read rows
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);
}
#else
// Set row, read cols
for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
for (uint8_t current_row = 0; current_row < NUM_ROW_PINS; current_row++) {
matrix_read_cols_on_row(curr_matrix, current_row);
}
#elif (DIODE_DIRECTION == ROW2COL)
// Set col, read rows
# if (DIODE_DIRECTION == EITHERWAY)
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);
}
# elif (DIODE_DIRECTION == DUPLEX_SCAN)
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
matrix_read_rows_on_col(curr_matrix + NUM_ROW_PINS, current_col, row_shifter);
}

# endif

#endif

bool changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0;
Expand Down
6 changes: 4 additions & 2 deletions quantum/matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "gpio.h"

/* diode directions */
#define COL2ROW 0
#define ROW2COL 1
#define COL2ROW 1
#define ROW2COL 2
#define EITHERWAY 3 /* Compensation for diode direction dissonance */
#define BOTHWAYS 4 /* Duplex-Matrix */

#if (MATRIX_COLS <= 8)
typedef uint8_t matrix_row_t;
Expand Down

0 comments on commit 883e34f

Please sign in to comment.