Misc files
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import digitalio
  2. def intify_coordinate(row, col):
  3. return row << 8 | col
  4. class DiodeOrientation:
  5. '''
  6. Orientation of diodes on handwired boards. You can think of:
  7. COLUMNS = vertical
  8. ROWS = horizontal
  9. '''
  10. COLUMNS = 0
  11. ROWS = 1
  12. class MatrixScanner:
  13. def __init__(
  14. self,
  15. cols,
  16. rows,
  17. diode_orientation=DiodeOrientation.COLUMNS,
  18. rollover_cols_every_rows=None,
  19. ):
  20. self.len_cols = len(cols)
  21. self.len_rows = len(rows)
  22. # A pin cannot be both a row and column, detect this by combining the
  23. # two tuples into a set and validating that the length did not drop
  24. #
  25. # repr() hackery is because CircuitPython Pin objects are not hashable
  26. unique_pins = {repr(c) for c in cols} | {repr(r) for r in rows}
  27. assert (
  28. len(unique_pins) == self.len_cols + self.len_rows
  29. ), 'Cannot use a pin as both a column and row'
  30. del unique_pins
  31. self.diode_orientation = diode_orientation
  32. # __class__.__name__ is used instead of isinstance as the MCP230xx lib
  33. # does not use the digitalio.DigitalInOut, but rather a self defined one:
  34. # https://github.com/adafruit/Adafruit_CircuitPython_MCP230xx/blob/3f04abbd65ba5fa938fcb04b99e92ae48a8c9406/adafruit_mcp230xx/digital_inout.py#L33
  35. if self.diode_orientation == DiodeOrientation.COLUMNS:
  36. self.outputs = [
  37. x
  38. if x.__class__.__name__ is 'DigitalInOut'
  39. else digitalio.DigitalInOut(x)
  40. for x in cols
  41. ]
  42. self.inputs = [
  43. x
  44. if x.__class__.__name__ is 'DigitalInOut'
  45. else digitalio.DigitalInOut(x)
  46. for x in rows
  47. ]
  48. self.translate_coords = True
  49. elif self.diode_orientation == DiodeOrientation.ROWS:
  50. self.outputs = [
  51. x
  52. if x.__class__.__name__ is 'DigitalInOut'
  53. else digitalio.DigitalInOut(x)
  54. for x in rows
  55. ]
  56. self.inputs = [
  57. x
  58. if x.__class__.__name__ is 'DigitalInOut'
  59. else digitalio.DigitalInOut(x)
  60. for x in cols
  61. ]
  62. self.translate_coords = False
  63. else:
  64. raise ValueError(
  65. 'Invalid DiodeOrientation: {}'.format(self.diode_orientation)
  66. )
  67. for pin in self.outputs:
  68. pin.switch_to_output()
  69. for pin in self.inputs:
  70. pin.switch_to_input(pull=digitalio.Pull.UP)
  71. self.rollover_cols_every_rows = rollover_cols_every_rows
  72. if self.rollover_cols_every_rows is None:
  73. self.rollover_cols_every_rows = self.len_rows
  74. self.len_state_arrays = self.len_cols * self.len_rows
  75. self.state = bytearray(self.len_state_arrays)
  76. self.report = bytearray(3)
  77. def scan_for_changes(self):
  78. '''
  79. Poll the matrix for changes and return either None (if nothing updated)
  80. or a bytearray (reused in later runs so copy this if you need the raw
  81. array itself for some crazy reason) consisting of (row, col, pressed)
  82. which are (int, int, bool)
  83. '''
  84. ba_idx = 0
  85. any_changed = False
  86. for oidx, opin in enumerate(self.outputs):
  87. opin.value = False
  88. for iidx, ipin in enumerate(self.inputs):
  89. # cast to int to avoid
  90. #
  91. # >>> xyz = bytearray(3)
  92. # >>> xyz[2] = True
  93. # Traceback (most recent call last):
  94. # File "<stdin>", line 1, in <module>
  95. # OverflowError: value would overflow a 1 byte buffer
  96. #
  97. # I haven't dived too far into what causes this, but it's
  98. # almost certainly because bool types in Python aren't just
  99. # aliases to int values, but are proper pseudo-types
  100. new_val = int(not ipin.value)
  101. old_val = self.state[ba_idx]
  102. if old_val != new_val:
  103. if self.translate_coords:
  104. new_oidx = oidx + self.len_cols * (
  105. iidx // self.rollover_cols_every_rows
  106. )
  107. new_iidx = iidx - self.rollover_cols_every_rows * (
  108. iidx // self.rollover_cols_every_rows
  109. )
  110. self.report[0] = new_iidx
  111. self.report[1] = new_oidx
  112. else:
  113. self.report[0] = oidx
  114. self.report[1] = iidx
  115. self.report[2] = new_val
  116. self.state[ba_idx] = new_val
  117. any_changed = True
  118. break
  119. ba_idx += 1
  120. opin.value = True
  121. if any_changed:
  122. break
  123. if any_changed:
  124. return self.report