aboutsummaryrefslogtreecommitdiff
path: root/keyboards/ferris/0_1/matrix.c
diff options
context:
space:
mode:
authorPierre Chevalier <pierrechevalier83@gmail.com>2020-10-17 20:20:34 +0100
committerGitHub <noreply@github.com>2020-10-17 12:20:34 -0700
commit704934c427af1cc2176fa7c82773e86d89cbfa3c (patch)
tree2699b29924e8496c1ee867a645a52a310ac9a532 /keyboards/ferris/0_1/matrix.c
parent47ea522e79f2b7bcc2804c309f2b94e029824eb9 (diff)
downloadqmk_firmware-704934c427af1cc2176fa7c82773e86d89cbfa3c.tar.gz
qmk_firmware-704934c427af1cc2176fa7c82773e86d89cbfa3c.zip
Ferris reorganization (#10564)
* Add my own keymap * Layer 0: Workman * Layer 1: RSTHD (my own take on it) * Layer 2: Mouse * Layer 3: Navigation * Layer 4: Symbols right * Layer 5: Symbols left * Layer 6: Fn keys * Layer 7: Numbers * Layer 8: Vim and misceallaneaous accessible from any other layer * Move the current code to a 0.1 folder in preparation for upcoming changes Version 0.2 is currently being prototyped and uses an arm chip which will need its own firmware. There is also the Ferris sweep which uses a pro-micro compatible board which will need its own directory. * Move Ferris out of handwired and into the light The keyboard is now well out of the prototype phase as tens of them have been produced and acquired by various people. With 0.2 coming out, adoption is going to increase again as the board will be available for sale at some recognized vendors. Now is probably a good time to recognize its status as more than a prototype :) * Add code for the Ferris Sweep (a.k.a cradio) The Ferris Sweep is a creation of the talented @davidphilipbarr, a.k.a DPB. It has the key layout of a Ferris and uses a pro-micro connected to the switches via direct pins so that diodes are not needed and the soldering is minimal. With their blessing, I took the code for it from DPB's own repo: https://github.com/davidphilipbarr/36keys/tree/master/qmk/cradio and did some adaptations such as removing commented out code, enabling EE_HANDS and mouse keys for consistency with my existing Ferris code. * Fix indentation in rules.mk * Fix indentation in sweep's config * Remove unnecessary lines from sweep's rules.mk * Remove unnecessary lines from sweep's rules.mk * Rename 0.1 to 0_1 This avoids the dot looking like the separation between a filename and its extension. In the same commit, bring matrix.c to the 0_1 folder as this was needed for the code to compile properly (matrix.c is referred to in the readme under `0_1` and is specific to that revision of the firmware). * Update copyright statements for Sweep
Diffstat (limited to 'keyboards/ferris/0_1/matrix.c')
-rw-r--r--keyboards/ferris/0_1/matrix.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/keyboards/ferris/0_1/matrix.c b/keyboards/ferris/0_1/matrix.c
new file mode 100644
index 000000000..e13c35d35
--- /dev/null
+++ b/keyboards/ferris/0_1/matrix.c
@@ -0,0 +1,282 @@
1/*
2Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com>
3 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 2 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/*
20 * This code was heavily inspired by the ergodox_ez keymap, and modernized
21 * to take advantage of the quantum.h microcontroller agnostics gpio control
22 * abstractions and use the macros defined in config.h for the wiring as opposed
23 * to repeating that information all over the place.
24 */
25
26#include QMK_KEYBOARD_H
27#include "i2c_master.h"
28
29extern i2c_status_t mcp23017_status;
30#define I2C_TIMEOUT 1000
31
32// For a better understanding of the i2c protocol, this is a good read:
33// https://www.robot-electronics.co.uk/i2c-tutorial
34
35// I2C address:
36// See the datasheet, section 3.3.1 on addressing I2C devices and figure 3-6 for an
37// illustration
38// http://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf
39// All address pins of the mcp23017 are connected to the ground on the ferris
40// | 0 | 1 | 0 | 0 | A2 | A1 | A0 |
41// | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
42#define I2C_ADDR 0b0100000
43#define I2C_ADDR_WRITE ((I2C_ADDR << 1) | I2C_WRITE)
44#define I2C_ADDR_READ ((I2C_ADDR << 1) | I2C_READ)
45
46// Register addresses
47// See https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library/blob/master/Adafruit_MCP23017.h
48#define IODIRA 0x00 // i/o direction register
49#define IODIRB 0x01
50#define GPPUA 0x0C // GPIO pull-up resistor register
51#define GPPUB 0x0D
52#define GPIOA 0x12 // general purpose i/o port register (write modifies OLAT)
53#define GPIOB 0x13
54#define OLATA 0x14 // output latch register
55#define OLATB 0x15
56
57bool i2c_initialized = 0;
58i2c_status_t mcp23017_status = I2C_ADDR;
59
60uint8_t init_mcp23017(void) {
61 print("starting init");
62 mcp23017_status = I2C_ADDR;
63
64 // I2C subsystem
65 if (i2c_initialized == 0) {
66 i2c_init(); // on pins D(1,0)
67 i2c_initialized = true;
68 wait_ms(I2C_TIMEOUT);
69 }
70
71 // set pin direction
72 // - unused : input : 1
73 // - input : input : 1
74 // - driving : output : 0
75 mcp23017_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT);
76 if (mcp23017_status) goto out;
77 mcp23017_status = i2c_write(IODIRA, I2C_TIMEOUT);
78 if (mcp23017_status) goto out;
79 // This means: we will read all the bits on GPIOA
80 mcp23017_status = i2c_write(0b11111111, I2C_TIMEOUT);
81 if (mcp23017_status) goto out;
82 // This means: we will write to the pins 0-4 on GPIOB (in select_rows)
83 mcp23017_status = i2c_write(0b11110000, I2C_TIMEOUT);
84 if (mcp23017_status) goto out;
85 i2c_stop();
86
87 // set pull-up
88 // - unused : on : 1
89 // - input : on : 1
90 // - driving : off : 0
91 mcp23017_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT);
92 if (mcp23017_status) goto out;
93 mcp23017_status = i2c_write(GPPUA, I2C_TIMEOUT);
94 if (mcp23017_status) goto out;
95 // This means: we will read all the bits on GPIOA
96 mcp23017_status = i2c_write(0b11111111, I2C_TIMEOUT);
97 if (mcp23017_status) goto out;
98 // This means: we will write to the pins 0-4 on GPIOB (in select_rows)
99 mcp23017_status = i2c_write(0b11110000, I2C_TIMEOUT);
100 if (mcp23017_status) goto out;
101
102out:
103 i2c_stop();
104 return mcp23017_status;
105}
106
107/* matrix state(1:on, 0:off) */
108static matrix_row_t matrix[MATRIX_ROWS]; // debounced values
109
110static matrix_row_t read_cols(uint8_t row);
111static void init_cols(void);
112static void unselect_rows(void);
113static void select_row(uint8_t row);
114
115static uint8_t mcp23017_reset_loop;
116
117void matrix_init_custom(void) {
118 // initialize row and col
119
120 mcp23017_status = init_mcp23017();
121
122 unselect_rows();
123 init_cols();
124
125 // initialize matrix state: all keys off
126 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
127 matrix[i] = 0;
128 }
129}
130
131void matrix_power_up(void) {
132 mcp23017_status = init_mcp23017();
133
134 unselect_rows();
135 init_cols();
136
137 // initialize matrix state: all keys off
138 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
139 matrix[i] = 0;
140 }
141}
142
143// Reads and stores a row, returning
144// whether a change occurred.
145static inline bool store_matrix_row(matrix_row_t current_matrix[], uint8_t index) {
146 matrix_row_t temp = read_cols(index);
147 if (current_matrix[index] != temp) {
148 current_matrix[index] = temp;
149 return true;
150 }
151 return false;
152}
153
154bool matrix_scan_custom(matrix_row_t current_matrix[]) {
155 if (mcp23017_status) { // if there was an error
156 if (++mcp23017_reset_loop == 0) {
157 // if (++mcp23017_reset_loop >= 1300) {
158 // since mcp23017_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans
159 // this will be approx bit more frequent than once per second
160 dprint("trying to reset mcp23017\n");
161 mcp23017_status = init_mcp23017();
162 if (mcp23017_status) {
163 dprint("right side not responding\n");
164 } else {
165 dprint("right side attached\n");
166 }
167 }
168 }
169
170 bool changed = false;
171 for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) {
172 // select rows from left and right hands
173 uint8_t left_index = i;
174 uint8_t right_index = i + MATRIX_ROWS_PER_SIDE;
175 select_row(left_index);
176 select_row(right_index);
177
178 // we don't need a 30us delay anymore, because selecting a
179 // left-hand row requires more than 30us for i2c.
180
181 changed |= store_matrix_row(current_matrix, left_index);
182 changed |= store_matrix_row(current_matrix, right_index);
183
184 unselect_rows();
185 }
186
187 return changed;
188}
189
190static void init_cols(void) {
191 // init on mcp23017
192 // not needed, already done as part of init_mcp23017()
193
194 // init on mcu
195 pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_MCU;
196 for (int pin_index = 0; pin_index < MATRIX_COLS_PER_SIDE; pin_index++) {
197 pin_t pin = matrix_col_pins_mcu[pin_index];
198 setPinInput(pin);
199 writePinHigh(pin);
200 }
201}
202
203static matrix_row_t read_cols(uint8_t row) {
204 if (row < MATRIX_ROWS_PER_SIDE) {
205 pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_MCU;
206 matrix_row_t current_row_value = 0;
207 // For each col...
208 for (uint8_t col_index = 0; col_index < MATRIX_COLS_PER_SIDE; col_index++) {
209 // Select the col pin to read (active low)
210 uint8_t pin_state = readPin(matrix_col_pins_mcu[col_index]);
211
212 // Populate the matrix row with the state of the col pin
213 current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
214 }
215 return current_row_value;
216 } else {
217 if (mcp23017_status) { // if there was an error
218 return 0;
219 } else {
220 uint8_t data = 0;
221 mcp23017_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT);
222 if (mcp23017_status) goto out;
223 mcp23017_status = i2c_write(GPIOA, I2C_TIMEOUT);
224 if (mcp23017_status) goto out;
225 mcp23017_status = i2c_start(I2C_ADDR_READ, I2C_TIMEOUT);
226 if (mcp23017_status) goto out;
227 mcp23017_status = i2c_read_nack(I2C_TIMEOUT);
228 if (mcp23017_status < 0) goto out;
229 // We read all the pins on GPIOA.
230 // 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.
231 // 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.
232 // Since the pins connected to eact columns are sequential, and counting from zero up (col 5 -> GPIOA0, col 6 -> GPIOA1 and so on), the only transformation needed is a bitwise not to swap all zeroes and ones.
233 data = ~((uint8_t)mcp23017_status);
234 mcp23017_status = I2C_STATUS_SUCCESS;
235 out:
236 i2c_stop();
237 // return reverse_bits(data, MATRIX_COLS_PER_SIDE);
238 return data;
239 }
240 }
241}
242
243static void unselect_rows(void) {
244 // no need to unselect on mcp23017, because the select step sets all
245 // the other row bits high, and it's not changing to a different
246 // direction
247
248 // unselect rows on microcontroller
249 pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_MCU;
250 for (int pin_index = 0; pin_index < MATRIX_ROWS_PER_SIDE; pin_index++) {
251 pin_t pin = matrix_row_pins_mcu[pin_index];
252 setPinInput(pin);
253 writePinLow(pin);
254 }
255}
256
257static void select_row(uint8_t row) {
258 if (row < MATRIX_ROWS_PER_SIDE) {
259 // select on atmega32u4
260 pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_MCU;
261 pin_t pin = matrix_row_pins_mcu[row];
262 setPinOutput(pin);
263 writePinLow(pin);
264 } else {
265 // select on mcp23017
266 if (mcp23017_status) { // if there was an error
267 // do nothing
268 } else {
269 mcp23017_status = i2c_start(I2C_ADDR_WRITE, I2C_TIMEOUT);
270 if (mcp23017_status) goto out;
271 mcp23017_status = i2c_write(GPIOB, I2C_TIMEOUT);
272 if (mcp23017_status) goto out;
273 // 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.
274 // 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.
275 mcp23017_status = i2c_write(0xFF & ~(1 << (row - MATRIX_ROWS_PER_SIDE)), I2C_TIMEOUT);
276
277 if (mcp23017_status) goto out;
278 out:
279 i2c_stop();
280 }
281 }
282}