diff options
Diffstat (limited to 'keyboards/3w6/rev1/matrix.c')
-rw-r--r-- | keyboards/3w6/rev1/matrix.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/keyboards/3w6/rev1/matrix.c b/keyboards/3w6/rev1/matrix.c new file mode 100644 index 000000000..ae2f96bfa --- /dev/null +++ b/keyboards/3w6/rev1/matrix.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | Copyright 2013 Oleg Kostyuk <[email protected]> | ||
3 | 2020 Pierre Chevalier <[email protected]> | ||
4 | 2021 weteor | ||
5 | |||
6 | This program is free software: you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation, either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * This code was heavily inspired by the ergodox_ez keymap, and modernized | ||
22 | * to take advantage of the quantum.h microcontroller agnostics gpio control | ||
23 | * abstractions and use the macros defined in config.h for the wiring as opposed | ||
24 | * to repeating that information all over the place. | ||
25 | */ | ||
26 | |||
27 | #include QMK_KEYBOARD_H | ||
28 | #include "i2c_master.h" | ||
29 | |||
30 | extern i2c_status_t tca9555_status; | ||
31 | #define I2C_TIMEOUT 1000 | ||
32 | |||
33 | // I2C address: | ||
34 | // All address pins of the tca9555 are connected to the ground | ||
35 | // | 0 | 1 | 0 | 0 | A2 | A1 | A0 | | ||
36 | // | 0 | 1 | 0 | 0 | 0 | 0 | 0 | | ||
37 | #define I2C_ADDR (0b0100000 << 1) | ||
38 | |||
39 | // Register addresses | ||
40 | #define IODIRA 0x06 // i/o direction register | ||
41 | #define IODIRB 0x07 | ||
42 | #define IREGP0 0x00 // GPIO pull-up resistor register | ||
43 | #define IREGP1 0x01 | ||
44 | #define OREGP0 0x02 // general purpose i/o port register (write modifies OLAT) | ||
45 | #define OREGP1 0x03 | ||
46 | |||
47 | bool i2c_initialized = 0; | ||
48 | i2c_status_t tca9555_status = I2C_ADDR; | ||
49 | |||
50 | uint8_t init_tca9555(void) { | ||
51 | print("starting init"); | ||
52 | tca9555_status = I2C_ADDR; | ||
53 | |||
54 | // I2C subsystem | ||
55 | if (i2c_initialized == 0) { | ||
56 | i2c_init(); // on pins D(1,0) | ||
57 | i2c_initialized = true; | ||
58 | wait_ms(I2C_TIMEOUT); | ||
59 | } | ||
60 | |||
61 | // set pin direction | ||
62 | // - unused : input : 1 | ||
63 | // - input : input : 1 | ||
64 | // - driving : output : 0 | ||
65 | uint8_t conf[2] = { | ||
66 | // This means: write on pin 5 of port 0, read on rest | ||
67 | 0b11011111, | ||
68 | // This means: we will write on pins 0 to 2 on port 1. read rest | ||
69 | 0b11111000, | ||
70 | }; | ||
71 | tca9555_status = i2c_writeReg(I2C_ADDR, IODIRA, conf, 2, I2C_TIMEOUT); | ||
72 | |||
73 | return tca9555_status; | ||
74 | } | ||
75 | |||
76 | /* matrix state(1:on, 0:off) */ | ||
77 | static matrix_row_t matrix[MATRIX_ROWS]; // debounced values | ||
78 | |||
79 | static matrix_row_t read_cols(uint8_t row); | ||
80 | static void init_cols(void); | ||
81 | static void unselect_rows(void); | ||
82 | static void select_row(uint8_t row); | ||
83 | |||
84 | static uint8_t tca9555_reset_loop; | ||
85 | |||
86 | void matrix_init_custom(void) { | ||
87 | // initialize row and col | ||
88 | |||
89 | tca9555_status = init_tca9555(); | ||
90 | |||
91 | unselect_rows(); | ||
92 | init_cols(); | ||
93 | |||
94 | // initialize matrix state: all keys off | ||
95 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
96 | matrix[i] = 0; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | void matrix_power_up(void) { | ||
101 | tca9555_status = init_tca9555(); | ||
102 | |||
103 | unselect_rows(); | ||
104 | init_cols(); | ||
105 | |||
106 | // initialize matrix state: all keys off | ||
107 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
108 | matrix[i] = 0; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | // Reads and stores a row, returning | ||
113 | // whether a change occurred. | ||
114 | static inline bool store_matrix_row(matrix_row_t current_matrix[], uint8_t index) { | ||
115 | matrix_row_t temp = read_cols(index); | ||
116 | if (current_matrix[index] != temp) { | ||
117 | current_matrix[index] = temp; | ||
118 | return true; | ||
119 | } | ||
120 | return false; | ||
121 | } | ||
122 | |||
123 | bool matrix_scan_custom(matrix_row_t current_matrix[]) { | ||
124 | if (tca9555_status) { // if there was an error | ||
125 | if (++tca9555_reset_loop == 0) { | ||
126 | // since tca9555_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans | ||
127 | // this will be approx bit more frequent than once per second | ||
128 | dprint("trying to reset tca9555\n"); | ||
129 | tca9555_status = init_tca9555(); | ||
130 | if (tca9555_status) { | ||
131 | dprint("right side not responding\n"); | ||
132 | } else { | ||
133 | dprint("right side attached\n"); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | bool changed = false; | ||
139 | for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) { | ||
140 | // select rows from left and right hands | ||
141 | uint8_t left_index = i; | ||
142 | uint8_t right_index = i + MATRIX_ROWS_PER_SIDE; | ||
143 | select_row(left_index); | ||
144 | select_row(right_index); | ||
145 | |||
146 | // we don't need a 30us delay anymore, because selecting a | ||
147 | // left-hand row requires more than 30us for i2c. | ||
148 | |||
149 | changed |= store_matrix_row(current_matrix, left_index); | ||
150 | changed |= store_matrix_row(current_matrix, right_index); | ||
151 | |||
152 | unselect_rows(); | ||
153 | } | ||
154 | |||
155 | return changed; | ||
156 | } | ||
157 | |||
158 | static void init_cols(void) { | ||
159 | // init on tca9555 | ||
160 | // not needed, already done as part of init_tca9555() | ||
161 | |||
162 | // init on mcu | ||
163 | pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; | ||
164 | for (int pin_index = 0; pin_index < MATRIX_COLS_PER_SIDE; pin_index++) { | ||
165 | pin_t pin = matrix_col_pins_mcu[pin_index]; | ||
166 | setPinInput(pin); | ||
167 | writePinHigh(pin); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static matrix_row_t read_cols(uint8_t row) { | ||
172 | if (row < MATRIX_ROWS_PER_SIDE) { | ||
173 | pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; | ||
174 | matrix_row_t current_row_value = 0; | ||
175 | // For each col... | ||
176 | for (uint8_t col_index = 0; col_index < MATRIX_COLS_PER_SIDE; col_index++) { | ||
177 | // Select the col pin to read (active low) | ||
178 | uint8_t pin_state = readPin(matrix_col_pins_mcu[col_index]); | ||
179 | |||
180 | // Populate the matrix row with the state of the col pin | ||
181 | current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); | ||
182 | } | ||
183 | return current_row_value; | ||
184 | } else { | ||
185 | if (tca9555_status) { // if there was an error | ||
186 | return 0; | ||
187 | } else { | ||
188 | uint8_t data = 0; | ||
189 | uint8_t ports[2] = {0}; | ||
190 | tca9555_status = i2c_readReg(I2C_ADDR, IREGP0, ports, 2, I2C_TIMEOUT); | ||
191 | if (tca9555_status) { // if there was an error | ||
192 | // do nothing | ||
193 | return 0; | ||
194 | } else { | ||
195 | uint8_t port0 = ports[0]; | ||
196 | uint8_t port1 = ports[1]; | ||
197 | |||
198 | // The initial state was all ones and any depressed key at a given column for the currently selected row will have its bit flipped to zero. | ||
199 | // The return value is a row as represented in the generic matrix code were the rightmost bits represent the lower columns and zeroes represent non-depressed keys while ones represent depressed keys. | ||
200 | // Since the pins are not ordered sequentially, we have to build the correct dataset from the two ports. Refer to the schematic to see where every pin is connected. | ||
201 | data |= ( port0 & 0x01 ); | ||
202 | data |= ( port0 & 0x02 ); | ||
203 | data |= ( port1 & 0x10 ) >> 2; | ||
204 | data |= ( port1 & 0x08 ); | ||
205 | data |= ( port0 & 0x40 ) >> 2; | ||
206 | data = ~(data); | ||
207 | |||
208 | tca9555_status = I2C_STATUS_SUCCESS; | ||
209 | return data; | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | static void unselect_rows(void) { | ||
216 | // no need to unselect on tca9555, because the select step sets all | ||
217 | // the other row bits high, and it's not changing to a different | ||
218 | // direction | ||
219 | |||
220 | // unselect rows on microcontroller | ||
221 | pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; | ||
222 | for (int pin_index = 0; pin_index < MATRIX_ROWS_PER_SIDE; pin_index++) { | ||
223 | pin_t pin = matrix_row_pins_mcu[pin_index]; | ||
224 | setPinInput(pin); | ||
225 | writePinLow(pin); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | static void select_row(uint8_t row) { | ||
230 | uint8_t port0 = 0xff; | ||
231 | uint8_t port1 = 0xff; | ||
232 | |||
233 | if (row < MATRIX_ROWS_PER_SIDE) { | ||
234 | // select on atmega32u4 | ||
235 | pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; | ||
236 | pin_t pin = matrix_row_pins_mcu[row]; | ||
237 | setPinOutput(pin); | ||
238 | writePinLow(pin); | ||
239 | } else { | ||
240 | // select on tca9555 | ||
241 | if (tca9555_status) { // if there was an error | ||
242 | // do nothing | ||
243 | } else { | ||
244 | switch(row) { | ||
245 | case 4: port1 &= ~(1 << 0); break; | ||
246 | case 5: port1 &= ~(1 << 1); break; | ||
247 | case 6: port1 &= ~(1 << 2); break; | ||
248 | case 7: port0 &= ~(1 << 5); break; | ||
249 | default: break; | ||
250 | } | ||
251 | |||
252 | uint8_t ports[2] = {port0, port1}; | ||
253 | tca9555_status = i2c_writeReg(I2C_ADDR, OREGP0, ports, 2, I2C_TIMEOUT); | ||
254 | // Select the desired row by writing a byte for the entire GPIOB bus where only the bit representing the row we want to select is a zero (write instruction) and every other bit is a one. | ||
255 | // Note that the row - MATRIX_ROWS_PER_SIDE reflects the fact that being on the right hand, the columns are numbered from MATRIX_ROWS_PER_SIDE to MATRIX_ROWS, but the pins we want to write to are indexed from zero up on the GPIOB bus. | ||
256 | } | ||
257 | } | ||
258 | } | ||