From 0402e7788596417b89869cee1bf84a16f4576069 Mon Sep 17 00:00:00 2001 From: di0ib Date: Mon, 11 Jan 2021 17:20:31 +0000 Subject: [PATCH] Upload files to 'PCA10095' --- PCA10095/code.py | 24 ++++++++ PCA10095/matrix.py | 149 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 PCA10095/code.py create mode 100644 PCA10095/matrix.py diff --git a/PCA10095/code.py b/PCA10095/code.py new file mode 100644 index 0000000..27f03f9 --- /dev/null +++ b/PCA10095/code.py @@ -0,0 +1,24 @@ +import board + +from kmk.keys import KC +from kmk.kmk_keyboard import KMKKeyboard +from kmk.matrix import DiodeOrientation + +keyboard = KMKKeyboard() + +keyboard.col_pins = (board.LED2_R, board.LED2_G, board.LED2_B) +keyboard.row_pins = (board.SW1, board.P1_10) + +keyboard.diode_orientation = DiodeOrientation.COLUMNS + +keyboard.debug_enabled = False + + +keyboard.keymap = [ + [ + KC.A, KC.NO, KC.NO, KC.NO, KC.NO, KC.NO, + ], +] + +if __name__ == '__main__': + keyboard.go() \ No newline at end of file diff --git a/PCA10095/matrix.py b/PCA10095/matrix.py new file mode 100644 index 0000000..9d095b7 --- /dev/null +++ b/PCA10095/matrix.py @@ -0,0 +1,149 @@ +import digitalio + + +def intify_coordinate(row, col): + return row << 8 | col + + +class DiodeOrientation: + ''' + Orientation of diodes on handwired boards. You can think of: + COLUMNS = vertical + ROWS = horizontal + ''' + + COLUMNS = 0 + ROWS = 1 + + +class MatrixScanner: + def __init__( + self, + cols, + rows, + diode_orientation=DiodeOrientation.COLUMNS, + rollover_cols_every_rows=None, + ): + self.len_cols = len(cols) + self.len_rows = len(rows) + + # A pin cannot be both a row and column, detect this by combining the + # two tuples into a set and validating that the length did not drop + # + # repr() hackery is because CircuitPython Pin objects are not hashable + unique_pins = {repr(c) for c in cols} | {repr(r) for r in rows} + assert ( + len(unique_pins) == self.len_cols + self.len_rows + ), 'Cannot use a pin as both a column and row' + del unique_pins + + self.diode_orientation = diode_orientation + + # __class__.__name__ is used instead of isinstance as the MCP230xx lib + # does not use the digitalio.DigitalInOut, but rather a self defined one: + # https://github.com/adafruit/Adafruit_CircuitPython_MCP230xx/blob/3f04abbd65ba5fa938fcb04b99e92ae48a8c9406/adafruit_mcp230xx/digital_inout.py#L33 + + if self.diode_orientation == DiodeOrientation.COLUMNS: + self.outputs = [ + x + if x.__class__.__name__ is 'DigitalInOut' + else digitalio.DigitalInOut(x) + for x in cols + ] + self.inputs = [ + x + if x.__class__.__name__ is 'DigitalInOut' + else digitalio.DigitalInOut(x) + for x in rows + ] + self.translate_coords = True + elif self.diode_orientation == DiodeOrientation.ROWS: + self.outputs = [ + x + if x.__class__.__name__ is 'DigitalInOut' + else digitalio.DigitalInOut(x) + for x in rows + ] + self.inputs = [ + x + if x.__class__.__name__ is 'DigitalInOut' + else digitalio.DigitalInOut(x) + for x in cols + ] + self.translate_coords = False + else: + raise ValueError( + 'Invalid DiodeOrientation: {}'.format(self.diode_orientation) + ) + + for pin in self.outputs: + pin.switch_to_output() + + for pin in self.inputs: + pin.switch_to_input(pull=digitalio.Pull.UP) + + self.rollover_cols_every_rows = rollover_cols_every_rows + if self.rollover_cols_every_rows is None: + self.rollover_cols_every_rows = self.len_rows + + self.len_state_arrays = self.len_cols * self.len_rows + self.state = bytearray(self.len_state_arrays) + self.report = bytearray(3) + + def scan_for_changes(self): + ''' + Poll the matrix for changes and return either None (if nothing updated) + or a bytearray (reused in later runs so copy this if you need the raw + array itself for some crazy reason) consisting of (row, col, pressed) + which are (int, int, bool) + ''' + ba_idx = 0 + any_changed = False + + for oidx, opin in enumerate(self.outputs): + opin.value = False + + for iidx, ipin in enumerate(self.inputs): + # cast to int to avoid + # + # >>> xyz = bytearray(3) + # >>> xyz[2] = True + # Traceback (most recent call last): + # File "", line 1, in + # OverflowError: value would overflow a 1 byte buffer + # + # I haven't dived too far into what causes this, but it's + # almost certainly because bool types in Python aren't just + # aliases to int values, but are proper pseudo-types + new_val = int(not ipin.value) + old_val = self.state[ba_idx] + + if old_val != new_val: + if self.translate_coords: + new_oidx = oidx + self.len_cols * ( + iidx // self.rollover_cols_every_rows + ) + new_iidx = iidx - self.rollover_cols_every_rows * ( + iidx // self.rollover_cols_every_rows + ) + + self.report[0] = new_iidx + self.report[1] = new_oidx + else: + self.report[0] = oidx + self.report[1] = iidx + + self.report[2] = new_val + self.state[ba_idx] = new_val + + any_changed = True + break + + ba_idx += 1 + + opin.value = True + if any_changed: + break + + if any_changed: + return self.report