Adding Pimoroni Keybow and Keybow 2040
This commit is contained in:
parent
ef7b29bd43
commit
2fce09986e
28
boards/pimoroni/README.md
Normal file
28
boards/pimoroni/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Pimoroni Keybow family
|
||||
|
||||
A family of macro pads based on raspberry pi hardware:
|
||||
![Keybow](image url for keybow)
|
||||
(Original) Keybow - Raspberry Pi hat. 4x3 hotswap keys, with an APA102 LED per key.
|
||||
|
||||
![Keybow 2040](image url for keybow 2040)
|
||||
Keybow 2040 - custom RP2040 board. 4x4 hotswap keys, with an RGB LED per key driven by a shared IS31FL3731 controller.
|
||||
|
||||
These boards share the 'feature' of using a single GPIO per key rather than a row and column matrix, so these both
|
||||
use CircuitPython's `keypad.Keys` module instead of the regular KMK matrix scanner.
|
||||
|
||||
|
||||
## Retailers
|
||||
### UK
|
||||
- Pimoroni
|
||||
- [Keybow](https://shop.pimoroni.com/products/keybow)
|
||||
- [Keybow 2040](https://shop.pimoroni.com/products/keybow-2040)
|
||||
|
||||
### AU
|
||||
- Core Electronics
|
||||
- [Keybow](https://core-electronics.com.au/pimoroni-keybow-mini-mechanical-keyboard-kit-clicky-keys.html)
|
||||
- [Keybow 2040](https://core-electronics.com.au/pimoroni-keybow-2040-tactile-keys.html)
|
||||
|
||||
Extensions enabled by default
|
||||
- [Layers](https://github.com/KMKfw/kmk_firmware/tree/master/docs/layers.md) Need more keys than switches? Use layers.
|
||||
- [RGB](https://github.com/KMKfw/kmk_firmware/tree/master/docs/rgb.md) Light it up (Keybow only so far)
|
||||
- [MediaKeys](https://github.com/KMKfw/kmk_firmware/tree/master/docs/media_keys.md) Control volume and other media functions
|
0
boards/pimoroni/__init__.py
Normal file
0
boards/pimoroni/__init__.py
Normal file
0
boards/pimoroni/keybow/__init__.py
Normal file
0
boards/pimoroni/keybow/__init__.py
Normal file
24
boards/pimoroni/keybow/code.py
Normal file
24
boards/pimoroni/keybow/code.py
Normal file
@ -0,0 +1,24 @@
|
||||
from keybow import Keybow
|
||||
|
||||
from kmk.extensions.media_keys import MediaKeys
|
||||
from kmk.keys import KC
|
||||
from kmk.modules.layers import Layers
|
||||
|
||||
keybow = Keybow()
|
||||
|
||||
# fmt: off
|
||||
keybow.keymap = [
|
||||
[
|
||||
KC.A, KC.B, KC.C,
|
||||
KC.E, KC.F, KC.G,
|
||||
KC.I, KC.J, KC.K,
|
||||
KC.M, KC.N, KC.O,
|
||||
]
|
||||
]
|
||||
|
||||
keybow.extensions.extend([MediaKeys()])
|
||||
keybow.modules.extend([Layers()])
|
||||
# fmt: on
|
||||
|
||||
if __name__ == '__main__':
|
||||
keybow.go()
|
94
boards/pimoroni/keybow/keybow.py
Normal file
94
boards/pimoroni/keybow/keybow.py
Normal file
@ -0,0 +1,94 @@
|
||||
'''
|
||||
KMK keyboard for Pimoroni Keybow.
|
||||
|
||||
WARNING: This doesn't currently function correctly on the Raspberry Pi Zero,
|
||||
some of the keys are stuck in the 'pressed' position. There's either a bug in
|
||||
the keypad implementation on the rpi0, or the pin numbers don't match the pins
|
||||
in linux.
|
||||
|
||||
This is a 4x3 macro pad designed to fit the rpi's GPIO connector. Each key is
|
||||
attached to a single GPIO and has an APA102 LED mounted underneath it.
|
||||
|
||||
The layout of the board is as follows (GPIO connector on the left):
|
||||
|
||||
R0 | D20 D6 D22
|
||||
R1 | D17 D16 D12
|
||||
R2 | D24 D27 D26
|
||||
R0 | D13 D5 D23
|
||||
------------------
|
||||
C0 C1 C2
|
||||
|
||||
This board also functions with an adaptor (see
|
||||
https://learn.adafruit.com/itsybitsy-keybow-mechanical-keypad/) to work with an
|
||||
itsybitsy in place of the rpi, which uses an alternate pin mapping:
|
||||
|
||||
R0 | A2 A1 A0
|
||||
R1 | A5 A4 A3
|
||||
R2 | D10 D9 D7
|
||||
R3 | D11 D12 D2
|
||||
------------------
|
||||
C0 C1 C2
|
||||
|
||||
This keyboard file should automatically select the correct mapping at runtime.
|
||||
'''
|
||||
|
||||
import board
|
||||
|
||||
import adafruit_dotstar
|
||||
import sys
|
||||
|
||||
from kmk.extensions.rgb import RGB, AnimationModes
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.scanners.native_keypad_scanner import keys_scanner
|
||||
|
||||
|
||||
# fmt: off
|
||||
def raspi_pins():
|
||||
return [
|
||||
[board.D20, board.D16, board.D26],
|
||||
[board.D6, board.D12, board.D13],
|
||||
[board.D22, board.D24, board.D5],
|
||||
[board.D17, board.D27, board.D23],
|
||||
]
|
||||
|
||||
|
||||
def itsybitsy_pins():
|
||||
return [
|
||||
[board.D11, board.D12, board.D2],
|
||||
[board.D10, board.D9, board.D7],
|
||||
[board.A5, board.A4, board.A3],
|
||||
[board.A2, board.A1, board.A0],
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
|
||||
def isPi():
|
||||
return sys.platform == 'BROADCOM'
|
||||
|
||||
|
||||
if isPi():
|
||||
_KEY_CFG = raspi_pins()
|
||||
_LED_PINS = (board.SCK, board.MOSI)
|
||||
else:
|
||||
_KEY_CFG = itsybitsy_pins()
|
||||
_LED_PINS = (board.SCK, board.MOSI)
|
||||
|
||||
|
||||
led_strip = adafruit_dotstar.DotStar(_LED_PINS[0], _LED_PINS[1], 12)
|
||||
rgb_ext = RGB(
|
||||
pixel_pin=0,
|
||||
pixels=led_strip,
|
||||
num_pixels=12,
|
||||
animation_mode=AnimationModes.BREATHING_RAINBOW,
|
||||
)
|
||||
|
||||
|
||||
class Keybow(KMKKeyboard):
|
||||
'''
|
||||
Default keyboard config for the Keybow.
|
||||
'''
|
||||
|
||||
extensions = [rgb_ext]
|
||||
|
||||
def __init__(self):
|
||||
self.matrix = keys_scanner(_KEY_CFG)
|
0
boards/pimoroni/keybow_2040/__init__.py
Normal file
0
boards/pimoroni/keybow_2040/__init__.py
Normal file
20
boards/pimoroni/keybow_2040/code.py
Normal file
20
boards/pimoroni/keybow_2040/code.py
Normal file
@ -0,0 +1,20 @@
|
||||
from keybow_2040 import Keybow2040
|
||||
|
||||
from kmk.keys import KC
|
||||
|
||||
keybow = Keybow2040()
|
||||
|
||||
# fmt: off
|
||||
keybow.keymap = [
|
||||
[
|
||||
KC.A, KC.B, KC.C, KC.D,
|
||||
KC.E, KC.F, KC.G, KC.H,
|
||||
KC.I, KC.J, KC.K, KC.L,
|
||||
KC.M, KC.N, KC.O, KC.P,
|
||||
KC.Q
|
||||
]
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
if __name__ == '__main__':
|
||||
keybow.go()
|
76
boards/pimoroni/keybow_2040/keybow_2040.py
Normal file
76
boards/pimoroni/keybow_2040/keybow_2040.py
Normal file
@ -0,0 +1,76 @@
|
||||
'''
|
||||
KMK keyboard for Pimoroni Keybow 2040.
|
||||
|
||||
This is a 4x4 macro pad based on the RP2040. Each key is attached to a single
|
||||
GPIO, so the KMK matrix scanner needs to be overridden. Additionally, each
|
||||
key has an RGB LED controlled by an IS31FL3731 controller which is incompatible
|
||||
with the default RGB module.
|
||||
|
||||
The layout of the board is as follows:
|
||||
|
||||
[RESET] [USB-C] [BOOT]
|
||||
R0 | SW3 SW7 SW11 SW15
|
||||
R1 | SW2 SW6 SW10 SW14
|
||||
R2 | SW1 SW5 SW9 SW13
|
||||
R3 | SW0 SW4 SW8 SW12
|
||||
-----------------------------
|
||||
C0 C1 C2 C3
|
||||
|
||||
The binding defined in the _KEY_CFG array binds the switches to keys such that
|
||||
the keymap can be written in a way that lines up with the natural order of the
|
||||
key switches, then adds [BOOT] in (4,0). [RESET] can't be mapped as a key.
|
||||
'''
|
||||
|
||||
import board
|
||||
|
||||
from adafruit_is31fl3731.keybow2040 import Keybow2040 as KeybowLeds
|
||||
from adafruit_pixelbuf import PixelBuf
|
||||
|
||||
# from kmk.extensions.rgb import RGB
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.scanners.native_keypad_scanner import keys_scanner
|
||||
|
||||
# fmt: off
|
||||
_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],
|
||||
[board.USER_SW],
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
|
||||
class Keybow2040Leds(PixelBuf):
|
||||
'''
|
||||
Minimal PixelBuf wrapper for the Keybow 2040's LED array.
|
||||
|
||||
NOTE: Currently broken.
|
||||
'''
|
||||
|
||||
def __init__(self, size: int):
|
||||
self.leds = KeybowLeds(board.I2C)
|
||||
super().__init__(size, byteorder='RGB')
|
||||
|
||||
def _transmit(self, buffer):
|
||||
for pixel in range(self._pixels):
|
||||
r = buffer[pixel * 3 + 0]
|
||||
g = buffer[pixel * 3 + 1]
|
||||
b = buffer[pixel * 3 + 2]
|
||||
self.leds.pixel(pixel // 4, pixel % 4, (r, g, b))
|
||||
|
||||
|
||||
# rgb_ext = RGB(0, pixels=Keybow2040Leds(16), num_pixels=16)
|
||||
|
||||
|
||||
class Keybow2040(KMKKeyboard):
|
||||
'''
|
||||
Default keyboard config for the Keybow2040.
|
||||
|
||||
TODO: Map the LEDs as well.
|
||||
'''
|
||||
|
||||
# extensions = [rgb_ext]
|
||||
|
||||
def __init__(self):
|
||||
self.matrix = keys_scanner(_KEY_CFG)
|
@ -244,12 +244,15 @@ class KMKKeyboard:
|
||||
Ensure the provided configuration is *probably* bootable
|
||||
'''
|
||||
assert self.keymap, 'must define a keymap with at least one row'
|
||||
assert self.row_pins, 'no GPIO pins defined for matrix rows'
|
||||
assert self.col_pins, 'no GPIO pins defined for matrix columns'
|
||||
assert self.diode_orientation is not None, 'diode orientation must be defined'
|
||||
assert (
|
||||
self.hid_type in HIDModes.ALL_MODES
|
||||
), 'hid_type must be a value from kmk.consts.HIDModes'
|
||||
if not self.matrix:
|
||||
assert self.row_pins, 'no GPIO pins defined for matrix rows'
|
||||
assert self.col_pins, 'no GPIO pins defined for matrix columns'
|
||||
assert (
|
||||
self.diode_orientation is not None
|
||||
), 'diode orientation must be defined'
|
||||
|
||||
return self
|
||||
|
||||
@ -262,6 +265,9 @@ class KMKKeyboard:
|
||||
To save RAM on boards that don't use Split, we don't import Split
|
||||
and do an isinstance check, but instead do string detection
|
||||
'''
|
||||
if self.matrix and self.matrix.coord_mapping:
|
||||
self.coord_mapping = self.matrix.coord_mapping
|
||||
|
||||
if any(x.__class__.__module__ == 'kmk.modules.split' for x in self.modules):
|
||||
return
|
||||
|
||||
|
@ -3,6 +3,9 @@ class Scanner:
|
||||
Base class for scanners.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
self.coord_mapping = None
|
||||
|
||||
def scan_for_changes(self):
|
||||
'''
|
||||
Scan for key events and return a key report if an event exists.
|
||||
|
@ -15,7 +15,6 @@ class NativeKeypadScanner(Scanner):
|
||||
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()
|
||||
|
Loading…
Reference in New Issue
Block a user