235 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // Copyright 2022 QMK
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| #include "gpio.h"
 | |
| #include "matrix.h"
 | |
| #include "mcp23018.h"
 | |
| #include "util.h"
 | |
| #include "wait.h"
 | |
| #include "debug.h"
 | |
| 
 | |
| #define I2C_ADDR 0x20
 | |
| 
 | |
| static uint8_t mcp23018_errors = 0;
 | |
| 
 | |
| static void expander_init(void) {
 | |
|     mcp23018_init(I2C_ADDR);
 | |
| }
 | |
| 
 | |
| static void expander_init_cols(void) {
 | |
|     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, ALL_INPUT);
 | |
|     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT);
 | |
| }
 | |
| 
 | |
| static void expander_select_row(uint8_t row) {
 | |
|     if (mcp23018_errors) {
 | |
|         // wait to mimic i2c interactions
 | |
|         wait_us(100);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ~(1 << (row + 1)));
 | |
| }
 | |
| 
 | |
| static void expander_unselect_row(uint8_t row) {
 | |
|     // No need to unselect row as the next `select_row` will blank everything anyway
 | |
| }
 | |
| 
 | |
| static void expander_unselect_rows(void) {
 | |
|     if (mcp23018_errors) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT);
 | |
| }
 | |
| 
 | |
| static matrix_row_t expander_read_row(void) {
 | |
|     if (mcp23018_errors) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     uint8_t ret = 0xFF;
 | |
|     mcp23018_errors += !mcp23018_readPins(I2C_ADDR, mcp23018_PORTA, &ret);
 | |
| 
 | |
|     ret = bitrev(~ret);
 | |
|     ret = ((ret & 0b11111000) >> 1) | (ret & 0b00000011);
 | |
| 
 | |
|     return ((uint16_t)ret) << 7;
 | |
| }
 | |
| 
 | |
| static void expander_scan(void) {
 | |
|     if (!mcp23018_errors) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     static uint16_t mcp23018_reset_loop = 0;
 | |
|     if (++mcp23018_reset_loop > 0x1FFF) {
 | |
|         // tuned to about 5s given the current scan rate
 | |
|         dprintf("trying to reset mcp23018\n");
 | |
|         mcp23018_reset_loop = 0;
 | |
|         mcp23018_errors     = 0;
 | |
|         expander_unselect_rows();
 | |
|         expander_init_cols();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Column pin configuration
 | |
|  *
 | |
|  * Pro Micro: 6    5    4    3    2    1    0
 | |
|  *            PD3  PD2  PD4  PC6  PD7  PE6  PB4
 | |
|  *
 | |
|  * Expander:  13   12   11   10   9    8    7
 | |
|  */
 | |
| static void init_cols(void) {
 | |
|     // Pro Micro
 | |
|     setPinInputHigh(E6);
 | |
|     setPinInputHigh(D2);
 | |
|     setPinInputHigh(D3);
 | |
|     setPinInputHigh(D4);
 | |
|     setPinInputHigh(D7);
 | |
|     setPinInputHigh(C6);
 | |
|     setPinInputHigh(B4);
 | |
| 
 | |
|     // Expander
 | |
|     expander_init_cols();
 | |
| }
 | |
| 
 | |
| static matrix_row_t read_cols(void) {
 | |
|     // clang-format off
 | |
|     return expander_read_row() |
 | |
|         (readPin(D3) ? 0 : (1<<6)) |
 | |
|         (readPin(D2) ? 0 : (1<<5)) |
 | |
|         (readPin(D4) ? 0 : (1<<4)) |
 | |
|         (readPin(C6) ? 0 : (1<<3)) |
 | |
|         (readPin(D7) ? 0 : (1<<2)) |
 | |
|         (readPin(E6) ? 0 : (1<<1)) |
 | |
|         (readPin(B4) ? 0 : (1<<0)) ;
 | |
|     // clang-format on
 | |
| }
 | |
| 
 | |
| /* Row pin configuration
 | |
|  *
 | |
|  * Pro Micro: 0   1   2   3   4   5
 | |
|  *            F4  F5  F6  F7  B1  B2
 | |
|  *
 | |
|  * Expander:  0   1   2   3   4   5
 | |
|  */
 | |
| static void unselect_rows(void) {
 | |
|     // Pro Micro
 | |
|     setPinInput(B1);
 | |
|     setPinInput(B2);
 | |
|     setPinInput(F4);
 | |
|     setPinInput(F5);
 | |
|     setPinInput(F6);
 | |
|     setPinInput(F7);
 | |
|     writePinLow(B1);
 | |
|     writePinLow(B2);
 | |
|     writePinLow(F4);
 | |
|     writePinLow(F5);
 | |
|     writePinLow(F6);
 | |
|     writePinLow(F7);
 | |
| 
 | |
|     // Expander
 | |
|     expander_unselect_rows();
 | |
| }
 | |
| 
 | |
| static void unselect_row(uint8_t row) {
 | |
|     // Pro Micro
 | |
|     switch (row) {
 | |
|         case 0:
 | |
|             setPinInput(F4);
 | |
|             writePinLow(F4);
 | |
|             break;
 | |
|         case 1:
 | |
|             setPinInput(F5);
 | |
|             writePinLow(F5);
 | |
|             break;
 | |
|         case 2:
 | |
|             setPinInput(F6);
 | |
|             writePinLow(F6);
 | |
|             break;
 | |
|         case 3:
 | |
|             setPinInput(F7);
 | |
|             writePinLow(F7);
 | |
|             break;
 | |
|         case 4:
 | |
|             setPinInput(B1);
 | |
|             writePinLow(B1);
 | |
|             break;
 | |
|         case 5:
 | |
|             setPinInput(B2);
 | |
|             writePinLow(B2);
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     // Expander
 | |
|     expander_unselect_row(row);
 | |
| }
 | |
| 
 | |
| static void select_row(uint8_t row) {
 | |
|     // Pro Micro
 | |
|     switch (row) {
 | |
|         case 0:
 | |
|             setPinOutput(F4);
 | |
|             writePinLow(F4);
 | |
|             break;
 | |
|         case 1:
 | |
|             setPinOutput(F5);
 | |
|             writePinLow(F5);
 | |
|             break;
 | |
|         case 2:
 | |
|             setPinOutput(F6);
 | |
|             writePinLow(F6);
 | |
|             break;
 | |
|         case 3:
 | |
|             setPinOutput(F7);
 | |
|             writePinLow(F7);
 | |
|             break;
 | |
|         case 4:
 | |
|             setPinOutput(B1);
 | |
|             writePinLow(B1);
 | |
|             break;
 | |
|         case 5:
 | |
|             setPinOutput(B2);
 | |
|             writePinLow(B2);
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     // Expander
 | |
|     expander_select_row(row);
 | |
| }
 | |
| 
 | |
| static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
 | |
|     // Store last value of row prior to reading
 | |
|     matrix_row_t last_row_value = current_matrix[current_row];
 | |
| 
 | |
|     // Clear data in matrix row
 | |
|     current_matrix[current_row] = 0;
 | |
| 
 | |
|     // Select row and wait for row selection to stabilize
 | |
|     select_row(current_row);
 | |
|     // Skip the wait_us(30); as i2c is slow enough to debounce the io changes
 | |
| 
 | |
|     current_matrix[current_row] = read_cols();
 | |
| 
 | |
|     unselect_row(current_row);
 | |
| 
 | |
|     return (last_row_value != current_matrix[current_row]);
 | |
| }
 | |
| 
 | |
| void matrix_init_custom(void) {
 | |
|     expander_init();
 | |
| 
 | |
|     unselect_rows();
 | |
|     init_cols();
 | |
| }
 | |
| 
 | |
| bool matrix_scan_custom(matrix_row_t current_matrix[]) {
 | |
|     expander_scan();
 | |
| 
 | |
|     bool changed = false;
 | |
|     for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
 | |
|         changed |= read_cols_on_row(current_matrix, current_row);
 | |
|     }
 | |
|     return changed;
 | |
| }
 | 
