aboutsummaryrefslogtreecommitdiff
path: root/keyboards/3w6/rev1/matrix.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/3w6/rev1/matrix.c')
-rw-r--r--keyboards/3w6/rev1/matrix.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/keyboards/3w6/rev1/matrix.c b/keyboards/3w6/rev1/matrix.c
new file mode 100644
index 000000000..7262fd22e
--- /dev/null
+++ b/keyboards/3w6/rev1/matrix.c
@@ -0,0 +1,280 @@
1/*
2Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com>
3 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
4 2021 weteor
5
6This program is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along 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
30extern 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
49bool i2c_initialized = 0;
50i2c_status_t tca9555_status = I2C_ADDR;
51
52uint8_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: write on pin 5 of port 0, read on rest
72 tca9555_status = i2c_write(0b11011111, I2C_TIMEOUT);
73 if (tca9555_status) goto out;
74 // This means: we will write on pins 0 to 2 on port 1. read rest
75 tca9555_status = i2c_write(0b11111000, I2C_TIMEOUT);
76 if (tca9555_status) goto out;
77
78out:
79 i2c_stop();
80 return tca9555_status;
81}
82
83/* matrix state(1:on, 0:off) */
84static matrix_row_t matrix[MATRIX_ROWS]; // debounced values
85
86static matrix_row_t read_cols(uint8_t row);
87static void init_cols(void);
88static void unselect_rows(void);
89static void select_row(uint8_t row);
90
91static uint8_t tca9555_reset_loop;
92
93void 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
107void 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.
121static 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
130bool 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
165static 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
178static 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 uint8_t port1 = 0;
198 tca9555_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT);
199 if (tca9555_status) goto out;
200 tca9555_status = i2c_write(IREGP0, I2C_TIMEOUT);
201 if (tca9555_status) goto out;
202 tca9555_status = i2c_start(I2C_ADDR_READ, I2C_TIMEOUT);
203 if (tca9555_status) goto out;
204 tca9555_status = i2c_read_ack(I2C_TIMEOUT);
205 if (tca9555_status < 0) goto out;
206 port0 = (uint8_t)tca9555_status;
207 tca9555_status = i2c_read_nack(I2C_TIMEOUT);
208 if (tca9555_status < 0) goto out;
209 port1 = (uint8_t)tca9555_status;
210
211 // 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.
212 // 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.
213 // 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.
214 data |= ( port0 & 0x01 );
215 data |= ( port0 & 0x02 );
216 data |= ( port1 & 0x10 ) >> 2;
217 data |= ( port1 & 0x08 );
218 data |= ( port0 & 0x40 ) >> 2;
219 data = ~(data);
220
221 tca9555_status = I2C_STATUS_SUCCESS;
222 out:
223 i2c_stop();
224 return data;
225 }
226 }
227}
228
229static void unselect_rows(void) {
230 // no need to unselect on tca9555, because the select step sets all
231 // the other row bits high, and it's not changing to a different
232 // direction
233
234 // unselect rows on microcontroller
235 pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L;
236 for (int pin_index = 0; pin_index < MATRIX_ROWS_PER_SIDE; pin_index++) {
237 pin_t pin = matrix_row_pins_mcu[pin_index];
238 setPinInput(pin);
239 writePinLow(pin);
240 }
241}
242
243static void select_row(uint8_t row) {
244 uint8_t port0 = 0xff;
245 uint8_t port1 = 0xff;
246
247 if (row < MATRIX_ROWS_PER_SIDE) {
248 // select on atmega32u4
249 pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L;
250 pin_t pin = matrix_row_pins_mcu[row];
251 setPinOutput(pin);
252 writePinLow(pin);
253 } else {
254 // select on tca9555
255 if (tca9555_status) { // if there was an error
256 // do nothing
257 } else {
258 switch(row) {
259 case 4: port1 &= ~(1 << 0); break;
260 case 5: port1 &= ~(1 << 1); break;
261 case 6: port1 &= ~(1 << 2); break;
262 case 7: port0 &= ~(1 << 5); break;
263 default: break;
264 }
265
266 tca9555_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT);
267 if (tca9555_status) goto out;
268 tca9555_status = i2c_write(OREGP0, I2C_TIMEOUT);
269 if (tca9555_status) goto out;
270 tca9555_status = i2c_write(port0, I2C_TIMEOUT);
271 if (tca9555_status) goto out;
272 tca9555_status = i2c_write(port1, I2C_TIMEOUT);
273 if (tca9555_status) goto out;
274 // 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.
275 // 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.
276 out:
277 i2c_stop();
278 }
279 }
280}