keybrd library is an open source library for creating custom-keyboard firmware.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

Row.cpp 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "Row.h"
  2. /*
  3. scans the row and calls any newly pressed or released keys.
  4. */
  5. void Row::process(const bool activeHigh)
  6. {
  7. //these variables are all bitwise, one bit per key
  8. uint8_t rowState; //1 means pressed, 0 means released
  9. uint16_t rowEnd; //1 bit marks positioned after last key of row
  10. uint8_t newDebounced; //1 means pressed, 0 means released
  11. uint8_t isFallingEdge; //1 means falling edge
  12. uint8_t isRisingEdge; //1 means rising edge
  13. scan(activeHigh); //save column-port-pin values to portState
  14. rowState = getRowState(rowEnd, activeHigh);
  15. newDebounced = debounce(rowState);
  16. detectEdge(newDebounced, isFallingEdge, isRisingEdge);
  17. pressRelease(rowEnd, isFallingEdge, isRisingEdge);
  18. }
  19. /*
  20. Strobes the row and reads the columns.
  21. Strobe is on for shortest possible time to preserve IR LED on DodoHand's optic switch.
  22. */
  23. void Row::scan(const bool activeHigh)
  24. {
  25. //strobe row on
  26. if (activeHigh)
  27. {
  28. refRowPort.setActivePinHigh(rowPin);
  29. }
  30. else //activeLow
  31. {
  32. refRowPort.setActivePinLow(rowPin);
  33. }
  34. //read all the column ports
  35. for (uint8_t i=0; i < colPortCount; i++)
  36. {
  37. ptrsColPorts[i]->read();
  38. }
  39. //strobe row off
  40. if (activeHigh)
  41. {
  42. refRowPort.setActivePinLow(rowPin);
  43. }
  44. else //activeLow
  45. {
  46. refRowPort.setActivePinHigh(rowPin);
  47. }
  48. }
  49. /*
  50. Copies column pins to rowState. Unused column pins are not copied.
  51. Sets rowEnd and returns rowState.
  52. rowEnd is bitwise, where 1 bit corrsiponds to place immediatly after last key of row.
  53. rowEnd and rowMask are larger type than portMask so that they can not overflow.
  54. */
  55. uint8_t Row::getRowState(uint16_t& rowEnd, const bool activeHigh)
  56. {
  57. uint16_t rowMask = 1; //bitwise, one col per bit, active col bit is 1
  58. uint8_t rowState = 0; //bitwise, one key per bit, 1 means key is pressed
  59. for (uint8_t i=0; i < colPortCount; i++) //for each col port
  60. {
  61. //bitwise colPins, 1 means pin is connected to column
  62. uint8_t colPins = ptrsColPorts[i]->getColPins();
  63. //bitwise colPortState, pin values where set in ColPort::read(), get them now
  64. uint8_t colPortState = ptrsColPorts[i]->getPortState();
  65. if (activeHigh)
  66. {
  67. colPortState = ~colPortState;
  68. }
  69. for ( uint8_t portMask = 1; portMask > 0; portMask <<= 1 ) //shift portMask until overflow
  70. { //for each pin of col port
  71. if (portMask & colPins) //if pin is connected to column
  72. {
  73. if (portMask & ~colPortState) //if pin detected a key press
  74. {
  75. rowState |= rowMask; //set rowState bit for that key
  76. }
  77. rowMask <<= 1; //shift rowMask to next key
  78. }
  79. }
  80. }
  81. rowEnd = rowMask;
  82. return rowState;
  83. }
  84. /*
  85. Parameter rowState is bitwise, 1 means pressed, 0 means released.
  86. Returns debounced rowState.
  87. Debounce uses multiple samples to debounces switch states,
  88. where each sample contains the switch states for a row of keys, one bit per switch.
  89. Debounce uses Marty's debounce algorithm from
  90. http://drmarty.blogspot.com.br/2009/05/best-switch-debounce-routine-ever.html
  91. I2C and TWI protocals do not include any Packet Error Checking (PEC).
  92. The goal of Marty's debounce routine is to reject spurious signals,
  93. which is useful when conecting split keyboards with a cable using I2C or TWI.
  94. Was tested on split keyboard with 3-meter long telephone wire to I/O expander
  95. Marty's debounce algorithm:
  96. Periodically read samples and update the state when a number consecutive sample bits are equal.
  97. samples[SAMPLE_COUNT] is a ring buffer and samplesIndex is it's current write index.
  98. SAMPLE_COUNT is #defined in Row.h
  99. SAMPLE_COUNT is the number of consecutive equal samples needed to debounce.
  100. It is a macro because it is used to define array size of samples[SAMPLE_COUNT] in Row.
  101. SAMPLE_COUNT should be at lease 2.
  102. Multiple samples are for error correction on I2C I/O expander and shorten response time.
  103. On keyboards without I/O expander, multiple samples only shorten response time.
  104. Larger SAMPLE_COUNTs are more reliable but consume more memory, where
  105. SAMPLE_COUNT*ROW_COUNT = bytes of memory consumed by keyboard
  106. So don't make SAMPLE_COUNT too large, SAMPLE_COUNT = 4 is very reliable for I2C error correction.
  107. big SAMPLE_COUNT for fast response, small SAMPLE_COUNT to save memory
  108. there is a way to define SAMPLE_COUNT in the sketch, but it's ugly
  109. see http://forum.arduino.cc/index.php?topic=364843.0 > rely #2
  110. */
  111. uint8_t Row::debounce(const uint8_t rowState)
  112. {
  113. uint8_t all_1 = ~0; //bitwise
  114. uint8_t all_0 = 0; //bitwise
  115. delayMicroseconds(DELAY_MICROSECONDS); //delay between Row scans to debounce key
  116. samples[samplesIndex] = rowState; //insert rowState into samples[] ring buffer
  117. if (++samplesIndex >= SAMPLE_COUNT)
  118. {
  119. samplesIndex = 0; //wrap samplesIndex to beginning of ring buffer
  120. }
  121. for (uint8_t j = 0; j < SAMPLE_COUNT; j++) //traverse the sample[] ring buffer
  122. {
  123. all_1 &= samples[j]; //1 if all samples are 1
  124. all_0 |= samples[j]; //0 if all samples are 0
  125. }
  126. // update newDebounce if all the samples agree with one another
  127. // if all samples=1 then newDebounced=1
  128. // elseif all samples=0 then newDebounced=0
  129. // else newDebounced=debounced i.e. no change
  130. return all_1 | (all_0 & debounced);
  131. }
  132. /*
  133. Computes isFallingEdge and isRisingEdge.
  134. All 3 parameters are bitwise.
  135. */
  136. void Row::detectEdge(uint8_t newDebounced, uint8_t& isFallingEdge, uint8_t& isRisingEdge)
  137. {
  138. uint8_t debouncedChanged; //bitwise
  139. debouncedChanged = newDebounced xor debounced;
  140. debounced = newDebounced;
  141. //bit=1 if last debounced changed from 1 to 0, else bit=0
  142. isFallingEdge = debouncedChanged & ~debounced;
  143. //bit=1 if last debounced changed from 0 to 1, else bit=0
  144. isRisingEdge = debouncedChanged & debounced;
  145. }
  146. /*
  147. calls key's press() or release() function if it was pressed or released.
  148. All 3 parameters are bitwise.
  149. */
  150. void Row::pressRelease(const uint16_t rowEnd, const uint8_t isFallingEdge,
  151. const uint8_t isRisingEdge)
  152. {
  153. uint8_t rowMask; //bitwise, active col bit is 1
  154. uint8_t col; //index for ptrsKeys[col] array
  155. for (rowMask=1, col=0; rowMask<rowEnd; rowMask<<=1, col++) //for each key in row
  156. {
  157. //release before press avoids impossible key sequence
  158. if (rowMask & isFallingEdge) //if key was released
  159. {
  160. ptrsKeys[col]->release();
  161. }
  162. if (rowMask & isRisingEdge) //if key was pressed
  163. {
  164. ptrsKeys[col]->press();
  165. keyWasPressed();
  166. }
  167. }
  168. }
  169. void Row::keyWasPressed()
  170. {
  171. //empty in Row class. To unstick sticky keys, override keyWasPressed() in derived class.
  172. }