From 2cbef3ea044998e521adaa89c63e9fc8a3f31e43 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Fri, 8 Feb 2019 13:56:14 -0500 Subject: [PATCH] Sample at 100Hz for a burst --- shared/mempad.py | 88 ++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/shared/mempad.py b/shared/mempad.py index b7fb68a1..c93a5a7e 100644 --- a/shared/mempad.py +++ b/shared/mempad.py @@ -3,7 +3,7 @@ # # numpad.py - Numeric keypad. Touch button matrix. # -import array, utime +import array, utime, pyb from uasyncio.queues import Queue from machine import Pin from random import shuffle @@ -13,6 +13,8 @@ _singleton = None NUM_ROWS = const(4) HISTORY_LEN = const(3) +SAMPLE_RATE = const(10) # ms +NUM_SAMPLES = const(NUM_ROWS * HISTORY_LEN) class MembraneNumpad(NumpadBase): @@ -23,6 +25,9 @@ class MembraneNumpad(NumpadBase): assert not _singleton _singleton = self + # No idea how to pick a safe timer number. + self.timer = pyb.Timer(7) + self.cols = [Pin(i, Pin.IN, pull=Pin.PULL_UP) for i in ('M2_COL0', 'M2_COL1', 'M2_COL2')] self.rows = [Pin(i, Pin.OUT_OD, value=0) @@ -30,23 +35,23 @@ class MembraneNumpad(NumpadBase): # Scan in random order, because Tempest. # However, we only scan where there is a touch, and we mustn't - # reveal which one by doing anything differently (in terms of scan). - self.scan_order = list(range(NUM_ROWS)) * HISTORY_LEN + # reveal which one by doing anything differently (in terms of scan pattern). + self.scan_order = array.array('i', list(range(NUM_ROWS)) * HISTORY_LEN) shuffle(self.scan_order) - self.scan_idx = 0 - self.history = [] - self.wait_any = True + self.history = array.array('i', (-1 for i in range(NUM_SAMPLES))) + self.scan_idx = 0 + self.waiting_for_any = True for c in self.cols: - c.irq(self.irq_handler, Pin.IRQ_FALLING|Pin.IRQ_RISING) + c.irq(self.anypress_irq, Pin.IRQ_FALLING|Pin.IRQ_RISING) # ready to start self.loop = loop - def irq_handler(self, pin): + def anypress_irq(self, pin): # come here for any change, high or low - if self.wait_any: + if self.waiting_for_any: # something was pressed, but we don't know what.. start a scan+debounce self._start_scan() @@ -57,35 +62,37 @@ class MembraneNumpad(NumpadBase): def _wait_any(self): # wait for any press. + self.timer.deinit() + for r in self.rows: r.off() - self.wait_any = True + self.waiting_for_any = True def _start_scan(self): # reset and start a new scan if self._disabled: return - self.history.clear() - self.wait_any = False - self.scan_idx = len(self.scan_order)-1 + self.waiting_for_any = False + self.scan_idx = NUM_SAMPLES-1 + self._scan_next() + self.timer.init(freq=1000//SAMPLE_RATE, callback=self._measure_irq) + self.loop.call_later_ms(SAMPLE_RATE * (NUM_SAMPLES + 2), self._finish_scan) def _scan_next(self): # enable detection on one row active_row = self.scan_order[self.scan_idx] - for r, pin in enumerate(self.rows): - if r == active_row: - pin.off() - else: - pin.on() + # unrolled because called during irq, no memory alloc + self.rows[0].value(active_row != 0) + self.rows[1].value(active_row != 1) + self.rows[2].value(active_row != 2) + self.rows[3].value(active_row != 3) - # schedule an event for a little later - self.loop.call_later_ms(20, self._measure) + def _measure_irq(self, _timer): + # CAUTION: Called at high rate, and cannot do memory alloc. - def _measure(self): - - # sample the column values (unroll for simple) + # sample the column values if self.cols[0].value() == 0: col_press = 0 elif self.cols[1].value() == 0: @@ -95,22 +102,30 @@ class MembraneNumpad(NumpadBase): else: col_press = -1 - if col_press != -1: - # anything down? - active_row = self.scan_order[self.scan_idx] - key = self.DECODER[(active_row, col_press)] - self.history.append(key) - print('+ ' + key) + # track sample data + self.history[self.scan_idx] = col_press - if self.scan_idx: + # move to next column, or stop + if self.scan_idx == 0: + # we are done a full scan + _timer.deinit() + else: self.scan_idx -= 1 self._scan_next() - return + def _finish_scan(self): # we're done a full scan (mulitple times: HISTORY_LEN) - if not self.history: + down = set() + + for i in range(NUM_ROWS * HISTORY_LEN): + # anything down? + col_press = self.history[i] + if col_press != -1: + key = self.DECODER[(self.scan_order[i], col_press)] + down.add(key) + + if not down: # all keys are up. - print('+quiet') self._key_event('') # stop scanning for now @@ -119,11 +134,10 @@ class MembraneNumpad(NumpadBase): else: # perform debounce # - do nothing if abiguous or in transition. - s = set(self.history) - if len(s) == 1: - self._key_event(self.history[0]) + if len(down) == 1: + self._key_event(down.pop()) else: - print('bounce: ' + ' '.join(s)) + print('bounce: ' + ' '.join(down)) self._start_scan()