Custom matrix scanners
- Introduce Scanner base class for MatrixScanner - Create new Scanner using built-in keypad module - Allow overriding the scanner used by KMKKeyboard
This commit is contained in:
parent
e7d306cf30
commit
57ba0fe8b2
66
docs/scanners.md
Normal file
66
docs/scanners.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Scanners
|
||||
|
||||
Smaller boards and macro pads sometimes assign a GPIO pin to each key, rather
|
||||
than using a full matrix. Boards like this aren't compatible with the default
|
||||
matrix scanner, so you will need to swap it out with an alternative scanner.
|
||||
|
||||
Beside the default `Matrix` scanner, KMK includes the following:
|
||||
|
||||
|
||||
## keypad.Keys
|
||||
|
||||
The `keypad.Keys` scanner treats individual GPIO pins as discrete keys. To use
|
||||
this scanner, provide a sequence of pins that describes the layout of your
|
||||
board then include it in the initialisation sequence of your keyboard class.
|
||||
|
||||
Since the `_init_sanity_check` method in the `KMKKeyboard` class will attempt
|
||||
to validate a number of settings that are no longer needed, you need to
|
||||
override it with a modified version that doesn't fail when they aren't present.
|
||||
|
||||
|
||||
```python
|
||||
import board
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.scanners.native_keypad_scanner import keys_scanner
|
||||
|
||||
|
||||
# GPIO to key mapping - each line is a new row.
|
||||
_KEY_CFG = [
|
||||
[board.SW3, board.SW7, board.SW11, board.SW15],
|
||||
[board.SW2, board.SW6, board.SW10, board.SW14],
|
||||
[board.SW1, board.SW5, board.SW9, board.SW13],
|
||||
[board.SW0, board.SW4, board.SW8, board.SW12],
|
||||
]
|
||||
|
||||
|
||||
# Keyboard implementation class
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner
|
||||
self.matrix = keys_scanner(_KEY_CFG)
|
||||
|
||||
# copy the key coordinates from the scanner
|
||||
self.coord_mapping = self.matrix.coord_mapping
|
||||
|
||||
def _init_sanity_check(self):
|
||||
return self
|
||||
```
|
||||
|
||||
|
||||
## keypad.KeyMatrix
|
||||
|
||||
The `keypad.KeyMatrix` scanner is an alternative implementation of the default
|
||||
matrix scanner using CircuitPython's builtin keypad objects. This is currently
|
||||
experimental and ***not recommended for use***.
|
||||
|
||||
Using this scanner is similar to the `keypad.Keys` scanner. Create the scanner
|
||||
using `keypad_matrix()` instead of `keys_scanner()`.
|
||||
|
||||
|
||||
## `Scanner` base class
|
||||
|
||||
If you require a different type of scanner, you can create your own by
|
||||
providing a subclass of `Scanner`. This is a very simple interface, it only
|
||||
contains a single method, `scan_for_changes(self)` which returns a key report
|
||||
if one exists, or `None` otherwise.
|
||||
|
@ -290,12 +290,18 @@ class KMKKeyboard:
|
||||
self._hid_send_enabled = True
|
||||
|
||||
def _init_matrix(self):
|
||||
self.matrix = self.matrix_scanner(
|
||||
cols=self.col_pins,
|
||||
rows=self.row_pins,
|
||||
diode_orientation=self.diode_orientation,
|
||||
rollover_cols_every_rows=getattr(self, 'rollover_cols_every_rows', None),
|
||||
)
|
||||
if self.matrix is None:
|
||||
print('Initialising matrix scanner from self.matrix_scanner')
|
||||
self.matrix = self.matrix_scanner(
|
||||
cols=self.col_pins,
|
||||
rows=self.row_pins,
|
||||
diode_orientation=self.diode_orientation,
|
||||
rollover_cols_every_rows=getattr(
|
||||
self, 'rollover_cols_every_rows', None
|
||||
),
|
||||
)
|
||||
else:
|
||||
print('Matrix scanner already set, not overwriting.')
|
||||
|
||||
return self
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import digitalio
|
||||
|
||||
from kmk.scanners import Scanner
|
||||
|
||||
|
||||
def intify_coordinate(row, col, len_cols):
|
||||
return len_cols * row + col
|
||||
@ -26,7 +28,7 @@ class KeyEvent:
|
||||
self.pressed = pressed
|
||||
|
||||
|
||||
class MatrixScanner:
|
||||
class MatrixScanner(Scanner):
|
||||
def __init__(
|
||||
self,
|
||||
cols,
|
||||
|
12
kmk/scanners/__init__.py
Normal file
12
kmk/scanners/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
class Scanner:
|
||||
'''
|
||||
Base class for scanners.
|
||||
'''
|
||||
|
||||
def scan_for_changes(self):
|
||||
'''
|
||||
Scan for key events and return a key report if an event exists.
|
||||
|
||||
The key report is a byte array with contents [row, col, True if pressed else False]
|
||||
'''
|
||||
pass
|
60
kmk/scanners/native_keypad_scanner.py
Normal file
60
kmk/scanners/native_keypad_scanner.py
Normal file
@ -0,0 +1,60 @@
|
||||
import keypad
|
||||
|
||||
from kmk.matrix import DiodeOrientation
|
||||
from kmk.scanners import Scanner
|
||||
|
||||
|
||||
class NativeKeypadScanner(Scanner):
|
||||
'''
|
||||
Translation layer around a CircuitPython 7 keypad scanner.
|
||||
|
||||
:param pin_map: A sequence of (row, column) tuples for each key.
|
||||
:param kp: An instance of the keypad class.
|
||||
'''
|
||||
|
||||
def __init__(self, pin_map, kp):
|
||||
self.pin_map = pin_map
|
||||
self.keypad = kp
|
||||
# self.coord_mapping = [ic(row, col) for (row, col) in self.pin_map]
|
||||
self.coord_mapping = list(range(len(pin_map)))
|
||||
|
||||
self.curr_event = keypad.Event()
|
||||
|
||||
def scan_for_changes(self):
|
||||
'''
|
||||
Scan for key events and return a key report if an event exists.
|
||||
|
||||
The key report is a byte array with contents [row, col, True if pressed else False]
|
||||
'''
|
||||
ev = self.curr_event
|
||||
has_event = self.keypad.events.get_into(ev)
|
||||
if has_event:
|
||||
return ev
|
||||
|
||||
|
||||
def keypad_matrix(row_pins, col_pins, direction=DiodeOrientation.COLUMNS):
|
||||
'''
|
||||
Row/Column matrix using the CircuitPython 7 keypad scanner.
|
||||
|
||||
:param row_pins: A sequence of pins used for rows.
|
||||
:param col_pins: A sequence of pins used for columns.
|
||||
:param direction: The diode orientation of the matrix.
|
||||
'''
|
||||
pin_map = [
|
||||
(row, col) for row in range(len(row_pins)) for col in range(len(col_pins))
|
||||
]
|
||||
kp = keypad.KeyMatrix(row_pins, col_pins, direction == DiodeOrientation.COLUMNS)
|
||||
return NativeKeypadScanner(pin_map, kp)
|
||||
|
||||
|
||||
def keys_scanner(pins):
|
||||
'''
|
||||
GPIO-per-key 'matrix' using the native CircuitPython 7 keypad scanner.
|
||||
|
||||
:param pins: An array of arrays of CircuitPython Pin objects, such that pins[r][c] is the pin for row r, column c.
|
||||
'''
|
||||
pin_map = [(row, col) for row in range(len(pins)) for col in range(len(pins[row]))]
|
||||
kp = keypad.Keys(
|
||||
[pins[r][c] for (r, c) in pin_map], value_when_pressed=False, pull=True
|
||||
)
|
||||
return NativeKeypadScanner(pin_map, kp)
|
Loading…
Reference in New Issue
Block a user