ec_switch_matrix.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /* Copyright 2023 Cipulot
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "ec_switch_matrix.h"
  17. #include "analog.h"
  18. #include "atomic_util.h"
  19. #include "math.h"
  20. #include "print.h"
  21. #include "wait.h"
  22. #if defined(__AVR__)
  23. # error "AVR platforms not supported due to a variety of reasons. Among them there are limited memory, limited number of pins and ADC not being able to give satisfactory results."
  24. #endif
  25. #define OPEN_DRAIN_SUPPORT defined(PAL_MODE_OUTPUT_OPENDRAIN)
  26. eeprom_ec_config_t eeprom_ec_config;
  27. ec_config_t ec_config;
  28. // Pin and port array
  29. const pin_t row_pins[] = MATRIX_ROW_PINS;
  30. const pin_t amux_sel_pins[] = AMUX_SEL_PINS;
  31. const pin_t amux_en_pins[] = AMUX_EN_PINS;
  32. const pin_t amux_n_col_sizes[] = AMUX_COL_CHANNELS_SIZES;
  33. const pin_t amux_n_col_channels[][AMUX_MAX_COLS_COUNT] = {AMUX_COL_CHANNELS};
  34. #define AMUX_SEL_PINS_COUNT ARRAY_SIZE(amux_sel_pins)
  35. #define EXPECTED_AMUX_SEL_PINS_COUNT ceil(log2(AMUX_MAX_COLS_COUNT)
  36. // Checks for the correctness of the configuration
  37. _Static_assert(ARRAY_SIZE(amux_en_pins) == AMUX_COUNT, "AMUX_EN_PINS doesn't have the minimum number of bits required to enable all the multiplexers available");
  38. // Check that number of select pins is enough to select all the channels
  39. _Static_assert(AMUX_SEL_PINS_COUNT == EXPECTED_AMUX_SEL_PINS_COUNT), "AMUX_SEL_PINS doesn't have the minimum number of bits required address all the channels");
  40. // Check that number of elements in AMUX_COL_CHANNELS_SIZES is enough to specify the number of channels for all the multiplexers available
  41. _Static_assert(ARRAY_SIZE(amux_n_col_sizes) == AMUX_COUNT, "AMUX_COL_CHANNELS_SIZES doesn't have the minimum number of elements required to specify the number of channels for all the multiplexers available");
  42. static uint16_t sw_value[MATRIX_ROWS][MATRIX_COLS];
  43. static adc_mux adcMux;
  44. // Initialize the row pins
  45. void init_row(void) {
  46. // Set all row pins as output and low
  47. for (uint8_t idx = 0; idx < MATRIX_ROWS; idx++) {
  48. gpio_set_pin_output(row_pins[idx]);
  49. gpio_write_pin_low(row_pins[idx]);
  50. }
  51. }
  52. // Initialize the multiplexers
  53. void init_amux(void) {
  54. for (uint8_t idx = 0; idx < AMUX_COUNT; idx++) {
  55. gpio_set_pin_output(amux_en_pins[idx]);
  56. gpio_write_pin_low(amux_en_pins[idx]);
  57. }
  58. for (uint8_t idx = 0; idx < AMUX_SEL_PINS_COUNT; idx++) {
  59. gpio_set_pin_output(amux_sel_pins[idx]);
  60. }
  61. }
  62. // Select the multiplexer channel of the specified multiplexer
  63. void select_amux_channel(uint8_t channel, uint8_t col) {
  64. // Get the channel for the specified multiplexer
  65. uint8_t ch = amux_n_col_channels[channel][col];
  66. // momentarily disable specified multiplexer
  67. gpio_write_pin_high(amux_en_pins[channel]);
  68. // Select the multiplexer channel
  69. for (uint8_t i = 0; i < AMUX_SEL_PINS_COUNT; i++) {
  70. gpio_write_pin(amux_sel_pins[i], ch & (1 << i));
  71. }
  72. // re enable specified multiplexer
  73. gpio_write_pin_low(amux_en_pins[channel]);
  74. }
  75. // Disable all the unused multiplexers
  76. void disable_unused_amux(uint8_t channel) {
  77. // disable all the other multiplexers apart from the current selected one
  78. for (uint8_t idx = 0; idx < AMUX_COUNT; idx++) {
  79. if (idx != channel) {
  80. gpio_write_pin_high(amux_en_pins[idx]);
  81. }
  82. }
  83. }
  84. // Discharge the peak hold capacitor
  85. void discharge_capacitor(void) {
  86. #ifdef OPEN_DRAIN_SUPPORT
  87. gpio_write_pin_low(DISCHARGE_PIN);
  88. #else
  89. gpio_write_pin_low(DISCHARGE_PIN);
  90. gpio_set_pin_output(DISCHARGE_PIN);
  91. #endif
  92. }
  93. // Charge the peak hold capacitor
  94. void charge_capacitor(uint8_t row) {
  95. #ifdef OPEN_DRAIN_SUPPORT
  96. gpio_write_pin_high(DISCHARGE_PIN);
  97. #else
  98. gpio_set_pin_input(DISCHARGE_PIN);
  99. #endif
  100. gpio_write_pin_high(row_pins[row]);
  101. }
  102. // Initialize the peripherals pins
  103. int ec_init(void) {
  104. // Initialize ADC
  105. palSetLineMode(ANALOG_PORT, PAL_MODE_INPUT_ANALOG);
  106. adcMux = pinToMux(ANALOG_PORT);
  107. // Dummy call to make sure that adcStart() has been called in the appropriate state
  108. adc_read(adcMux);
  109. // Initialize discharge pin as discharge mode
  110. gpio_write_pin_low(DISCHARGE_PIN);
  111. #ifdef OPEN_DRAIN_SUPPORT
  112. gpio_set_pin_output_open_drain(DISCHARGE_PIN);
  113. #else
  114. gpio_set_pin_output(DISCHARGE_PIN);
  115. #endif
  116. // Initialize drive lines
  117. init_row();
  118. // Initialize AMUXs
  119. init_amux();
  120. return 0;
  121. }
  122. // Get the noise floor
  123. void ec_noise_floor(void) {
  124. // Initialize the noise floor
  125. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  126. for (uint8_t col = 0; col < MATRIX_COLS; col++) {
  127. ec_config.noise_floor[row][col] = 0;
  128. }
  129. }
  130. // Sample the noise floor
  131. for (uint8_t i = 0; i < DEFAULT_NOISE_FLOOR_SAMPLING_COUNT; i++) {
  132. for (uint8_t amux = 0; amux < AMUX_COUNT; amux++) {
  133. disable_unused_amux(amux);
  134. for (uint8_t col = 0; col < amux_n_col_sizes[amux]; col++) {
  135. uint8_t sum = 0;
  136. for (uint8_t i = 0; i < (amux > 0 ? amux : 0); i++)
  137. sum += amux_n_col_sizes[i];
  138. uint8_t adjusted_col = col + sum;
  139. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  140. ec_config.noise_floor[row][adjusted_col] += ec_readkey_raw(amux, row, col);
  141. }
  142. }
  143. }
  144. wait_ms(5);
  145. }
  146. // Average the noise floor
  147. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  148. for (uint8_t col = 0; col < MATRIX_COLS; col++) {
  149. ec_config.noise_floor[row][col] /= DEFAULT_NOISE_FLOOR_SAMPLING_COUNT;
  150. }
  151. }
  152. }
  153. // Scan key values and update matrix state
  154. bool ec_matrix_scan(matrix_row_t current_matrix[]) {
  155. bool updated = false;
  156. for (uint8_t amux = 0; amux < AMUX_COUNT; amux++) {
  157. disable_unused_amux(amux);
  158. for (uint8_t col = 0; col < amux_n_col_sizes[amux]; col++) {
  159. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  160. uint8_t sum = 0;
  161. for (uint8_t i = 0; i < (amux > 0 ? amux : 0); i++)
  162. sum += amux_n_col_sizes[i];
  163. uint8_t adjusted_col = col + sum;
  164. sw_value[row][adjusted_col] = ec_readkey_raw(amux, row, col);
  165. if (ec_config.bottoming_calibration) {
  166. if (ec_config.bottoming_calibration_starter[row][adjusted_col]) {
  167. ec_config.bottoming_reading[row][adjusted_col] = sw_value[row][adjusted_col];
  168. ec_config.bottoming_calibration_starter[row][adjusted_col] = false;
  169. } else if (sw_value[row][adjusted_col] > ec_config.bottoming_reading[row][adjusted_col]) {
  170. ec_config.bottoming_reading[row][adjusted_col] = sw_value[row][adjusted_col];
  171. }
  172. } else {
  173. updated |= ec_update_key(&current_matrix[row], row, adjusted_col, sw_value[row][adjusted_col]);
  174. }
  175. }
  176. }
  177. }
  178. return ec_config.bottoming_calibration ? false : updated;
  179. }
  180. // Read the capacitive sensor value
  181. uint16_t ec_readkey_raw(uint8_t channel, uint8_t row, uint8_t col) {
  182. uint16_t sw_value = 0;
  183. // Select the multiplexer
  184. select_amux_channel(channel, col);
  185. // Set the row pin to low state to avoid ghosting
  186. gpio_write_pin_low(row_pins[row]);
  187. ATOMIC_BLOCK_FORCEON {
  188. // Set the row pin to high state and have capacitor charge
  189. charge_capacitor(row);
  190. // Read the ADC value
  191. sw_value = adc_read(adcMux);
  192. }
  193. // Discharge peak hold capacitor
  194. discharge_capacitor();
  195. // Waiting for the ghost capacitor to discharge fully
  196. wait_us(DISCHARGE_TIME);
  197. return sw_value;
  198. }
  199. // Update press/release state of key
  200. bool ec_update_key(matrix_row_t* current_row, uint8_t row, uint8_t col, uint16_t sw_value) {
  201. bool current_state = (*current_row >> col) & 1;
  202. // Real Time Noise Floor Calibration
  203. if (sw_value < (ec_config.noise_floor[row][col] - NOISE_FLOOR_THRESHOLD)) {
  204. uprintf("Noise Floor Change: %d, %d, %d\n", row, col, sw_value);
  205. ec_config.noise_floor[row][col] = sw_value;
  206. ec_config.rescaled_mode_0_actuation_threshold[row][col] = rescale(ec_config.mode_0_actuation_threshold, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
  207. ec_config.rescaled_mode_0_release_threshold[row][col] = rescale(ec_config.mode_0_release_threshold, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
  208. ec_config.rescaled_mode_1_initial_deadzone_offset[row][col] = rescale(ec_config.mode_1_initial_deadzone_offset, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
  209. }
  210. // Normal board-wide APC
  211. if (ec_config.actuation_mode == 0) {
  212. if (current_state && sw_value < ec_config.rescaled_mode_0_release_threshold[row][col]) {
  213. *current_row &= ~(1 << col);
  214. uprintf("Key released: %d, %d, %d\n", row, col, sw_value);
  215. return true;
  216. }
  217. if ((!current_state) && sw_value > ec_config.rescaled_mode_0_actuation_threshold[row][col]) {
  218. *current_row |= (1 << col);
  219. uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
  220. return true;
  221. }
  222. }
  223. // Rapid Trigger
  224. else if (ec_config.actuation_mode == 1) {
  225. // Is key in active zone?
  226. if (sw_value > ec_config.rescaled_mode_1_initial_deadzone_offset[row][col]) {
  227. // Is key pressed while in active zone?
  228. if (current_state) {
  229. // Is the key still moving down?
  230. if (sw_value > ec_config.extremum[row][col]) {
  231. ec_config.extremum[row][col] = sw_value;
  232. uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
  233. }
  234. // Has key moved up enough to be released?
  235. else if (sw_value < ec_config.extremum[row][col] - ec_config.mode_1_release_offset) {
  236. ec_config.extremum[row][col] = sw_value;
  237. *current_row &= ~(1 << col);
  238. uprintf("Key released: %d, %d, %d\n", row, col, sw_value);
  239. return true;
  240. }
  241. }
  242. // Key is not pressed while in active zone
  243. else {
  244. // Is the key still moving up?
  245. if (sw_value < ec_config.extremum[row][col]) {
  246. ec_config.extremum[row][col] = sw_value;
  247. }
  248. // Has key moved down enough to be pressed?
  249. else if (sw_value > ec_config.extremum[row][col] + ec_config.mode_1_actuation_offset) {
  250. ec_config.extremum[row][col] = sw_value;
  251. *current_row |= (1 << col);
  252. uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
  253. return true;
  254. }
  255. }
  256. }
  257. // Key is not in active zone
  258. else {
  259. // Check to avoid key being stuck in pressed state near the active zone threshold
  260. if (sw_value < ec_config.extremum[row][col]) {
  261. ec_config.extremum[row][col] = sw_value;
  262. *current_row &= ~(1 << col);
  263. return true;
  264. }
  265. }
  266. }
  267. return false;
  268. }
  269. // Print the matrix values
  270. void ec_print_matrix(void) {
  271. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  272. for (uint8_t col = 0; col < MATRIX_COLS - 1; col++) {
  273. uprintf("%4d,", sw_value[row][col]);
  274. }
  275. uprintf("%4d\n", sw_value[row][MATRIX_COLS - 1]);
  276. }
  277. print("\n");
  278. }
  279. // Rescale the value to a different range
  280. uint16_t rescale(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
  281. return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
  282. }