@@ -136,8 +136,72 @@ def __len__(self) -> int:
136
136
return len (self ._outq ) + len (self ._inq )
137
137
138
138
139
- class McpMatrixScanner :
139
+ class McpScanner :
140
+
141
+ def __init__ (self ,
142
+ mcp : any ,
143
+ irq : Optional [Pin ] = None ,
144
+ ):
145
+ self .mcp = mcp
146
+ self .keys_state = set ()
147
+ self .events = EventQueue ()
148
+ self .irq = None
149
+ if irq :
150
+ self .irq = DigitalInOut (irq )
151
+ self .irq .switch_to_input (Pull .UP )
152
+
153
+ @property
154
+ def key_count (self ) -> int :
155
+ """The number of keys in the scanner."""
156
+ return self ._key_count
157
+
158
+ def update (self ) -> None :
159
+ """
160
+ Run the scan and create events in the event queue.
161
+ """
162
+ timestamp = ticks_ms ()
163
+ # scan the matrix, find Neo
164
+ current_state = self ._scan_pins ()
165
+ # use set algebra to find released and pressed keys
166
+ released_keys = self .keys_state - current_state
167
+ pressed_keys = current_state - self .keys_state
168
+ # create the events into the queue
169
+ for key in released_keys :
170
+ self .events .append (Event (key , False , timestamp ))
171
+ for key in pressed_keys :
172
+ self .events .append (Event (key , True , timestamp ))
173
+ # end
174
+ self .keys_state = current_state
175
+
176
+ def reset (self ) -> None :
177
+ """
178
+ Reset the internal state of the scanner to assume that all keys are now
179
+ released. Any key that is already pressed at the time of this call will
180
+ therefore cause a new key-pressed event to occur on the next scan.
181
+ """
182
+ self .events .clear ()
183
+ self .keys_state .clear ()
184
+
185
+ def deinit (self ) -> None :
186
+ """Release the IRQ pin"""
187
+ if self .irq :
188
+ self .irq .deinit ()
189
+ self .irq = None
190
+ # TODO: reset the mcp configuration
191
+
192
+ def __enter__ (self ) -> "McpScanner" :
193
+ """No-op used by Context Managers."""
194
+ return self
195
+
196
+ def __exit__ (self , type_er , value , traceback ) -> None :
197
+ """Automatically deinitializes when exiting a context."""
198
+ self .deinit ()
199
+
200
+
201
+ class McpMatrixScanner (McpScanner ):
140
202
"""
203
+ Class to scan a matrix of keys connected to the MCP chip.
204
+
141
205
Columns are on port A and inputs.
142
206
Rows are on port B and outputs.
143
207
"""
@@ -149,22 +213,17 @@ def __init__(
149
213
column_pins : Iterable [int ],
150
214
irq : Optional [Pin ] = None ,
151
215
):
216
+ super ().__init__ (mcp , irq )
152
217
self ._key_count = len (column_pins ) * len (row_pins )
153
218
self .columns = column_pins
154
219
self .rows = row_pins
155
- self .mcp = mcp
156
- self .keys_state = set ()
157
- self .events = EventQueue ()
158
220
# set port A to output (columns)
159
221
mcp .iodira = 0x00
160
222
# set port B to input (rows) all pull ups
161
223
mcp .iodirb = 0xFF
162
224
mcp .gppub = 0xFF
163
225
# set interrupts
164
- self .irq = None
165
226
if irq :
166
- self .irq = DigitalInOut (irq )
167
- self .irq .switch_to_input (Pull .UP )
168
227
# TODO: configure mcp based on row and column numbers
169
228
# to leave the other pins free to use ?
170
229
mcp .interrupt_enable = 0xFF00
@@ -174,12 +233,7 @@ def __init__(
174
233
mcp .io_control = 0x44 # Interrupt as open drain and mirrored
175
234
mcp .clear_ints ()
176
235
177
- @property
178
- def key_count (self ) -> int :
179
- """The number of keys in the scanner."""
180
- return self ._key_count
181
-
182
- def _scan_matrix (self ) -> Set [int ]:
236
+ def _scan_pins (self ) -> Set [int ]:
183
237
"""Scan the matrix and return the list of keys down"""
184
238
pressed = set ()
185
239
num_cols = len (self .columns )
@@ -198,24 +252,6 @@ def _scan_matrix(self) -> Set[int]:
198
252
self .mcp .gpioa = 0xFF
199
253
return pressed
200
254
201
- def update (self ) -> None :
202
- """
203
- Run the scan and create events in the event queue.
204
- """
205
- timestamp = ticks_ms ()
206
- # scan the matrix, find Neo
207
- current_state = self ._scan_matrix ()
208
- # use set algebra to find released and pressed keys
209
- released_keys = self .keys_state - current_state
210
- pressed_keys = current_state - self .keys_state
211
- # create the events into the queue
212
- for key in released_keys :
213
- self .events .append (Event (key , False , timestamp ))
214
- for key in pressed_keys :
215
- self .events .append (Event (key , True , timestamp ))
216
- # end
217
- self .keys_state = current_state
218
-
219
255
def key_number_to_row_column (self , key_number : int ) -> Tuple [int ]:
220
256
"""Convert key number to row, column"""
221
257
row = key_number // len (self .columns )
@@ -226,26 +262,43 @@ def row_column_to_key_number(self, row: int, column: int) -> int:
226
262
"""Convert row, column to key number"""
227
263
return row * len (self .columns ) + column
228
264
229
- def reset (self ) -> None :
230
- """
231
- Reset the internal state of the scanner to assume that all keys are now
232
- released. Any key that is already pressed at the time of this call will
233
- therefore cause a new key-pressed event to occur on the next scan.
234
- """
235
- self .events .clear ()
236
- self .keys_state .clear ()
237
265
238
- def deinit (self ) -> None :
239
- """Release the IRQ pin"""
240
- if self .irq :
241
- self .irq .deinit ()
242
- self .irq = None
243
- # TODO: reset the mcp configuration
266
+ class McpKeysScanner (McpScanner ):
267
+ """
268
+ Class to scan a key per pin of the MCP chip.
244
269
245
- def __enter__ (self ) -> "McpMatrixScanner" :
246
- """No-op used by Context Managers."""
247
- return self
270
+ Pins 0-7 are on port A. Pins 8-15 are on port B.
271
+ """
248
272
249
- def __exit__ (self , type_er , value , traceback ) -> None :
250
- """Automatically deinitializes when exiting a context."""
251
- self .deinit ()
273
+ def __init__ (
274
+ self ,
275
+ mcp : any ,
276
+ pins : Iterable [int ],
277
+ irq : Optional [Pin ] = None ,
278
+ ):
279
+ super ().__init__ (mcp , irq )
280
+ self ._key_count = len (pins )
281
+ self .pins = pins
282
+ self .pin_bits = sum (1 << x for x in pins )
283
+ # set port A and B to input all pull ups
284
+ mcp .iodir = 0xFFFF & self .pin_bits
285
+ mcp .gppu = 0xFFFF & self .pin_bits
286
+ # set interrupts
287
+ if irq :
288
+ # set interrupts
289
+ mcp .interrupt_enable = 0xFFFF & self .pin_bits
290
+ mcp .default_value = 0xFFFF & self .pin_bits
291
+ # compare input to default value (1) or previous value (0)
292
+ mcp .interrupt_configuration = 0xFFFF & self .pin_bits
293
+ mcp .io_control = 0x44 # Interrupt as open drain and mirrored
294
+ mcp .clear_ints ()
295
+
296
+ def _scan_pins (self ) -> Set [int ]:
297
+ """Scan the buttons and return the list of keys down"""
298
+ pressed = set ()
299
+ inputs = self .mcp .gpio & self .pin_bits
300
+ for scan in self .pins :
301
+ for pin in self .pins :
302
+ if inputs & (1 << pin ) == 0 :
303
+ pressed .add (pin )
304
+ return pressed
0 commit comments