Skip to content

Commit 8a6d35c

Browse files
committed
more example
1 parent 3d42798 commit 8a6d35c

File tree

3 files changed

+205
-5
lines changed

3 files changed

+205
-5
lines changed

CircuitPython_Commodore_16_KB2040/code.py renamed to CircuitPython_Commodore_16_KB2040/advanced/code.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# * There are no diodes, not even on modifiers, so there's only 2-key rollover.
99

1010
import asyncio.core
11-
from board import * # pylint: disable=wildcard-import,unused-wildcard-import
11+
import board
1212
import keypad
1313
from adafruit_hid.keycode import Keycode as K
1414
from adafruit_hid.keyboard import Keyboard
@@ -22,10 +22,9 @@
2222
# 1 3 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # connector pins
2323
# R5 C7 R7 C4 R1 C5 C6 R3 R2 R4 C2 C1 R6 C3 C0 R0 # row/column in schematic
2424
# D2 D3 D4 D5 D6 D7 D8 D9 D10 MOSI MISO SCK A0 A1 A2 A3 # conencted to kb2040 at
25-
# pylint: disable=undefined-variable
26-
rows = [A3, D6, D10, D9, MOSI, D2, A0, D4] # give the following ...
27-
cols = [A2, SCK, MISO, A1, D5, D7, D8, D3]
28-
# pylint: enable=undefined-variable
25+
# results in the the following assignment of rows and columns:
26+
rows = [board.A3, board.D6, board.D10, board.D9, board.MOSI, board.D2, board.A0, board.D4]
27+
cols = [board.A2, board.SCK, board.MISO, board.A1, board.D5, board.D7, board.D8, board.D3]
2928

3029
# ROM listing of key values from ed7.src in
3130
# http://www.zimmers.net/anonftp/pub/cbm/src/plus4/ted_kernal_basic_src.tar.gz
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# SPDX-FileCopyrightText: 2022 Jeff Epler for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
# Commodore 16 to USB HID adapter with Adafruit KB2040
5+
#
6+
# Note that:
7+
# * This matrix is different than the (more common) Commodore 64 matrix
8+
# * There are no diodes, not even on modifiers, so there's only 2-key rollover.
9+
# * This is a "physical" keymap, so that the functions of the keys are similar to the
10+
# function of a standard PC keyboard key in the same location.
11+
#
12+
# See the guide or the advanced code for more information about the key matrix
13+
14+
import board
15+
import keypad
16+
from adafruit_hid.keycode import Keycode as K
17+
from adafruit_hid.keyboard import Keyboard
18+
import usb_hid
19+
20+
rows = [board.A3, board.D6, board.D10, board.D9, board.MOSI, board.D2, board.A0, board.D4]
21+
cols = [board.A2, board.SCK, board.MISO, board.A1, board.D5, board.D7, board.D8, board.D3]
22+
23+
keycodes = [
24+
K.BACKSPACE, K.ENTER, K.LEFT_ARROW, K.F8, K.F1, K.F2, K.F3, K.LEFT_BRACKET,
25+
K.THREE, K.W, K.A, K.FOUR, K.Z, K.S, K.E, K.LEFT_SHIFT,
26+
K.FIVE, K.R, K.D, K.SIX, K.C, K.F, K.T, K.X,
27+
K.SEVEN, K.Y, K.G, K.EIGHT, K.B, K.H, K.U, K.V,
28+
K.NINE, K.I, K.J, K.ZERO, K.M, K.K, K.O, K.N,
29+
K.DOWN_ARROW, K.P, K.L, K.UP_ARROW, K.PERIOD, K.SEMICOLON, K.BACKSLASH, K.COMMA,
30+
K.MINUS, K.KEYPAD_ASTERISK, K.QUOTE, K.EQUALS, K.ESCAPE, K.RIGHT_ARROW, K.RIGHT_BRACKET,
31+
K.FORWARD_SLASH, K.ONE, K.HOME, K.LEFT_CONTROL, K.TWO, K.SPACE, K.ALT, K.Q, K.GRAVE_ACCENT,
32+
]
33+
34+
kbd = Keyboard(usb_hid.devices)
35+
36+
with keypad.KeyMatrix(rows, cols) as keys:
37+
while True:
38+
if ev := keys.events.get():
39+
keycode = keycodes[ev.key_number]
40+
if ev.pressed:
41+
kbd.press(keycode)
42+
else:
43+
kbd.release(keycode)
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# SPDX-FileCopyrightText: 2022 Jeff Epler for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
# KeyMatrix Whisperer
5+
#
6+
# Interactively determine a matrix keypad's row and column pins
7+
#
8+
# Wait until the program prints "press keys now". Then, press and hold a key
9+
# until it registers. Repeat until all rows and columns are identified. If your
10+
# keyboard matrix does NOT have dioes, you MUST take care to only press a
11+
# single key at a time.
12+
#
13+
# How identification is performed: When a key is pressed _some_ pair of I/Os
14+
# will be connected. This code repeatedly scans all possible pairs, recording
15+
# them. The very first pass when no key is pressed is recorded as "junk" so it
16+
# can be ignored.
17+
#
18+
# Then, the first I/O involved in the first non-junk press is arbitrarily
19+
# recorded as a "row pin". If the matrix does not have diodes, this can
20+
# actually vary from run to run or depending on the first key you pressed. The
21+
# only net effect of this is that the row & column lists are exchanged.
22+
#
23+
# After enough key presses, you'll get a full list of "row" and "column" pins.
24+
# For instance, on the Commodore 16 keyboard you'd get 8 row pins and 8 column pins.
25+
#
26+
# This doesn't help determine the LOGICAL ORDER of rows and columns or the
27+
# physical layout of the keyboard. You still have to do that for yourself.
28+
29+
import board
30+
import microcontroller
31+
from digitalio import DigitalInOut, Pull
32+
33+
# List of pins to test, or None to test all pins
34+
IO_PINS = None # [board.D0, board.D1]
35+
# Which value(s) to set the driving pin to
36+
values = [True] # [True, False]
37+
38+
def discover_io():
39+
return [pin_maybe for name in dir(microcontroller.pin) if isinstance(pin_maybe := getattr(microcontroller.pin, name), microcontroller.Pin)]
40+
41+
def pin_lookup(pin):
42+
for i in dir(board):
43+
if getattr(board, i) is pin: return i
44+
for i in dir(microcontroller.pin):
45+
if getattr(microcontroller.pin, i) is pin: return i
46+
47+
# Find all I/O pins, if IO_PINS is not explicitly set above
48+
if IO_PINS is None:
49+
IO_PINS = discover_io()
50+
51+
# Initialize all pins as inputs, make a lookup table to get the name from the pin
52+
ios_lookup = dict([(pin_lookup(pin), DigitalInOut(pin)) for pin in IO_PINS])
53+
ios = ios_lookup.values()
54+
ios_items = ios_lookup.items()
55+
for io in ios:
56+
io.switch_to_input(pull=Pull.UP)
57+
58+
# Partial implementation of 'defaultdict' class from standard Python
59+
# from https://github.com/micropython/micropython-lib/blob/master/python-stdlib/collections.defaultdict/collections/defaultdict.py
60+
class defaultdict:
61+
@staticmethod
62+
def __new__(cls, default_factory=None, **kwargs):
63+
# Some code (e.g. urllib.urlparse) expects that basic defaultdict
64+
# functionality will be available to subclasses without them
65+
# calling __init__().
66+
self = super(defaultdict, cls).__new__(cls)
67+
self.d = {}
68+
return self
69+
70+
def __init__(self, default_factory=None, **kwargs):
71+
self.d = kwargs
72+
self.default_factory = default_factory
73+
74+
def __getitem__(self, key):
75+
try:
76+
return self.d[key]
77+
except KeyError:
78+
v = self.__missing__(key)
79+
self.d[key] = v
80+
return v
81+
82+
def __setitem__(self, key, v):
83+
self.d[key] = v
84+
85+
def __delitem__(self, key):
86+
del self.d[key]
87+
88+
def __contains__(self, key):
89+
return key in self.d
90+
91+
def __missing__(self, key):
92+
if self.default_factory is None:
93+
raise KeyError(key)
94+
return self.default_factory()
95+
96+
# Track combinations that were pressed, including ones during the "junk" scan
97+
pressed_or_junk = defaultdict(set)
98+
# Track combinations that were pressed, excluding the "junk" scan
99+
pressed = defaultdict(set)
100+
# During the first run, anything scanned is "junk". Could occur for unused pins.
101+
first_run = True
102+
# List of pins identified as rows and columns
103+
rows = []
104+
cols = []
105+
# The first pin identified is arbitrarily called a 'row' pin.
106+
row_arbitrarily = None
107+
108+
while True:
109+
changed = False
110+
last_pressed = None
111+
for value in values:
112+
pull = [Pull.UP, Pull.DOWN][value]
113+
for io in ios:
114+
io.switch_to_input(pull=pull)
115+
for name1, io1 in ios_items:
116+
io1.switch_to_output(value)
117+
for name2, io2 in ios_items:
118+
if io2 is io1: continue
119+
if io2.value == value:
120+
if first_run:
121+
pressed_or_junk[name1].add(name2)
122+
pressed_or_junk[name2].add(name1)
123+
elif name2 not in pressed_or_junk[name1]:
124+
if row_arbitrarily is None: row_arbitrarily = name1
125+
pressed_or_junk[name1].add(name2)
126+
pressed_or_junk[name2].add(name1)
127+
if name2 not in pressed[name1]:
128+
pressed[name1].add(name2)
129+
pressed[name2].add(name1)
130+
changed = True
131+
if name2 in pressed[name1]:
132+
last_pressed = (name1, name2)
133+
print("Key registered. Release to continue")
134+
while io2.value == value: pass
135+
io1.switch_to_input(pull=pull)
136+
if first_run:
137+
print("Press keys now")
138+
first_run = False
139+
elif changed:
140+
rows = set([row_arbitrarily])
141+
cols = set()
142+
to_check = [row_arbitrarily]
143+
for check in to_check:
144+
for other in pressed[check]:
145+
if other in rows or other in cols: continue
146+
if check in rows:
147+
cols.add(other)
148+
else:
149+
rows.add(other)
150+
to_check.append(other)
151+
152+
rows = sorted(rows)
153+
cols = sorted(cols)
154+
if changed or last_pressed:
155+
print("Rows", len(rows), *rows)
156+
print("Cols", len(cols), *cols)
157+
print("Last pressed", *last_pressed)
158+
print()

0 commit comments

Comments
 (0)