diff options
Diffstat (limited to 'keyboards/3w6/rev2/matrix.c')
-rw-r--r-- | keyboards/3w6/rev2/matrix.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/keyboards/3w6/rev2/matrix.c b/keyboards/3w6/rev2/matrix.c new file mode 100644 index 000000000..5bc967bed --- /dev/null +++ b/keyboards/3w6/rev2/matrix.c | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com> | ||
3 | 2020 Pierre Chevalier <pierrechevalier83@gmail.com> | ||
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 | ||
38 | #define I2C_ADDR_WRITE ((I2C_ADDR << 1) | I2C_WRITE) | ||
39 | #define I2C_ADDR_READ ((I2C_ADDR << 1) | I2C_READ) | ||
40 | |||
41 | // Register addresses | ||
42 | #define IODIRA 0x06 // i/o direction register | ||
43 | #define IODIRB 0x07 | ||
44 | #define IREGP0 0x00 // GPIO pull-up resistor register | ||
45 | #define IREGP1 0x01 | ||
46 | #define OREGP0 0x02 // general purpose i/o port register (write modifies OLAT) | ||
47 | #define OREGP1 0x03 | ||
48 | |||
49 | bool i2c_initialized = 0; | ||
50 | i2c_status_t tca9555_status = I2C_ADDR; | ||
51 | |||
52 | uint8_t init_tca9555(void) { | ||
53 | print("starting init"); | ||
54 | tca9555_status = I2C_ADDR; | ||
55 | |||
56 | // I2C subsystem | ||
57 | if (i2c_initialized == 0) { | ||
58 | i2c_init(); // on pins D(1,0) | ||
59 | i2c_initialized = true; | ||
60 | wait_ms(I2C_TIMEOUT); | ||
61 | } | ||
62 | |||
63 | // set pin direction | ||
64 | // - unused : input : 1 | ||
65 | // - input : input : 1 | ||
66 | // - driving : output : 0 | ||
67 | tca9555_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT); | ||
68 | if (tca9555_status) goto out; | ||
69 | tca9555_status = i2c_write(IODIRA, I2C_TIMEOUT); | ||
70 | if (tca9555_status) goto out; | ||
71 | // This means: read all pins of port 0 | ||
72 | tca9555_status = i2c_write(0b11111111, I2C_TIMEOUT); | ||
73 | if (tca9555_status) goto out; | ||
74 | // This means: we will write on pins 0 to 3 on port 1. read rest | ||
75 | tca9555_status = i2c_write(0b11110000, I2C_TIMEOUT); | ||
76 | if (tca9555_status) goto out; | ||
77 | |||
78 | out: | ||
79 | i2c_stop(); | ||
80 | return tca9555_status; | ||
81 | } | ||
82 | |||
83 | /* matrix state(1:on, 0:off) */ | ||
84 | static matrix_row_t matrix[MATRIX_ROWS]; // debounced values | ||
85 | |||
86 | static matrix_row_t read_cols(uint8_t row); | ||
87 | static void init_cols(void); | ||
88 | static void unselect_rows(void); | ||
89 | static void select_row(uint8_t row); | ||
90 | |||
91 | static uint8_t tca9555_reset_loop; | ||
92 | |||
93 | void matrix_init_custom(void) { | ||
94 | // initialize row and col | ||
95 | |||
96 | tca9555_status = init_tca9555(); | ||
97 | |||
98 | unselect_rows(); | ||
99 | init_cols(); | ||
100 | |||
101 | // initialize matrix state: all keys off | ||
102 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
103 | matrix[i] = 0; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void matrix_power_up(void) { | ||
108 | tca9555_status = init_tca9555(); | ||
109 | |||
110 | unselect_rows(); | ||
111 | init_cols(); | ||
112 | |||
113 | // initialize matrix state: all keys off | ||
114 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
115 | matrix[i] = 0; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | // Reads and stores a row, returning | ||
120 | // whether a change occurred. | ||
121 | static inline bool store_matrix_row(matrix_row_t current_matrix[], uint8_t index) { | ||
122 | matrix_row_t temp = read_cols(index); | ||
123 | if (current_matrix[index] != temp) { | ||
124 | current_matrix[index] = temp; | ||
125 | return true; | ||
126 | } | ||
127 | return false; | ||
128 | } | ||
129 | |||
130 | bool matrix_scan_custom(matrix_row_t current_matrix[]) { | ||
131 | if (tca9555_status) { // if there was an error | ||
132 | if (++tca9555_reset_loop == 0) { | ||
133 | // since tca9555_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans | ||
134 | // this will be approx bit more frequent than once per second | ||
135 | dprint("trying to reset tca9555\n"); | ||
136 | tca9555_status = init_tca9555(); | ||
137 | if (tca9555_status) { | ||
138 | dprint("right side not responding\n"); | ||
139 | } else { | ||
140 | dprint("right side attached\n"); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | bool changed = false; | ||
146 | for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) { | ||
147 | // select rows from left and right hands | ||
148 | uint8_t left_index = i; | ||
149 | uint8_t right_index = i + MATRIX_ROWS_PER_SIDE; | ||
150 | select_row(left_index); | ||
151 | select_row(right_index); | ||
152 | |||
153 | // we don't need a 30us delay anymore, because selecting a | ||
154 | // left-hand row requires more than 30us for i2c. | ||
155 | |||
156 | changed |= store_matrix_row(current_matrix, left_index); | ||
157 | changed |= store_matrix_row(current_matrix, right_index); | ||
158 | |||
159 | unselect_rows(); | ||
160 | } | ||
161 | |||
162 | return changed; | ||
163 | } | ||
164 | |||
165 | static void init_cols(void) { | ||
166 | // init on tca9555 | ||
167 | // not needed, already done as part of init_tca9555() | ||
168 | |||
169 | // init on mcu | ||
170 | pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; | ||
171 | for (int pin_index = 0; pin_index < MATRIX_COLS_PER_SIDE; pin_index++) { | ||
172 | pin_t pin = matrix_col_pins_mcu[pin_index]; | ||
173 | setPinInput(pin); | ||
174 | writePinHigh(pin); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static matrix_row_t read_cols(uint8_t row) { | ||
179 | if (row < MATRIX_ROWS_PER_SIDE) { | ||
180 | pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; | ||
181 | matrix_row_t current_row_value = 0; | ||
182 | // For each col... | ||
183 | for (uint8_t col_index = 0; col_index < MATRIX_COLS_PER_SIDE; col_index++) { | ||
184 | // Select the col pin to read (active low) | ||
185 | uint8_t pin_state = readPin(matrix_col_pins_mcu[col_index]); | ||
186 | |||
187 | // Populate the matrix row with the state of the col pin | ||
188 | current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); | ||
189 | } | ||
190 | return current_row_value; | ||
191 | } else { | ||
192 | if (tca9555_status) { // if there was an error | ||
193 | return 0; | ||
194 | } else { | ||
195 | uint8_t data = 0; | ||
196 | uint8_t port0 = 0; | ||
197 | tca9555_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT); | ||
198 | if (tca9555_status) goto out; | ||
199 | tca9555_status = i2c_write(IREGP0, I2C_TIMEOUT); | ||
200 | if (tca9555_status) goto out; | ||
201 | tca9555_status = i2c_start(I2C_ADDR_READ, I2C_TIMEOUT); | ||
202 | if (tca9555_status) goto out; | ||
203 | tca9555_status = i2c_read_nack(I2C_TIMEOUT); | ||
204 | if (tca9555_status < 0) goto out; | ||
205 | |||
206 | port0 = ~(uint8_t)tca9555_status; | ||
207 | |||
208 | // We read all the pins on GPIOA. | ||
209 | // 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. | ||
210 | // 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. | ||
211 | // the pins connected to eact columns are sequential, but in reverse order, and counting from zero down (col 5 -> GPIO04, col6 -> GPIO03 and so on). | ||
212 | data |= ( port0 & 0x01 ) << 4; | ||
213 | data |= ( port0 & 0x02 ) << 2; | ||
214 | data |= ( port0 & 0x04 ); | ||
215 | data |= ( port0 & 0x08 ) >> 2; | ||
216 | data |= ( port0 & 0x10 ) >> 4; | ||
217 | |||
218 | tca9555_status = I2C_STATUS_SUCCESS; | ||
219 | out: | ||
220 | i2c_stop(); | ||
221 | |||
222 | return data; | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | static void unselect_rows(void) { | ||
228 | // no need to unselect on tca9555, because the select step sets all | ||
229 | // the other row bits high, and it's not changing to a different | ||
230 | // direction | ||
231 | |||
232 | // unselect rows on microcontroller | ||
233 | pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; | ||
234 | for (int pin_index = 0; pin_index < MATRIX_ROWS_PER_SIDE; pin_index++) { | ||
235 | pin_t pin = matrix_row_pins_mcu[pin_index]; | ||
236 | setPinInput(pin); | ||
237 | writePinLow(pin); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | static void select_row(uint8_t row) { | ||
242 | uint8_t port1 = 0xff; | ||
243 | |||
244 | if (row < MATRIX_ROWS_PER_SIDE) { | ||
245 | // select on atmega32u4 | ||
246 | pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; | ||
247 | pin_t pin = matrix_row_pins_mcu[row]; | ||
248 | setPinOutput(pin); | ||
249 | writePinLow(pin); | ||
250 | } else { | ||
251 | // select on tca9555 | ||
252 | if (tca9555_status) { // if there was an error | ||
253 | // do nothing | ||
254 | } else { | ||
255 | switch(row) { | ||
256 | case 4: port1 &= ~(1 << 0); break; | ||
257 | case 5: port1 &= ~(1 << 1); break; | ||
258 | case 6: port1 &= ~(1 << 2); break; | ||
259 | case 7: port1 &= ~(1 << 3); break; | ||
260 | default: break; | ||
261 | } | ||
262 | |||
263 | // 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. | ||
264 | // 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. | ||
265 | tca9555_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT); | ||
266 | if (tca9555_status) goto out; | ||
267 | tca9555_status = i2c_write(OREGP1, I2C_TIMEOUT); | ||
268 | if (tca9555_status) goto out; | ||
269 | tca9555_status = i2c_write(port1, I2C_TIMEOUT); | ||
270 | if (tca9555_status) goto out; | ||
271 | out: | ||
272 | i2c_stop(); | ||
273 | } | ||
274 | } | ||
275 | } | ||