d552c51ddf
into 1fa84ea83c
@ -0,0 +1,18 @@ | |||
// Copyright 2023 beekeeb | |||
// Copyright 2021 weteor | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#pragma once | |||
/* key matrix size */ | |||
#define MATRIX_ROWS 8 | |||
#define MATRIX_COLS 10 | |||
#define MATRIX_ROWS_PER_SIDE (MATRIX_ROWS / 2) | |||
#define MATRIX_COLS_PER_SIDE (MATRIX_COLS / 2) | |||
#define MATRIX_ROW_PINS_L { GP7, GP8, GP9, GP10} | |||
#define MATRIX_COL_PINS_L { GP11, GP12, GP13, GP14, GP15 } | |||
#define MATRIX_ROW_PINS_R { P10, P11, P12, P05 } | |||
#define MATRIX_COL_PINS_R { P06, P13, P14, P01, P00 } |
@ -0,0 +1,22 @@ | |||
/* Copyright 2023 QMK | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
#define HAL_USE_I2C TRUE | |||
#include_next <halconf.h> | |||
@ -0,0 +1,69 @@ | |||
{ | |||
"manufacturer": "beekeeb", | |||
"keyboard_name": "3w6hs", | |||
"maintainer": "beekeeb", | |||
"diode_direction": "COL2ROW", | |||
"usb": { | |||
"device_version": "1.0.0", | |||
"pid": "0x0003", | |||
"vid": "0xBEEB", | |||
"no_startup_check": true | |||
}, | |||
"processor": "RP2040", | |||
"bootloader": "rp2040", | |||
"features": { | |||
"bootmagic": true, | |||
"mousekey": true, | |||
"extrakey": true | |||
}, | |||
"community_layouts": ["split_3x5_3"], | |||
"layouts": { | |||
"LAYOUT_split_3x5_3": { | |||
"layout": [ | |||
{"matrix": [0, 0], "x": 0, "y": 0.8}, | |||
{"matrix": [0, 1], "x": 1, "y": 0.2}, | |||
{"matrix": [0, 2], "x": 2, "y": 0}, | |||
{"matrix": [0, 3], "x": 3, "y": 0.2}, | |||
{"matrix": [0, 4], "x": 4, "y": 0.4}, | |||
{"matrix": [4, 0], "x": 8, "y": 0.4}, | |||
{"matrix": [4, 1], "x": 9, "y": 0.2}, | |||
{"matrix": [4, 2], "x": 10, "y": 0}, | |||
{"matrix": [4, 3], "x": 11, "y": 0.2}, | |||
{"matrix": [4, 4], "x": 12, "y": 0.8}, | |||
{"matrix": [1, 0], "x": 0, "y": 1.8}, | |||
{"matrix": [1, 1], "x": 1, "y": 1.2}, | |||
{"matrix": [1, 2], "x": 2, "y": 1}, | |||
{"matrix": [1, 3], "x": 3, "y": 1.2}, | |||
{"matrix": [1, 4], "x": 4, "y": 1.4}, | |||
{"matrix": [5, 0], "x": 8, "y": 1.4}, | |||
{"matrix": [5, 1], "x": 9, "y": 1.2}, | |||
{"matrix": [5, 2], "x": 10, "y": 1}, | |||
{"matrix": [5, 3], "x": 11, "y": 1.2}, | |||
{"matrix": [5, 4], "x": 12, "y": 1.8}, | |||
{"matrix": [2, 0], "x": 0, "y": 2.8}, | |||
{"matrix": [2, 1], "x": 1, "y": 2.2}, | |||
{"matrix": [2, 2], "x": 2, "y": 2}, | |||
{"matrix": [2, 3], "x": 3, "y": 2.2}, | |||
{"matrix": [2, 4], "x": 4, "y": 2.4}, | |||
{"matrix": [6, 0], "x": 8, "y": 2.4}, | |||
{"matrix": [6, 1], "x": 9, "y": 2.2}, | |||
{"matrix": [6, 2], "x": 10, "y": 2}, | |||
{"matrix": [6, 3], "x": 11, "y": 2.2}, | |||
{"matrix": [6, 4], "x": 12, "y": 2.8}, | |||
{"matrix": [3, 2], "x": 3.2, "y": 3.6}, | |||
{"matrix": [3, 3], "x": 4.2, "y": 3.6}, | |||
{"matrix": [3, 4], "x": 5.2, "y": 3.8}, | |||
{"matrix": [7, 0], "x": 6.8, "y": 3.8}, | |||
{"matrix": [7, 1], "x": 7.8, "y": 3.6}, | |||
{"matrix": [7, 2], "x": 8.8, "y": 3.6} | |||
] | |||
} | |||
} | |||
} |
@ -0,0 +1,69 @@ | |||
/* Copyright 2021 weteor | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include QMK_KEYBOARD_H | |||
enum layers | |||
{ | |||
_ALPHA_QWERTY = 0, | |||
_ALPHA_COLEMAK, | |||
_SYM, | |||
_NAV, | |||
_NUM, | |||
_CFG, | |||
}; | |||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | |||
[_ALPHA_QWERTY] = LAYOUT_split_3x5_3( | |||
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, | |||
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, | |||
LSFT_T(KC_Z), KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, RSFT_T(KC_SLSH), | |||
LCTL_T(KC_ESC), LT(_NUM,KC_SPC), LT(_NAV, KC_TAB), LT(_SYM, KC_BSPC), KC_ENT, LALT_T(KC_DEL) | |||
), | |||
[_ALPHA_COLEMAK] = LAYOUT_split_3x5_3( | |||
KC_Q, KC_W, KC_F, KC_P, KC_G, KC_J, KC_L, KC_U, KC_Y, KC_QUOT, | |||
KC_A, KC_R, KC_S, KC_T, KC_D, KC_H, KC_N, KC_E, KC_I, KC_O, | |||
LSFT_T(KC_Z), KC_X, KC_C, KC_V, KC_B, KC_K, KC_M, KC_COMM, KC_DOT, RSFT_T(KC_SCLN), | |||
LCTL_T(KC_ENT), LT(_NUM,KC_SPC), LT(_NAV, KC_TAB), LT(_SYM, KC_BSPC), KC_ENT, LALT_T(KC_DEL) | |||
), | |||
[_SYM] = LAYOUT_split_3x5_3( | |||
KC_GRV , KC_CIRC, KC_AT, KC_DLR, KC_TILD, KC_AMPR, KC_EXLM, KC_PIPE, KC_UNDS, KC_HASH, | |||
KC_SLSH, KC_LBRC, KC_LCBR, KC_LPRN, KC_EQL, KC_ASTR, KC_RPRN, KC_RCBR, KC_RBRC, KC_BSLS, | |||
_______, KC_QUES, KC_PLUS, KC_PERC, XXXXXXX, XXXXXXX, XXXXXXX, KC_MINS, XXXXXXX, _______, | |||
XXXXXXX, MO(_CFG), XXXXXXX, _______, XXXXXXX, XXXXXXX | |||
), | |||
[_NAV] = LAYOUT_split_3x5_3( | |||
XXXXXXX, KC_VOLD, KC_MUTE, KC_VOLU, XXXXXXX, XXXXXXX, KC_PGDN, KC_UP, KC_PGUP, KC_DEL, | |||
KC_MPRV, KC_MPLY, KC_MSTP, KC_MNXT, XXXXXXX, KC_HOME, KC_LEFT, KC_DOWN, KC_RGHT, KC_END, | |||
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | |||
XXXXXXX, XXXXXXX, _______, XXXXXXX, MO(_CFG), XXXXXXX | |||
), | |||
[_NUM] = LAYOUT_split_3x5_3( | |||
XXXXXXX, KC_F9, KC_F10, KC_F11, KC_F12, KC_PPLS, KC_P7, KC_P8, KC_P9, KC_PSLS, | |||
XXXXXXX, KC_F5, KC_F6, KC_F7, KC_F8, KC_P0, KC_P4, KC_P5, KC_P6, KC_PDOT, | |||
XXXXXXX, KC_F1, KC_F2, KC_F3, KC_F4, KC_PMNS, KC_P1, KC_P2, KC_P3, KC_PAST, | |||
XXXXXXX, _______, XXXXXXX, KC_PEQL, KC_PENT, XXXXXXX | |||
), | |||
[_CFG] = LAYOUT_split_3x5_3( | |||
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,DF(_ALPHA_QWERTY), DF(_ALPHA_COLEMAK), | |||
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | |||
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | |||
XXXXXXX, _______, XXXXXXX, XXXXXXX, _______, XXXXXXX | |||
), | |||
}; |
@ -0,0 +1,248 @@ | |||
/* | |||
Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com> | |||
2020 Pierre Chevalier <pierrechevalier83@gmail.com> | |||
2021 weteor | |||
2023 beekeeb | |||
*/ | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
/* | |||
* This code was heavily inspired by the ergodox_ez keymap, and modernized | |||
* to take advantage of the quantum.h microcontroller agnostics gpio control | |||
* abstractions and use the macros defined in config.h for the wiring as opposed | |||
* to repeating that information all over the place. | |||
*/ | |||
#include "matrix.h" | |||
#include "debug.h" | |||
#include "wait.h" | |||
#include "i2c_master.h" | |||
extern i2c_status_t tca9555_status; | |||
//#define I2C_TIMEOUT 1000 | |||
// I2C address: | |||
// All address pins of the tca9555 are connected to the ground | |||
// | 0 | 1 | 0 | 0 | A2 | A1 | A0 | | |||
// | 0 | 1 | 0 | 0 | 0 | 0 | 0 | | |||
#define I2C_ADDR (0b0100000 << 1) | |||
// Register addresses | |||
#define IODIRA 0x06 // i/o direction register | |||
#define IODIRB 0x07 | |||
#define IREGP0 0x00 // GPIO pull-up resistor register | |||
#define IREGP1 0x01 | |||
#define OREGP0 0x02 // general purpose i/o port register (write modifies OLAT) | |||
#define OREGP1 0x03 | |||
bool i2c_initialized = 0; | |||
i2c_status_t tca9555_status = I2C_ADDR; | |||
uint8_t init_tca9555(void) { | |||
print("starting init"); | |||
tca9555_status = I2C_ADDR; | |||
// I2C subsystem | |||
if (i2c_initialized == 0) { | |||
i2c_init(); // on pins D(1,0) | |||
i2c_initialized = true; | |||
wait_ms(I2C_TIMEOUT); | |||
} | |||
// set pin direction | |||
// - unused : input : 1 | |||
// - input : input : 1 | |||
// - driving : output : 0 | |||
uint8_t conf[2] = { | |||
// This means: read all pins of port 0 | |||
0b11111111, | |||
// This means: we will write on pins 0 to 3 on port 1. read rest | |||
0b11110000, | |||
}; | |||
tca9555_status = i2c_write_register(I2C_ADDR, IODIRA, conf, 2, I2C_TIMEOUT); | |||
return tca9555_status; | |||
} | |||
/* matrix state(1:on, 0:off) */ | |||
static matrix_row_t matrix[MATRIX_ROWS]; // debounced values | |||
static matrix_row_t read_cols(uint8_t row); | |||
static void init_cols(void); | |||
static void unselect_rows(void); | |||
static void select_row(uint8_t row); | |||
static uint8_t tca9555_reset_loop; | |||
void matrix_init_custom(void) { | |||
// initialize row and col | |||
tca9555_status = init_tca9555(); | |||
unselect_rows(); | |||
init_cols(); | |||
// initialize matrix state: all keys off | |||
for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | |||
matrix[i] = 0; | |||
} | |||
} | |||
void matrix_power_up(void) { | |||
tca9555_status = init_tca9555(); | |||
unselect_rows(); | |||
init_cols(); | |||
// initialize matrix state: all keys off | |||
for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | |||
matrix[i] = 0; | |||
} | |||
} | |||
// Reads and stores a row, returning | |||
// whether a change occurred. | |||
static inline bool store_matrix_row(matrix_row_t current_matrix[], uint8_t index) { | |||
matrix_row_t temp = read_cols(index); | |||
if (current_matrix[index] != temp) { | |||
current_matrix[index] = temp; | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool matrix_scan_custom(matrix_row_t current_matrix[]) { | |||
if (tca9555_status) { // if there was an error | |||
if (++tca9555_reset_loop == 0) { | |||
// since tca9555_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans | |||
// this will be approx bit more frequent than once per second | |||
dprint("trying to reset tca9555\n"); | |||
tca9555_status = init_tca9555(); | |||
if (tca9555_status) { | |||
dprint("right side not responding\n"); | |||
} else { | |||
dprint("right side attached\n"); | |||
} | |||
} | |||
} | |||
bool changed = false; | |||
for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) { | |||
// select rows from left and right hands | |||
uint8_t left_index = i; | |||
uint8_t right_index = i + MATRIX_ROWS_PER_SIDE; | |||
select_row(left_index); | |||
select_row(right_index); | |||
// we don't need a 30us delay anymore, because selecting a | |||
// left-hand row requires more than 30us for i2c. | |||
changed |= store_matrix_row(current_matrix, left_index); | |||
changed |= store_matrix_row(current_matrix, right_index); | |||
unselect_rows(); | |||
} | |||
return changed; | |||
} | |||
static void init_cols(void) { | |||
// init on tca9555 | |||
// not needed, already done as part of init_tca9555() | |||
// init on mcu | |||
pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; | |||
for (int pin_index = 0; pin_index < MATRIX_COLS_PER_SIDE; pin_index++) { | |||
pin_t pin = matrix_col_pins_mcu[pin_index]; | |||
gpio_set_pin_input_high(pin); | |||
gpio_write_pin_high(pin); | |||
} | |||
} | |||
static matrix_row_t read_cols(uint8_t row) { | |||
if (row < MATRIX_ROWS_PER_SIDE) { | |||
pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; | |||
matrix_row_t current_row_value = 0; | |||
// For each col... | |||
for (uint8_t col_index = 0; col_index < MATRIX_COLS_PER_SIDE; col_index++) { | |||
// Select the col pin to read (active low) | |||
uint8_t pin_state = gpio_read_pin(matrix_col_pins_mcu[col_index]); | |||
// Populate the matrix row with the state of the col pin | |||
current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); | |||
} | |||
return current_row_value; | |||
} else { | |||
if (tca9555_status) { // if there was an error | |||
return 0; | |||
} else { | |||
uint8_t data = 0; | |||
uint8_t port0 = 0; | |||
tca9555_status = i2c_read_register(I2C_ADDR, IREGP0, &port0, 1, I2C_TIMEOUT); | |||
if (tca9555_status) { // if there was an error | |||
// do nothing | |||
return 0; | |||
} else { | |||
port0 = ~port0; | |||
// We read all the pins on GPIOA. | |||
// 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. | |||
// 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. | |||
// 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). | |||
data |= (port0 & 0x01) << 4; | |||
data |= (port0 & 0x02) << 2; | |||
data |= (port0 & 0x04); | |||
data |= (port0 & 0x08) >> 2; | |||
data |= (port0 & 0x10) >> 4; | |||
tca9555_status = I2C_STATUS_SUCCESS; | |||
return data; | |||
} | |||
} | |||
} | |||
} | |||
static void unselect_rows(void) { | |||
// no need to unselect on tca9555, because the select step sets all | |||
// the other row bits high, and it's not changing to a different | |||
// direction | |||
// unselect rows on microcontroller | |||
pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; | |||
for (int pin_index = 0; pin_index < MATRIX_ROWS_PER_SIDE; pin_index++) { | |||
pin_t pin = matrix_row_pins_mcu[pin_index]; | |||
gpio_set_pin_input_high(pin); | |||
gpio_write_pin_low(pin); | |||
} | |||
} | |||
static void select_row(uint8_t row) { | |||
uint8_t port1 = 0xff; | |||
if (row < MATRIX_ROWS_PER_SIDE) { | |||
// select on atmega32u4 | |||
pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; | |||
pin_t pin = matrix_row_pins_mcu[row]; | |||
gpio_set_pin_output(pin); | |||
gpio_write_pin_low(pin); | |||
} else { | |||
// select on tca9555 | |||
if (tca9555_status) { // if there was an error | |||
// do nothing | |||
} else { | |||
switch(row) { | |||
case 4: port1 &= ~(1 << 0); break; | |||
case 5: port1 &= ~(1 << 1); break; | |||
case 6: port1 &= ~(1 << 2); break; | |||
case 7: | |||
port1 &= ~(1 << 3); | |||
break; | |||
default: break; | |||
} | |||
tca9555_status = i2c_write_register(I2C_ADDR, OREGP1, &port1, 1, I2C_TIMEOUT); | |||
// 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. | |||
// 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. | |||
} | |||
} | |||
} |
@ -0,0 +1,34 @@ | |||
/* Copyright 2023 QMK | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
#include_next <mcuconf.h> | |||
#undef RP_I2C_USE_I2C0 | |||
#undef RP_I2C_USE_I2C1 | |||
#define RP_I2C_USE_I2C0 TRUE | |||
#define RP_I2C_USE_I2C1 FALSE | |||
#undef I2C_DRIVER | |||
#define I2C_DRIVER I2CD0 | |||
#undef I2C1_SCL_PIN | |||
#undef I2C1_SDA_PIN | |||
#define I2C0_SDA_PIN GP0 | |||
#define I2C0_SCL_PIN GP1 | |||
#define I2C1_SDA_PIN GP0 | |||
#define I2C1_SCL_PIN GP1 | |||
@ -0,0 +1,26 @@ | |||
# 3W6HS | |||
![3W6HS](https://i.imgur.com/CPxwGSt.jpeg) | |||
The 3W6HS is a hotswap, RP2040, low profile, split keyboard with 36 keys, modified from 3W6 by weteor. | |||
* Keyboard Maintainer: [beekeeb](https://github.com/beekeeb) | |||
* Hardware Supported: RP2040 | |||
* Hardware Availability: [https://shop.beekeeb.com/](https://shop.beekeeb.com) | |||
Make example for this keyboard (after setting up your build environment): | |||
make beekeeb/3w6hs:default | |||
Flashing example for this keyboard: | |||
make beekeeb/3w6hs:default:flash | |||
See 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). | |||
## Bootloader | |||
To enter the bootloader, follow these steps: | |||
* Disconnect the keyboard from the computer. | |||
* Locate the BOOT button, which is the top button on the left side of the keyboard. | |||
* While holding down the BOOT button, connect the keyboard back to the computer. |
@ -0,0 +1,3 @@ | |||
CUSTOM_MATRIX = lite | |||
SRC += matrix.c | |||
I2C_DRIVER_REQUIRED = yes |