-
Notifications
You must be signed in to change notification settings - Fork 61
/
matrix.py
168 lines (137 loc) · 4.73 KB
/
matrix.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import digitalio
import time
# from microcontroller.pin import *
class Matrix:
"""
Implement the drive of keyboard matrix and provide an event queue.
"""
# ROWS = (P0_05, P0_06, P0_07, P0_08, P1_09, P1_08, P0_12, P0_11)
# COLS = (P0_19, P0_20, P0_21, P0_22, P0_23, P0_24, P0_25, P0_26)
ROWS = ()
COLS = ()
# direction of diode
ROW2COL = False
def __init__(self):
self.keys = len(self.ROWS) * len(self.COLS)
self.queue = bytearray(self.keys)
self.head = 0
self.tail = 0
self.length = 0
self.rows = [] # row as output
for pin in self.ROWS:
io = digitalio.DigitalInOut(pin)
io.direction = digitalio.Direction.OUTPUT
io.drive_mode = digitalio.DriveMode.PUSH_PULL
io.value = 0
self.rows.append(io)
self.cols = [] # col as input
for pin in self.COLS:
io = digitalio.DigitalInOut(pin)
io.direction = digitalio.Direction.INPUT
io.pull = digitalio.Pull.DOWN if self.ROW2COL else digitalio.Pull.UP
self.cols.append(io)
# row selected value depends on diodes' direction
self.pressed = bool(self.ROW2COL)
self.t0 = [0] * self.keys # key pressed time
self.t1 = [0] * self.keys # key released time
self.mask = 0
self.count = 0
self._debounce_time = 20000000
def scan(self):
"""
Scan keyboard matrix and save key event into the queue.
:return: length of the key event queue.
"""
t = time.monotonic_ns()
# use local variables to speed up
pressed = self.pressed
last_mask = self.mask
cols = self.cols
mask = 0
count = 0
key_index = -1
for row in self.rows:
row.value = pressed # select row
for col in cols:
key_index += 1
if col.value == pressed:
key_mask = 1 << key_index
if not (last_mask & key_mask):
if t - self.t1[key_index] < self._debounce_time:
print("debonce")
continue
self.t0[key_index] = t
self.put(key_index)
mask |= key_mask
count += 1
elif last_mask and (last_mask & (1 << key_index)):
if t - self.t0[key_index] < self._debounce_time:
print("debonce")
mask |= 1 << key_index
continue
self.t1[key_index] = t
self.put(0x80 | key_index)
row.value = not pressed
self.mask = mask
self.count = count
return self.length
def wait(self, timeout=1000):
"""Wait for a new key event or timeout"""
last = self.length
if timeout:
end_time = time.monotonic_ns() + timeout * 1000000
while True:
n = self.scan()
if n > last or time.monotonic_ns() > end_time:
return n
else:
while True:
n = self.scan()
if n > last:
return n
def put(self, data):
"""Put a key event into the queue"""
self.queue[self.head] = data
self.head += 1
if self.head >= self.keys:
self.head = 0
self.length += 1
def get(self):
"""Remove and return the first event from the queue."""
data = self.queue[self.tail]
self.tail += 1
if self.tail >= self.keys:
self.tail = 0
self.length -= 1
return data
def view(self, n):
"""Return the specified event"""
return self.queue[(self.tail + n) % self.keys]
def __getitem__(self, n):
"""Return the specified event"""
return self.queue[(self.tail + n) % self.keys]
def __len__(self):
"""Return the number of events in the queue"""
return self.length
def get_keydown_time(self, key):
"""Return the key pressed time"""
return self.t0[key]
def get_keyup_time(self, key):
"""Return the key released time"""
return self.t1[key]
def time(self):
"""Return current time"""
return time.monotonic_ns()
def ms(self, t):
"""Convert time to milliseconds"""
return t // 1000000
@property
def debounce_time(self):
return self._debounce_time // 1000000
@debounce_time.setter
def debounce_time(self, t):
"""Set debounce time"""
self._debounce_time = t * 1000000
def suspend(self):
"""Suspend keyboard"""
pass