232 lines
6.9 KiB
C
232 lines
6.9 KiB
C
/*
|
|
Copyright 2021 mtei
|
|
|
|
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
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
// clang-format off
|
|
#ifndef readPort
|
|
# include "gpio_extr.h"
|
|
#endif
|
|
#include "atomic_util.h"
|
|
#include "util.h"
|
|
#include "matrix.h"
|
|
#include "matrix_extr.h"
|
|
#include "debounce.h"
|
|
|
|
#define ALWAYS_INLINE inline __attribute__((always_inline))
|
|
#define NO_INLINE __attribute__((noinline))
|
|
#define LOCAL_FUNC static
|
|
#define LOCAL_DATA static
|
|
|
|
#ifndef _BV
|
|
# define _BV(bit) (1 << (bit))
|
|
#endif
|
|
|
|
#ifndef MATRIX_DEBUG_PIN
|
|
# define MATRIX_DEBUG_PIN_INIT()
|
|
# define MATRIX_DEBUG_SCAN_START()
|
|
# define MATRIX_DEBUG_SCAN_END()
|
|
# define MATRIX_DEBUG_DELAY_START()
|
|
# define MATRIX_DEBUG_DELAY_END()
|
|
# define MATRIX_DEBUG_GAP()
|
|
#else
|
|
# define MATRIX_DEBUG_GAP() asm volatile("nop \n nop":::"memory")
|
|
#endif
|
|
|
|
typedef uint16_t port_width_t;
|
|
#if MATRIX_TYPE == DIRECT_SWITCH || MATRIX_TYPE == DIODE_COL2ROW
|
|
# define MATRIX_LINES MATRIX_ROWS
|
|
typedef matrix_row_t matrix_line_t;
|
|
#endif
|
|
#if MATRIX_TYPE == DIODE_ROW2COL
|
|
# define MATRIX_LINES MATRIX_COLS
|
|
typedef matrix_col_t matrix_line_t;
|
|
#endif
|
|
typedef struct _port_descriptor {
|
|
int device;
|
|
pin_t port;
|
|
} port_descriptor;
|
|
|
|
/* 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
|
|
|
|
#define setPortBitOutput_writeLow(port, bit) \
|
|
do { setPortBitOutput(port, bit); writePortBitLow(port, bit); } while(0)
|
|
#define setPortBitOutput_writeLow_atomic(port, bit) \
|
|
do { ATOMIC_BLOCK_FORCEON { setPortBitOutput_writeLow(port, bit); } } while(0)
|
|
#define setPortBitInputHigh_atomic(port, bit) \
|
|
do { ATOMIC_BLOCK_FORCEON { setPortBitInputHigh(port, bit); } } while(0)
|
|
|
|
#if defined(MATRIX_IN_PORTS) && defined(MATRIX_IN_PINS)
|
|
# include "matrix_config_expand.c"
|
|
#else
|
|
# error matrix.c need defined MATRIX_IN_PORTS and MATRIX_IN_PINS
|
|
#endif
|
|
|
|
LOCAL_FUNC
|
|
void unselect_output(uint8_t out_index) {
|
|
unselect_output_inline(out_index);
|
|
}
|
|
|
|
LOCAL_FUNC
|
|
void init_output_ports(void) {
|
|
for (int i = 0; i < END_outpin_index; i++) {
|
|
unselect_output(i);
|
|
}
|
|
}
|
|
|
|
LOCAL_FUNC
|
|
void init_all_ports(void) {
|
|
init_input_ports();
|
|
init_output_ports();
|
|
init_inport_mask();
|
|
init_extension();
|
|
}
|
|
|
|
LOCAL_FUNC ALWAYS_INLINE void select_line_and_read_input_ports(uint8_t current_line, port_width_t port_buffer[NUM_OF_INPUT_PORTS]);
|
|
LOCAL_FUNC void select_line_and_read_input_ports(uint8_t current_line, port_width_t port_buffer[NUM_OF_INPUT_PORTS]) {
|
|
// Select row (or col)
|
|
select_output(current_line);
|
|
matrix_output_select_delay();
|
|
|
|
// Read ports
|
|
read_all_input_ports(port_buffer, false);
|
|
|
|
// Unselect row (or col)
|
|
unselect_output_inline(current_line);
|
|
}
|
|
|
|
LOCAL_FUNC ALWAYS_INLINE void read_matrix_line(matrix_line_t phy_matrix[], uint8_t current_line);
|
|
|
|
#if MATRIX_TYPE == DIODE_ROW2COL || MATRIX_TYPE == DIODE_COL2ROW
|
|
LOCAL_FUNC void read_matrix_line(matrix_line_t phy_matrix[], uint8_t current_line) {
|
|
// Start with a clear matrix row
|
|
matrix_line_t current_line_value = 0;
|
|
port_width_t port_buffer[NUM_OF_INPUT_PORTS];
|
|
|
|
#ifdef MATRIX_GPIO_NEED_SEPARATE_ATOMIC
|
|
select_line_and_read_input_ports(current_line, port_buffer);
|
|
#else
|
|
ATOMIC_BLOCK_FORCEON {
|
|
select_line_and_read_input_ports(current_line, port_buffer);
|
|
}
|
|
#endif
|
|
|
|
// Build row (or col)
|
|
current_line_value = build_matrix_line(port_buffer);
|
|
|
|
// Wait signal raise up
|
|
if (current_line_value) {
|
|
MATRIX_DEBUG_DELAY_START();
|
|
wait_unselect_done();
|
|
MATRIX_DEBUG_DELAY_END();
|
|
}
|
|
phy_matrix[current_line] = current_line_value;
|
|
}
|
|
#endif // MATRIX_TYPE == DIODE_ROW2COL || MATRIX_TYPE == DIODE_COL2ROW
|
|
|
|
#if MATRIX_TYPE == DIRECT_SWITCH
|
|
LOCAL_FUNC void read_matrix_line(matrix_line_t phy_matrix[], uint8_t current_line) {
|
|
port_width_t port_buffer[NUM_OF_INPUT_PORTS];
|
|
|
|
if (current_line != 0) {
|
|
return;
|
|
}
|
|
|
|
for (uint8_t i = 0; i < MATRIX_LINES; i++) {
|
|
phy_matrix[i] = 0;
|
|
}
|
|
|
|
read_all_input_ports(port_buffer, false);
|
|
|
|
// Build matrix
|
|
build_matrix_direct(port_buffer, phy_matrix);
|
|
}
|
|
#endif // MATRIX_TYPE == DIRECT_SWITCH
|
|
|
|
void matrix_init(void) {
|
|
// initialize key pins
|
|
init_all_ports();
|
|
|
|
// initialize matrix state: all keys off
|
|
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
|
|
raw_matrix[i] = 0;
|
|
matrix[i] = 0;
|
|
}
|
|
|
|
debounce_init(MATRIX_ROWS);
|
|
|
|
matrix_init_kb();
|
|
}
|
|
|
|
uint8_t matrix_scan(void) {
|
|
matrix_line_t phy_matrix[MATRIX_LINES];
|
|
|
|
MATRIX_DEBUG_PIN_INIT();
|
|
|
|
MATRIX_DEBUG_SCAN_START();
|
|
|
|
// read I/O port to phy_matrix[] (physical matrix)
|
|
//select line, read inputs
|
|
for (uint8_t current_line = 0; current_line < MATRIX_LINES; current_line++) {
|
|
read_matrix_line(phy_matrix, current_line);
|
|
}
|
|
MATRIX_DEBUG_SCAN_END(); MATRIX_DEBUG_GAP(); MATRIX_DEBUG_SCAN_START();
|
|
|
|
bool changed = false;
|
|
#if MATRIX_TYPE == DIRECT_SWITCH || MATRIX_TYPE == DIODE_COL2ROW
|
|
// copy phy_matrix[] to raw_matrix[]
|
|
for (uint8_t current_line = 0; current_line < MATRIX_ROWS; current_line++) {
|
|
if (raw_matrix[current_line] != phy_matrix[current_line]) {
|
|
changed = true;
|
|
raw_matrix[current_line] = phy_matrix[current_line];
|
|
}
|
|
}
|
|
#endif
|
|
#if MATRIX_TYPE == DIODE_ROW2COL
|
|
// transpose phy_matrix[] to raw_matrix[]
|
|
matrix_row_t trans_matrix[MATRIX_ROWS];
|
|
for (uint8_t i = 0; i < MATRIX_ROWS; i++ ) {
|
|
trans_matrix[i] = 0;
|
|
}
|
|
for (uint8_t src_line = 0; src_line < MATRIX_LINES; src_line++) {
|
|
matrix_line_t src_line_data = phy_matrix[src_line];
|
|
matrix_row_t dist_bit = MATRIX_ROW_SHIFTER << src_line;
|
|
for (uint8_t dist_rows = 0; dist_rows < MATRIX_ROWS; dist_rows++) {
|
|
if ((src_line_data & 1) == 1) {
|
|
trans_matrix[dist_rows] |= dist_bit;
|
|
}
|
|
src_line_data >>= 1;
|
|
}
|
|
}
|
|
for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
|
|
if (raw_matrix[current_row] != trans_matrix[current_row]) {
|
|
changed = true;
|
|
raw_matrix[current_row] = trans_matrix[current_row];
|
|
}
|
|
}
|
|
#endif
|
|
MATRIX_DEBUG_SCAN_END(); MATRIX_DEBUG_GAP(); MATRIX_DEBUG_SCAN_START();
|
|
|
|
// debounce raw_matrix[] to matrix[]
|
|
debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
|
|
MATRIX_DEBUG_SCAN_END(); MATRIX_DEBUG_GAP();
|
|
|
|
MATRIX_DEBUG_SCAN_START();
|
|
matrix_scan_kb();
|
|
MATRIX_DEBUG_SCAN_END();
|
|
return (uint8_t)changed;
|
|
}
|