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.

97 lines
3.0 KiB

  1. // Copyright 2023 Kyle McCreery
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "matrix.h"
  4. #include "mcp23018.h"
  5. #include "wait.h"
  6. #include "debug.h"
  7. #include "encoder.h"
  8. #define I2C_ADDR 0x20
  9. #define ROW_POS { 0b01000000, 0b10000000, 0b01000000, 0b10000000, 0b00000100, 0b00010000, 0b00100000, 0b00000010, 0b00001000 }
  10. static uint8_t mcp23018_errors = 0;
  11. static void mcp23018_init_cols(void) {
  12. mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, ALL_INPUT);
  13. mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT);
  14. }
  15. static void mcp23018_scan(void) {
  16. if (!mcp23018_errors) {
  17. return;
  18. }
  19. static uint16_t mcp23018_reset_loop = 0;
  20. if (++mcp23018_reset_loop > 0x1FFF) {
  21. // tuned to about 5s given the current scan rate
  22. dprintf("trying to reset mcp23018\n");
  23. mcp23018_reset_loop = 0;
  24. mcp23018_errors = 0;
  25. mcp23018_init_cols();
  26. }
  27. }
  28. static matrix_row_t read_cols(void) {
  29. if (mcp23018_errors) {
  30. return 0;
  31. }
  32. uint8_t ret = 0xFF; // sets all to 1
  33. mcp23018_errors += !mcp23018_readPins(I2C_ADDR, mcp23018_PORTB, &ret); // will update with values 0 = pulled down by connection, 1 = pulled up by pullup resistors
  34. return (~ret) & 0b00111111; // Clears out the two row bits in the B buffer.
  35. }
  36. static void select_row(uint8_t row) {
  37. uint8_t row_pos[MATRIX_ROWS] = ROW_POS;
  38. if (mcp23018_errors) {
  39. // wait to mimic i2c interactions
  40. //wait_us(100);
  41. return;
  42. }
  43. if (row > 1) {
  44. mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT);
  45. mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, ~(row_pos[row]));
  46. } else {
  47. mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, ALL_INPUT);
  48. mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ~(row_pos[row]));
  49. }
  50. }
  51. static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
  52. // Store last value of row prior to reading
  53. matrix_row_t last_row_value = current_matrix[current_row];
  54. // Clear data in matrix row
  55. current_matrix[current_row] = 0;
  56. // Select row and wait for row selection to stabilize
  57. select_row(current_row);
  58. // Skip the wait_us(30); as i2c is slow enough to debounce the io changes
  59. current_matrix[current_row] = read_cols();
  60. return (last_row_value != current_matrix[current_row]);
  61. }
  62. void matrix_init_custom(void) {
  63. mcp23018_init(I2C_ADDR);
  64. mcp23018_init_cols();
  65. }
  66. bool matrix_scan_custom(matrix_row_t current_matrix[]) {
  67. mcp23018_scan();
  68. bool changed = false;
  69. for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
  70. changed |= read_cols_on_row(current_matrix, current_row);
  71. #ifdef ENCODER_ENABLE
  72. // Need to frequently read the encoder pins while scanning because the I/O expander takes a long time in comparison.
  73. encoder_driver_task();
  74. #endif
  75. }
  76. return changed;
  77. }