matrix.c (3775B)
1 /* 2 Copyright (c) 2022 David Kuehling <dvdkhlng TA posteo TOD de> 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "spi_master.h" 19 #include "matrix.h" 20 21 static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; 22 23 static void unselect_rows(void); 24 25 void matrix_init_custom(void) { 26 /* initialize row pins */ 27 for (uint8_t row = 0; row < MATRIX_ROWS; row++) { 28 gpio_set_pin_output(row_pins[row]); 29 } 30 unselect_rows(); 31 32 /* columns read via shift-register on SPI lines */ 33 34 /* Enable SPI, Master, set clock rate fck/16. First bit already at Qh 35 * output before clock edge (CPHA=0). SN74HC165 shift register shifts 36 * on low-to-high transition (CPOL=1). Receive the LSB first (DORD=1). 37 */ 38 bool lsbFirst = true; 39 uint8_t mode = 2; /* CPOL=1, CPHA=0 */ 40 uint16_t divisor = 16; 41 42 /* According to Atmega32U4 datasheet, PB0 *must* be set to output, 43 * otherwise it will interfere with SPI master operation. On pro-micro 44 * it's connected to a yellew LED. */ 45 pin_t slavePin = PB0; 46 spi_init(); 47 spi_start(slavePin, lsbFirst, mode, divisor); 48 49 /* Initialize pin controlling the shift register's SH/~LD pin */ 50 gpio_set_pin_output(ROW_SHIFT_PIN); 51 } 52 53 static void select_row(uint8_t row) { 54 pin_t pin = row_pins[row]; 55 if (pin != NO_PIN) { 56 gpio_write_pin_high(pin); 57 } 58 } 59 60 static void unselect_row(uint8_t row) { 61 pin_t pin = row_pins[row]; 62 if (pin != NO_PIN) { 63 gpio_write_pin_low(pin); 64 } 65 } 66 67 static void unselect_rows(void) { 68 for (uint8_t row = 0; row < MATRIX_ROWS; row++) { 69 unselect_row(row); 70 } 71 } 72 73 bool matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 74 /* Start with a clear matrix row */ 75 matrix_row_t current_row_value = 0; 76 77 /* Set shift register SH/~LD pin to "load" mode */ 78 gpio_write_pin_low(ROW_SHIFT_PIN); 79 select_row(current_row); 80 matrix_output_select_delay(); 81 82 /* Set shift register SH/~LD pin to "shift" mode */ 83 gpio_write_pin_high(ROW_SHIFT_PIN); 84 85 /* For each octet of columns... */ 86 for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index += 8) { 87 spi_status_t read_result = spi_read(); 88 if (read_result >= 0) { 89 /* only if SPI read successful: populate the matrix row with the 90 state of the 8 consecutive column bits */ 91 current_row_value |= ((matrix_row_t)read_result << col_index); 92 } 93 } 94 95 /* Unselect row & wait for all columns signals to go high. */ 96 unselect_row(current_row); 97 matrix_output_unselect_delay(current_row, current_row_value != 0); 98 99 /* Update row in matrix. */ 100 if (current_row_value != current_matrix[current_row]) { 101 current_matrix[current_row] = current_row_value; 102 return true; 103 } 104 105 return false; 106 } 107 108 bool matrix_scan_custom(matrix_row_t curr_matrix[]) { 109 bool changed = false; 110 111 /* set row, read cols */ 112 for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { 113 changed |= matrix_read_cols_on_row(curr_matrix, current_row); 114 } 115 116 return changed; 117 } 118 119 /* 120 * Local Variables: 121 * c-basic-offset:4 122 * fill-column: 76 123 * End: 124 */