aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Chevalier <pierrechevalier83@gmail.com>2020-07-18 04:57:33 +0100
committerGitHub <noreply@github.com>2020-07-18 13:57:33 +1000
commita8c230743ae3a5721b291558e6fa0e4aac2bc564 (patch)
tree51b32f85c5d444b5d62a1601ce5c5416f7342816
parent3c84157d83ed18f6c31632a4728d6a0be5b68d59 (diff)
downloadqmk_firmware-a8c230743ae3a5721b291558e6fa0e4aac2bc564.tar.gz
qmk_firmware-a8c230743ae3a5721b291558e6fa0e4aac2bc564.zip
Write firmware for the Ferris keyboard (#9634)
* Write firmware for the Ferris keyboard Took inspiration from the gergoplex and the ergodox_ez firmware for the split matrix with io_expander on the right hand. Cleaned up a lot of bit fiddling on the mcu side by taking inspiration from the `split_custom` in quantum. Still bit fiddling on the mcp side as it is particularly natural to do so with the abstractions provided by the i2c protocol. Would be good to clean that up and abstract away the wiring from the generic i2c code in a similar fashion as quantum and the mcp side behave. One improvement over the ergodox_ez and the gergoplex firmwares is that the wiring is straight forward as opposed to swapping rows and columns in two different places that end up cancelling out for some reason. At this stage, I have flashed this firmware to a board and have verified that all keys are behaving as intended by shorting pins. I still have to solder in some switches and test that everything works correctly at normal typing speeds, but I don't expect any major issues given I'm building up on previous effort, including the debouncing code from the ergodox_ez. * Remove rotation from info.json and label the keys as per default keymap * Comply with minor review feedback points * Use CUSTOM_MATRIX=lite to remove boilerplate * Update keyboards/handwired/ferris/info.json Didn't play nicely in the configurator Co-authored-by: Ryan <fauxpark@gmail.com> * Remove MIDI_ENABLE from rules.mk Co-authored-by: Ryan <fauxpark@gmail.com> * Remove FAUXCLICKY_ENABLE from rules.mk Co-authored-by: Ryan <fauxpark@gmail.com> * Prefer wait_ms over _delay_ms Co-authored-by: Ryan <fauxpark@gmail.com> * Remove unused include Co-authored-by: Ryan <fauxpark@gmail.com> * Remove unused include Co-authored-by: Ryan <fauxpark@gmail.com> * Remove unused include Co-authored-by: Ryan <fauxpark@gmail.com> * Remove unused includeh Co-authored-by: Ryan <fauxpark@gmail.com> * Use dprint over print and remove include for print.h * Remove all unused includes * Remove unused code * Cleanups thanks to code review * Move more personal settings from the ferris config to the default keymap config These setting happen to be unused in the default keymap at the moment, as it has only one layer with no homerow modifiers and no mouse key; but I would like to keep it there for two reasons: * It can serve as an example to people creating their own keymap * I plan to design a more usable default keymap that uses these features once this PR which adds the Ferris keyboard is merged. * Consolidate mcp logic inside matrix.c Co-authored-by: Ryan <fauxpark@gmail.com>
-rw-r--r--keyboards/handwired/ferris/config.h60
-rw-r--r--keyboards/handwired/ferris/ferris.c17
-rw-r--r--keyboards/handwired/ferris/ferris.h43
-rw-r--r--keyboards/handwired/ferris/info.json54
-rw-r--r--keyboards/handwired/ferris/keymaps/default/config.h39
-rw-r--r--keyboards/handwired/ferris/keymaps/default/keymap.c39
-rw-r--r--keyboards/handwired/ferris/matrix.c282
-rw-r--r--keyboards/handwired/ferris/readme.md16
-rw-r--r--keyboards/handwired/ferris/rules.mk28
9 files changed, 578 insertions, 0 deletions
diff --git a/keyboards/handwired/ferris/config.h b/keyboards/handwired/ferris/config.h
new file mode 100644
index 000000000..33494d927
--- /dev/null
+++ b/keyboards/handwired/ferris/config.h
@@ -0,0 +1,60 @@
1/*
2Copyright 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20/* USB Device descriptor parameter */
21#define VENDOR_ID 0xC2AB
22#define PRODUCT_ID 0x0000
23#define DEVICE_VER 0x0001
24#define MANUFACTURER Pierre
25#define PRODUCT Ferris the keeb
26#define DESCRIPTION A minimalistic 34 - keys split keyboard
27
28/* key matrix size */
29#define MATRIX_ROWS 8
30#define MATRIX_COLS 10
31
32#define MATRIX_ROWS_PER_SIDE (MATRIX_ROWS / 2)
33#define MATRIX_COLS_PER_SIDE (MATRIX_COLS / 2)
34
35#define UNUSED_MCU 14
36#define UNUSED_MCP 7
37
38// wiring
39#define MATRIX_ROW_PINS_MCU \
40 { B3, B2, B1, F0 }
41#define MATRIX_COL_PINS_MCU \
42 { D6, D7, B4, B5, B6 }
43#define UNUSED_PINS_MCU \
44 { B0, B7, C6, C7, D2, D3, D4, D5, E6, F1, F4, F5, F6, F7 }
45#define MATRIX_ROW_PINS_MCP \
46 { B0, B1, B2, B3 }
47#define MATRIX_COL_PINS_MCP \
48 { A0, A1, A2, A3, A4 }
49#define UNUSED_PINS_MCP \
50 { B4, B5, B6, B7, A5, A6, A7 }
51
52/* COL2ROW, ROW2COL*/
53#define DIODE_DIRECTION COL2ROW
54
55/* define if matrix has ghost (lacks anti-ghosting diodes) */
56//#define MATRIX_HAS_GHOST
57
58/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
59#define DEBOUNCE 5
60
diff --git a/keyboards/handwired/ferris/ferris.c b/keyboards/handwired/ferris/ferris.c
new file mode 100644
index 000000000..0e132fa98
--- /dev/null
+++ b/keyboards/handwired/ferris/ferris.c
@@ -0,0 +1,17 @@
1/*
2Copyright 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17#include "ferris.h"
diff --git a/keyboards/handwired/ferris/ferris.h b/keyboards/handwired/ferris/ferris.h
new file mode 100644
index 000000000..4602637ca
--- /dev/null
+++ b/keyboards/handwired/ferris/ferris.h
@@ -0,0 +1,43 @@
1/*
2Copyright 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include "quantum.h"
21
22// clang-format off
23
24/* left hand right hand */
25#define LAYOUT(\
26 K0_0, K0_1, K0_2, K0_3, K0_4, K0_5, K0_6, K0_7, K0_8, K0_9,\
27 K1_0, K1_1, K1_2, K1_3, K1_4, K1_5, K1_6, K1_7, K1_8, K1_9,\
28 K2_0, K2_1, K2_2, K2_3, K2_4, K2_5, K2_6, K2_7, K2_8, K2_9,\
29 K3_3, K3_4, K3_5, K3_6)\
30/* matrix positions */\
31{\
32 {K0_0, K0_1, K0_2, K0_3, K0_4},\
33 {K1_0, K1_1, K1_2, K1_3, K1_4},\
34 {K2_0, K2_1, K2_2, K2_3, K2_4},\
35 {KC_NO, KC_NO, KC_NO, K3_3, K3_4},\
36 \
37 {K0_5, K0_6, K0_7, K0_8, K0_9},\
38 {K1_5, K1_6, K1_7, K1_8, K1_9},\
39 {K2_5, K2_6, K2_7, K2_8, K2_9},\
40 {K3_5, K3_6, KC_NO, KC_NO, KC_NO}\
41}
42
43// clang-format on
diff --git a/keyboards/handwired/ferris/info.json b/keyboards/handwired/ferris/info.json
new file mode 100644
index 000000000..ffffb58ec
--- /dev/null
+++ b/keyboards/handwired/ferris/info.json
@@ -0,0 +1,54 @@
1{
2 "keyboard_name": "Ferris",
3 "url": "https://github.com/pierrechevalier83/ferris/",
4 "maintainer": "@pierrec83",
5 "width": 12,
6 "height": 4.75,
7 "layouts": {
8 "LAYOUT": {
9 "layout": [
10 {"x": 0, "y": 0.93},
11 {"x": 1, "y": 0.31},
12 {"x": 2, "y": 0},
13 {"x": 3, "y": 0.28},
14 {"x": 4, "y": 0.42},
15
16 {"x": 7, "y": 0.42},
17 {"x": 8, "y": 0.28},
18 {"x": 9, "y": 0},
19 {"x": 10, "y": 0.31},
20 {"x": 11, "y": 0.93},
21
22 {"x": 0, "y": 1.93},
23 {"x": 1, "y": 1.31},
24 {"x": 2, "y": 1},
25 {"x": 3, "y": 1.28},
26 {"x": 4, "y": 1.42},
27
28 {"x": 7, "y": 1.42},
29 {"x": 8, "y": 1.28},
30 {"x": 9, "y": 1},
31 {"x": 10, "y": 1.31},
32 {"x": 11, "y": 1.93},
33
34 {"x": 0, "y": 2.93},
35 {"x": 1, "y": 2.31},
36 {"x": 2, "y": 2},
37 {"x": 3, "y": 2.28},
38 {"x": 4, "y": 2.42},
39
40 {"x": 7, "y": 2.42},
41 {"x": 8, "y": 2.28},
42 {"x": 9, "y": 2},
43 {"x": 10, "y": 2.31},
44 {"x": 11, "y": 2.93},
45
46 {"x": 3.5, "y": 3.75},
47 {"x": 4.5, "y": 4},
48
49 {"x": 6.5, "y": 4},
50 {"x": 7.5, "y": 3.75}
51 ]
52 }
53 }
54}
diff --git a/keyboards/handwired/ferris/keymaps/default/config.h b/keyboards/handwired/ferris/keymaps/default/config.h
new file mode 100644
index 000000000..cf0fb7478
--- /dev/null
+++ b/keyboards/handwired/ferris/keymaps/default/config.h
@@ -0,0 +1,39 @@
1/*
2Copyright 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19// Set the mouse settings to a comfortable speed/accuracy trade-off,
20// assuming a screen refresh rate of 60 Htz or higher
21// The default is 50. This makes the mouse ~3 times faster and more accurate
22#define MOUSEKEY_INTERVAL 16
23// The default is 20. Since we made the mouse about 3 times faster with the previous setting,
24// give it more time to accelerate to max speed to retain precise control over short distances.
25#define MOUSEKEY_TIME_TO_MAX 40
26// The default is 300. Let's try and make this as low as possible while keeping the cursor responsive
27#define MOUSEKEY_DELAY 100
28// It makes sense to use the same delay for the mouseweel
29#define MOUSEKEY_WHEEL_DELAY 100
30// The default is 100
31#define MOUSEKEY_WHEEL_INTERVAL 50
32// The default is 40
33#define MOUSEKEY_WHEEL_TIME_TO_MAX 100
34
35// Pick good defaults for enabling homerow modifiers
36#define TAPPING_TERM 200
37#define PERMISSIVE_HOLD
38#define IGNORE_MOD_TAP_INTERRUPT
39#define TAPPING_FORCE_HOLD
diff --git a/keyboards/handwired/ferris/keymaps/default/keymap.c b/keyboards/handwired/ferris/keymaps/default/keymap.c
new file mode 100644
index 000000000..faf5203e7
--- /dev/null
+++ b/keyboards/handwired/ferris/keymaps/default/keymap.c
@@ -0,0 +1,39 @@
1/*
2Copyright 2020 Pierre Chevalier <pierrechevalier83@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include QMK_KEYBOARD_H
19
20// Blank template at the bottom
21const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
22/* Keymap 0: Qwerty
23 *
24 * ,-----------------------------. ,-----------------------------.
25 * | Q | W | E | R | T | | Y | U | I | O | P |
26 * |-----+-----+-----+-----+-----| |-----------------------------|
27 * | A | S | D | F | G | | H | J | K | L | ; |
28 * |-----+-----+-----+-----+-----+ |-----------------------------|
29 * | Z | X | C | V | B | | N | M | < | > | ? |
30 * `-----+-----+-----+-----+-----+--. ,-+-----------------------------'
31 * | BSPC | SPC | | SPC | ENT |
32 * '------------' '-----------'
33 */
34[0] = LAYOUT(
35 KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
36 KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN,
37 KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
38 KC_BSPC, KC_SPC, KC_SPC, KC_ENT)
39};
diff --git a/keyboards/handwired/ferris/matrix.c b/keyboards/handwired/ferris/matrix.c
new file mode 100644
index 000000000..e13c35d35
--- /dev/null
+++ b/keyboards/handwired/ferris/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}
diff --git a/keyboards/handwired/ferris/readme.md b/keyboards/handwired/ferris/readme.md
new file mode 100644
index 000000000..23aeca874
--- /dev/null
+++ b/keyboards/handwired/ferris/readme.md
@@ -0,0 +1,16 @@
1# Ferris
2
3![Ferris, top view](https://imgur.com/V4QuaGs.jpg)
4![Ferris, bottom view](https://i.imgur.com/7DJYME8.jpg)
5
6A split 34 keys column staggered keyboard named and decorated after the rustlang mascott. All PCB files and some thoughts on the design are available on the [project's github page](https://github.com/pierrechevalier83/ferris)
7
8* Keyboard Maintainer: [Pierre Chevalier](https://github.com/pierrechevalier83)
9* Hardware Supported: Ferris PCB
10* Hardware Availability: Still in prototype stage
11
12Make example for this keyboard (after setting up your build environment):
13
14 make handwired/ferris:default
15
16See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
diff --git a/keyboards/handwired/ferris/rules.mk b/keyboards/handwired/ferris/rules.mk
new file mode 100644
index 000000000..8645dbba0
--- /dev/null
+++ b/keyboards/handwired/ferris/rules.mk
@@ -0,0 +1,28 @@
1# MCU name
2MCU = atmega32u4
3
4# Bootloader selection
5BOOTLOADER = atmel-dfu
6
7# change yes to no to disable
8#
9BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration
10MOUSEKEY_ENABLE = yes # Mouse keys
11EXTRAKEY_ENABLE = yes # Audio control and System control
12CONSOLE_ENABLE = no # Console for debug
13COMMAND_ENABLE = no # Commands for debug and configuration
14# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
15SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
16# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
17NKRO_ENABLE = no # USB Nkey Rollover
18BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
19RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
20BLUETOOTH_ENABLE = no # Enable Bluetooth
21AUDIO_ENABLE = no # Audio output
22UNICODE_ENABLE = yes
23CUSTOM_MATRIX = lite
24NO_USB_STARTUP_CHECK = yes
25LTO_ENABLE = yes
26
27SRC += matrix.c
28QUANTUM_LIB_SRC += i2c_master.c