summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Roberts <miker@miker.org>2019-05-11 21:12:06 -0400
committerMechMerlin <30334081+mechmerlin@users.noreply.github.com>2019-05-11 18:12:06 -0700
commitf542c0589bec06501a3990070c08638a9124f055 (patch)
tree82fa658c9b6a16bde2f8b3a91239c085af09763a
parentd53cbd2dc6ec6877f2815c546c03b39fac7a8afa (diff)
NEK Type A (#5175)
* project creation and config.h import * fix name * cleanup * layout for left * working left with feather pins * full keymap * ? * let's do this * non working twimaster version * it fucking works! * bluetooth! * cleanup * use auto output for ADAFRUIT_BLE * remove auto from custom matrix * better ble auto * fix f1 * revert * fix ble * update readme * Update readme.md * Update readme.md
-rw-r--r--keyboards/nek_type_a/config.h55
-rw-r--r--keyboards/nek_type_a/info.json0
-rw-r--r--keyboards/nek_type_a/keymaps/default/config.h19
-rw-r--r--keyboards/nek_type_a/keymaps/default/keymap.c39
-rw-r--r--keyboards/nek_type_a/keymaps/default/readme.md3
-rw-r--r--keyboards/nek_type_a/matrix.c412
-rw-r--r--keyboards/nek_type_a/mcp23017.c107
-rw-r--r--keyboards/nek_type_a/mcp23017.h71
-rw-r--r--keyboards/nek_type_a/nek_type_a.c43
-rw-r--r--keyboards/nek_type_a/nek_type_a.h58
-rw-r--r--keyboards/nek_type_a/readme.md30
-rw-r--r--keyboards/nek_type_a/rules.mk33
12 files changed, 870 insertions, 0 deletions
diff --git a/keyboards/nek_type_a/config.h b/keyboards/nek_type_a/config.h
new file mode 100644
index 0000000000..782b91d0e3
--- /dev/null
+++ b/keyboards/nek_type_a/config.h
@@ -0,0 +1,55 @@
+/*
+Copyright 2018 Mike Roberts
+
+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/>.
+*/
+
+#pragma once
+
+#include "config_common.h"
+
+/* USB Device descriptor parameter */
+#define VENDOR_ID 0xFEED
+#define PRODUCT_ID 0x0000
+#define DEVICE_VER 0x0001
+#define MANUFACTURER miker
+#define PRODUCT nek_type_a
+#define DESCRIPTION NEK Type A
+
+/* key matrix size */
+#define MATRIX_ROWS 6
+#define MATRIX_COLS 18
+
+/* left columns are all onboard, right columns all on expander */
+#define COL_EXPANDED { false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true}
+#define MATRIX_COL_PINS { C6, D7, B5, B6, B7, D6, D3, GPA0, GPA1, GPA2, GPA3, GPA4, GPA5, GPA6, GPA7, GPB0, GPB1, GPB2 }
+#define MATRIX_ROW_PINS { F7, F6, F5, F4, F1, F0 }
+
+#define DIODE_DIRECTION ROW2COL
+
+/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
+#define DEBOUNCING_DELAY 5
+
+/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
+#define LOCKING_SUPPORT_ENABLE
+
+/* Locking resynchronize hack */
+#define LOCKING_RESYNC_ENABLE
+
+/* key combination for magic key command */
+#define IS_COMMAND() ( \
+ keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
+)
+
+
diff --git a/keyboards/nek_type_a/info.json b/keyboards/nek_type_a/info.json
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/keyboards/nek_type_a/info.json
diff --git a/keyboards/nek_type_a/keymaps/default/config.h b/keyboards/nek_type_a/keymaps/default/config.h
new file mode 100644
index 0000000000..5c2aaa2f31
--- /dev/null
+++ b/keyboards/nek_type_a/keymaps/default/config.h
@@ -0,0 +1,19 @@
+/* Copyright 2018 Mike Roberts
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+// place overrides here
diff --git a/keyboards/nek_type_a/keymaps/default/keymap.c b/keyboards/nek_type_a/keymaps/default/keymap.c
new file mode 100644
index 0000000000..627aa45900
--- /dev/null
+++ b/keyboards/nek_type_a/keymaps/default/keymap.c
@@ -0,0 +1,39 @@
+/* Copyright 2018 Mike Roberts
+ *
+ * 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
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+ [0] = LAYOUT(
+ KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC__MUTE, KC__VOLDOWN, KC__VOLUP, \
+ KC_GRAVE, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINUS, KC_EQUAL, KC_BSPACE, KC_INSERT, KC_HOME, KC_PGUP, \
+ KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRACKET, KC_RBRACKET, KC_BSLASH, KC_DELETE, KC_END, KC_PGDOWN, \
+ KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCOLON, KC_QUOTE, KC_ENTER, \
+ KC_LSHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMMA, KC_DOT, KC_SLASH, KC_RSHIFT, KC_UP, \
+ KC_LCTRL, KC_LALT, KC_LCMD, KC_SPC, KC_SPC, KC_RCMD, KC_RALT, KC_RCTRL, KC_APP, KC_LEFT, KC_DOWN, KC_RIGHT \
+ ),
+};
+
+void matrix_init_user(void) {
+
+}
+
+void matrix_scan_user(void) {
+
+}
+
+void led_set_user(uint8_t usb_led) {
+
+}
diff --git a/keyboards/nek_type_a/keymaps/default/readme.md b/keyboards/nek_type_a/keymaps/default/readme.md
new file mode 100644
index 0000000000..763125cea5
--- /dev/null
+++ b/keyboards/nek_type_a/keymaps/default/readme.md
@@ -0,0 +1,3 @@
+![NEK Type A Layout](https://i.imgur.com/ElEVvze.png)
+
+# Default NEK Type A Keymap
diff --git a/keyboards/nek_type_a/matrix.c b/keyboards/nek_type_a/matrix.c
new file mode 100644
index 0000000000..525296b1f1
--- /dev/null
+++ b/keyboards/nek_type_a/matrix.c
@@ -0,0 +1,412 @@
+/*
+Copyright 2012-2018 Jun Wako, Jack Humbert, Mike Roberts
+
+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/>.
+*/
+
+/*
+ * This matrix.c has been hacked up to support some columns being on an ex pander in ROW2COL mode.
+ * The columns are only ever selected and unselected, never read. Unselecting a single column via the expander is not
+ * implemented because updating one column costs the same as updating all the columns in a bank. Currently both banks
+ * are unselected but two i2c transactions could be removed if we only unselect the the proper half.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#if defined(__AVR__)
+#include <avr/io.h>
+#endif
+#include "wait.h"
+#include "print.h"
+#include "debug.h"
+#include "util.h"
+#include "matrix.h"
+#include "timer.h"
+#include "mcp23017.h"
+#include "outputselect.h"
+
+/* Set 0 if debouncing isn't needed */
+
+#ifndef DEBOUNCING_DELAY
+# define DEBOUNCING_DELAY 5
+#endif
+
+#if (DEBOUNCING_DELAY > 0)
+ static uint16_t debouncing_time;
+ static bool debouncing = false;
+#endif
+
+#if (MATRIX_COLS <= 8)
+# define print_matrix_header() print("\nr/c 01234567\n")
+# define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))
+# define matrix_bitpop(i) bitpop(matrix[i])
+# define ROW_SHIFTER ((uint8_t)1)
+#elif (MATRIX_COLS <= 16)
+# define print_matrix_header() print("\nr/c 0123456789ABCDEF\n")
+# define print_matrix_row(row) print_bin_reverse16(matrix_get_row(row))
+# define matrix_bitpop(i) bitpop16(matrix[i])
+# define ROW_SHIFTER ((uint16_t)1)
+#elif (MATRIX_COLS <= 32)
+# define print_matrix_header() print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
+# define print_matrix_row(row) print_bin_reverse32(matrix_get_row(row))
+# define matrix_bitpop(i) bitpop32(matrix[i])
+# define ROW_SHIFTER ((uint32_t)1)
+#endif
+
+#ifdef MATRIX_MASKED
+ extern const matrix_row_t matrix_mask[];
+#endif
+
+#if (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
+static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
+static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+static const bool col_expanded[MATRIX_COLS] = COL_EXPANDED;
+#endif
+
+/* matrix state(1:on, 0:off) */
+static matrix_row_t matrix[MATRIX_ROWS];
+
+static matrix_row_t matrix_debouncing[MATRIX_ROWS];
+
+
+#if (DIODE_DIRECTION == COL2ROW)
+ static void init_cols(void);
+ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
+ static void unselect_rows(void);
+ static void select_row(uint8_t row);
+ static void unselect_row(uint8_t row);
+#elif (DIODE_DIRECTION == ROW2COL)
+ static void init_rows(void);
+ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
+ static void unselect_cols(void);
+ static void unselect_col(uint8_t col);
+ static void select_col(uint8_t col);
+#endif
+
+__attribute__ ((weak))
+void matrix_init_quantum(void) {
+ expander_init();
+ matrix_init_kb();
+}
+
+__attribute__ ((weak))
+void matrix_scan_quantum(void) {
+ matrix_scan_kb();
+}
+
+__attribute__ ((weak))
+void matrix_init_kb(void) {
+ matrix_init_user();
+}
+
+__attribute__ ((weak))
+void matrix_scan_kb(void) {
+ matrix_scan_user();
+}
+
+__attribute__ ((weak))
+void matrix_init_user(void) {
+}
+
+__attribute__ ((weak))
+void matrix_scan_user(void) {
+}
+
+inline
+uint8_t matrix_rows(void) {
+ return MATRIX_ROWS;
+}
+
+inline
+uint8_t matrix_cols(void) {
+ return MATRIX_COLS;
+}
+
+void matrix_init(void) {
+ // initialize row and col
+#if (DIODE_DIRECTION == COL2ROW)
+ unselect_rows();
+ init_cols();
+#elif (DIODE_DIRECTION == ROW2COL)
+ unselect_cols();
+ init_rows();
+#endif
+
+ // initialize matrix state: all keys off
+ for (uint8_t i=0; i < MATRIX_ROWS; i++) {
+ matrix[i] = 0;
+ matrix_debouncing[i] = 0;
+ }
+
+ matrix_init_quantum();
+ set_output(OUTPUT_AUTO);
+}
+
+uint8_t matrix_scan(void)
+{
+
+#if (DIODE_DIRECTION == COL2ROW)
+
+ // Set row, read cols
+ for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
+# if (DEBOUNCING_DELAY > 0)
+ bool matrix_changed = read_cols_on_row(matrix_debouncing, current_row);
+
+ if (matrix_changed) {
+ debouncing = true;
+ debouncing_time = timer_read();
+ }
+
+# else
+ read_cols_on_row(matrix, current_row);
+# endif
+
+ }
+
+#elif (DIODE_DIRECTION == ROW2COL)
+
+ // Set col, read rows
+ for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
+# if (DEBOUNCING_DELAY > 0)
+ bool matrix_changed = read_rows_on_col(matrix_debouncing, current_col);
+ if (matrix_changed) {
+ debouncing = true;
+ debouncing_time = timer_read();
+ }
+# else
+ read_rows_on_col(matrix, current_col);
+# endif
+
+ }
+
+#endif
+
+# if (DEBOUNCING_DELAY > 0)
+ if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+ matrix[i] = matrix_debouncing[i];
+ }
+ debouncing = false;
+ }
+# endif
+
+ matrix_scan_quantum();
+ return 1;
+}
+
+bool matrix_is_modified(void)
+{
+#if (DEBOUNCING_DELAY > 0)
+ if (debouncing) return false;
+#endif
+ return true;
+}
+
+inline
+bool matrix_is_on(uint8_t row, uint8_t col)
+{
+ return (matrix[row] & ((matrix_row_t)1<col));
+}
+
+inline
+matrix_row_t matrix_get_row(uint8_t row)
+{
+ // Matrix mask lets you disable switches in the returned matrix data. For example, if you have a
+ // switch blocker installed and the switch is always pressed.
+#ifdef MATRIX_MASKED
+ return matrix[row] & matrix_mask[row];
+#else
+ return matrix[row];
+#endif
+}
+
+void matrix_print(void)
+{
+ print_matrix_header();
+
+ for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+ phex(row); print(": ");
+ print_matrix_row(row);
+ print("\n");
+ }
+}
+
+uint8_t matrix_key_count(void)
+{
+ uint8_t count = 0;
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+ count += matrix_bitpop(i);
+ }
+ return count;
+}
+
+
+
+#if (DIODE_DIRECTION == COL2ROW)
+
+static void init_cols(void)
+{
+ for(uint8_t x = 0; x < MATRIX_COLS; x++) {
+ uint8_t pin = col_pins[x];
+ _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+ _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF); // HI
+ }
+}
+
+static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
+{
+ // Store last value of row prior to reading
+ matrix_row_t last_row_value = current_matrix[current_row];
+
+ // Clear data in matrix row
+ current_matrix[current_row] = 0;
+
+ // Select row and wait for row selecton to stabilize
+ select_row(current_row);
+ wait_us(30);
+
+ // For each col...
+ for(uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
+
+ // Select the col pin to read (active low)
+ uint8_t pin = col_pins[col_index];
+ uint8_t pin_state = (_SFR_IO8(pin >> 4) & _BV(pin & 0xF));
+
+ // Populate the matrix row with the state of the col pin
+ current_matrix[current_row] |= pin_state ? 0 : (ROW_SHIFTER << col_index);
+ }
+
+ // Unselect row
+ unselect_row(current_row);
+
+ return (last_row_value != current_matrix[current_row]);
+}
+
+static void select_row(uint8_t row)
+{
+ uint8_t pin = row_pins[row];
+ _SFR_IO8((pin >> 4) + 1) |= _BV(pin & 0xF); // OUT
+ _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
+}
+
+static void unselect_row(uint8_t row)
+{
+ uint8_t pin = row_pins[row];
+ _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+ _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF); // HI
+}
+
+static void unselect_rows(void)
+{
+ for(uint8_t x = 0; x < MATRIX_ROWS; x++) {
+ uint8_t pin = row_pins[x];
+ _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+ _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF); // HI
+ }
+}
+
+#elif (DIODE_DIRECTION == ROW2COL)
+
+static void init_rows(void)
+{
+ for(uint8_t x = 0; x < MATRIX_ROWS; x++) {
+ uint8_t pin = row_pins[x];
+ _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+ _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF); // HI
+ }
+}
+
+static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
+{
+ bool matrix_changed = false;
+
+ // Select col and wait for col selecton to stabilize
+ select_col(current_col);
+ wait_us(30);
+
+ // For each row...
+ for(uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++)
+ {
+ // Store last value of row prior to reading
+ matrix_row_t last_row_value = current_matrix[row_index];
+
+ // Check row pin state
+ if ((_SFR_IO8(row_pins[row_index] >> 4) & _BV(row_pins[row_index] & 0xF)) == 0)
+ {
+ // Pin LO, set col bit
+ current_matrix[row_index] |= (ROW_SHIFTER << current_col);
+ }
+ else
+ {
+ // Pin HI, clear col bit
+ current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
+ }
+
+ // Determine if the matrix changed state
+ if ((last_row_value != current_matrix[row_index]) && !(matrix_changed))
+ {
+ matrix_changed = true;
+ }
+ }
+
+ // Unselect col
+ unselect_col(current_col);
+
+ return matrix_changed;
+}
+
+static void select_col(uint8_t col)
+{
+ uint8_t pin = col_pins[col];
+ if (col_expanded[col])
+ {
+ expander_select(pin);
+ }
+ else
+ {
+ _SFR_IO8((pin >> 4) + 1) |= _BV(pin & 0xF); // OUT
+ _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
+ }
+}
+
+
+static void unselect_col(uint8_t col)
+{
+ uint8_t pin = col_pins[col];
+ if (col_expanded[col])
+ {
+ expander_unselect_all();
+ }
+ else
+ {
+ _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+ _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF); // HI
+ }
+}
+
+static void unselect_cols(void)
+{
+ expander_unselect_all();
+
+ for(uint8_t col = 0; col < MATRIX_COLS; col++) {
+ uint8_t pin = col_pins[col];
+ if (!col_expanded[col])
+ {
+ _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+ _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF); // HI
+ }
+ }
+}
+
+#endif
diff --git a/keyboards/nek_type_a/mcp23017.c b/keyboards/nek_type_a/mcp23017.c
new file mode 100644
index 0000000000..e242316804
--- /dev/null
+++ b/keyboards/nek_type_a/mcp23017.c
@@ -0,0 +1,107 @@
+/* Copyright 2018 Mike Roberts
+ *
+ * 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 <stdbool.h>
+#include "action.h"
+#include "lib/lufa/LUFA/Drivers/Peripheral/TWI.h"
+#include "lib/lufa/LUFA/Drivers/Peripheral/AVR8/TWI_AVR8.c"
+#include "mcp23017.h"
+#include "debug.h"
+#include "wait.h"
+
+uint8_t bit_for_pin(uint8_t pin);
+
+uint8_t expander_write(uint8_t reg, uint8_t data);
+
+uint8_t expander_read(uint8_t reg, uint8_t *data);
+
+void expander_config(void);
+
+static const char *twi_err_str(uint8_t res) {
+ switch (res) {
+ case TWI_ERROR_NoError:
+ return "OK";
+ case TWI_ERROR_BusFault:
+ return "BUSFAULT";
+ case TWI_ERROR_BusCaptureTimeout:
+ return "BUSTIMEOUT";
+ case TWI_ERROR_SlaveResponseTimeout:
+ return "SLAVETIMEOUT";
+ case TWI_ERROR_SlaveNotReady:
+ return "SLAVENOTREADY";
+ case TWI_ERROR_SlaveNAK:
+ return "SLAVENAK";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+void expander_init(void) {
+ TWI_Init(TWI_BIT_PRESCALE_1, TWI_BITLENGTH_FROM_FREQ(1, 400000));
+}
+
+// set IN and HI
+void expander_unselect_all() {
+ expander_write(EXPANDER_REG_IODIRA, 0xff);
+ expander_write(EXPANDER_REG_IODIRB, 0xff);
+ expander_write(EXPANDER_REG_OLATA, 0xff);
+ expander_write(EXPANDER_REG_OLATB, 0xff);
+ wait_us(EXPANDER_PAUSE);
+}
+
+// set OUT and LOW
+void expander_select(uint8_t pin) {
+ const uint8_t mask = 0xff & ~(1 << bit_for_pin(pin));
+ if (pin < 8) {
+ expander_write(EXPANDER_REG_IODIRA, mask);
+ expander_write(EXPANDER_REG_OLATA, mask);
+ } else {
+ expander_write(EXPANDER_REG_IODIRB, mask);
+ expander_write(EXPANDER_REG_OLATB, mask);
+ }
+ wait_us(EXPANDER_PAUSE);
+}
+
+void expander_config() {
+ // set everything to input
+ expander_write(EXPANDER_REG_IODIRA, 0xff);
+ expander_write(EXPANDER_REG_IODIRB, 0xff);
+
+ // turn on pull-ups
+ expander_write(EXPANDER_REG_GPPUA, 0xff);
+ expander_write(EXPANDER_REG_GPPUB, 0xff);
+
+ // disable interrupts
+ expander_write(EXPANDER_REG_GPINTENA, 0x0);
+ expander_write(EXPANDER_REG_GPINTENB, 0x0);
+
+ // polarity
+ expander_write(EXPANDER_REG_IPOLA, 0x0);
+ expander_write(EXPANDER_REG_IPOLB, 0x0);
+}
+
+uint8_t bit_for_pin(uint8_t pin) {
+ return pin % 8;
+}
+
+uint8_t expander_write(uint8_t reg, unsigned char val) {
+ uint8_t addr = reg;
+ uint8_t result = TWI_WritePacket(EXPANDER_ADDR << 1, I2C_TIMEOUT, &addr, sizeof(addr), &val, sizeof(val));
+ if (result) {
+ xprintf("mcp: set_register %d = %d failed: %s\n", reg, val, twi_err_str(result));
+ }
+ return result == 0;
+}
+
diff --git a/keyboards/nek_type_a/mcp23017.h b/keyboards/nek_type_a/mcp23017.h
new file mode 100644
index 0000000000..41c747beab
--- /dev/null
+++ b/keyboards/nek_type_a/mcp23017.h
@@ -0,0 +1,71 @@
+/* Copyright 2018 Mike Roberts
+ *
+ * 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/>.
+ */
+#ifndef MAP23017_H
+#define MAP23017_H
+
+#define EXPANDER_ADDR 0x27
+#define I2C_TIMEOUT 200 // milliseconds
+#define EXPANDER_PAUSE 0 // microseconds
+
+enum EXPANDER_REGISTERS {
+ EXPANDER_REG_IODIRA = 0x00,
+ EXPANDER_REG_IODIRB = 0x01,
+ EXPANDER_REG_IPOLA = 0x02,
+ EXPANDER_REG_IPOLB = 0x03,
+ EXPANDER_REG_GPINTENA = 0x04,
+ EXPANDER_REG_GPINTENB = 0x05,
+ EXPANDER_REG_DEFVALA = 0x06,
+ EXPANDER_REG_DEFVALB = 0x07,
+ EXPANDER_REG_INTCONA = 0x08,
+ EXPANDER_REG_INTCONB = 0x09,
+ EXPANDER_REG_IOCONA = 0x0A,
+ EXPANDER_REG_IOCONB = 0x0B,
+ EXPANDER_REG_GPPUA = 0x0C,
+ EXPANDER_REG_GPPUB = 0x0D,
+ EXPANDER_REG_INTFA = 0x0E,
+ EXPANDER_REG_INTFB = 0x0F,
+ EXPANDER_REG_INTCAPA = 0x10,
+ EXPANDER_REG_INTCAPB = 0x11,
+ EXPANDER_REG_GPIOA = 0x12,
+ EXPANDER_REG_GPIOB = 0x13,
+ EXPANDER_REG_OLATA = 0x14,
+ EXPANDER_REG_OLATB = 0x15
+};
+
+#define GPA0 0x0
+#define GPA1 0x1
+#define GPA2 0x2
+#define GPA3 0x3
+#define GPA4 0x4
+#define GPA5 0x5
+#define GPA6 0x6
+#define GPA7 0x7
+#define GPB0 0x8
+#define GPB1 0x9
+#define GPB2 0xA
+#define GPB3 0xB
+#define GPB4 0xC
+#define GPB5 0xD
+#define GPB6 0xE
+#define GPB7 0xF
+
+
+void expander_init(void);
+void expander_select(uint8_t pin);
+void expander_unselect(uint8_t pin);
+void expander_unselect_all(void);
+
+#endif \ No newline at end of file
diff --git a/keyboards/nek_type_a/nek_type_a.c b/keyboards/nek_type_a/nek_type_a.c
new file mode 100644
index 0000000000..ec76a209b1
--- /dev/null
+++ b/keyboards/nek_type_a/nek_type_a.c
@@ -0,0 +1,43 @@
+/* Copyright 2018 Mike Roberts
+ *
+ * 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 "nek_type_a.h"
+
+void matrix_init_kb(void) {
+ // put your keyboard start-up code here
+ // runs once when the firmware starts up
+
+ matrix_init_user();
+}
+
+void matrix_scan_kb(void) {
+ // put your looping keyboard code here
+ // runs every cycle (a lot)
+
+ matrix_scan_user();
+}
+
+bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+ // put your per-action keyboard code here
+ // runs for every action, just before processing by the firmware
+
+ return process_record_user(keycode, record);
+}
+
+void led_set_kb(uint8_t usb_led) {
+ // put your keyboard LED indicator (ex: Caps Lock LED) toggling code here
+
+ led_set_user(usb_led);
+}
diff --git a/keyboards/nek_type_a/nek_type_a.h b/keyboards/nek_type_a/nek_type_a.h
new file mode 100644
index 0000000000..9bf6028cb9
--- /dev/null
+++ b/keyboards/nek_type_a/nek_type_a.h
@@ -0,0 +1,58 @@
+/* Copyright 2018 REPLACE_WITH_YOUR_NAME
+ *
+ * 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/>.
+ */
+#ifndef NEK_TYPE_A_H
+#define NEK_TYPE_A_H
+
+#include "quantum.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <util/delay.h>
+
+#define I2C_ADDR 0b0100000
+#define I2C_ADDR_WRITE ( (I2C_ADDR<<1) | I2C_WRITE )
+#define I2C_ADDR_READ ( (I2C_ADDR<<1) | I2C_READ )
+#define IODIRA 0x00 // i/o direction register
+#define IODIRB 0x01
+#define GPPUA 0x0C // GPIO pull-up resistor register
+#define GPPUB 0x0D
+#define GPIOA 0x12 // general purpose i/o port register (write modifies OLAT)
+#define GPIOB 0x13
+#define OLATA 0x14 // output latch register
+#define OLATB 0x15
+
+extern uint8_t expander_status;
+extern uint8_t expander_input_pin_mask;
+extern bool i2c_initialized;
+
+void init_expander(void);
+
+#define LAYOUT( \
+ L12, L14, L15, L16, L17, R11, R12, R13, R14, R15, R16, R17, R18, R19, R1A, R1B, \
+ L21, L22, L23, L24, L25, L26, L27, R21, R22, R23, R24, R25, R26, R28, R29, R2A, R2B, \
+ L31, L32, L33, L34, L35, L36, R31, R32, R33, R34, R35, R36, R37, R38, R39, R3A, R3B, \
+ L41, L42, L43, L44, L45, L46, R41, R42, R43, R44, R45, R46, R48, \
+ L51, L52, L53, L54, L55, L56, R51, R52, R53, R54, R55, R58, R5A, \
+ L61, L62, L63, L65, R61, R63, R65, R66, R68, R69, R6A, R6B \
+) \
+{ \
+ { KC_NO, L12, KC_NO, L14, L15, L16, L17, R11, R12, R13, R14, R15, R16, R17, R18, R19, R1A, R1B }, \
+ { L21, L22, L23, L24, L25, L26, L27, R21, R22, R23, R24, R25, R26, KC_NO, R28, R29, R2A, R2B }, \
+ { L31, L32, L33, L34, L35, L36, KC_NO, R31, R32, R33, R34, R35, R36, R37, R38, R39, R3A, R3B }, \
+ { L41, L42, L43, L44, L45, L46, KC_NO, R41, R42, R43, R44, R45, R46, KC_NO, R48, KC_NO, KC_NO, KC_NO }, \
+ { L51, L52, L53, L54, L55, L56, KC_NO, R51, R52, R53, R54, R55, KC_NO, KC_NO, R58, KC_NO, R5A, KC_NO }, \
+ { L61, L62, L63, KC_NO, L65, KC_NO, KC_NO, R61, KC_NO, R63, KC_NO, R65, R66, KC_NO, R68, R69, R6A, R6B }, \
+}
+#endif
diff --git a/keyboards/nek_type_a/readme.md b/keyboards/nek_type_a/readme.md
new file mode 100644
index 0000000000..49f4a46599
--- /dev/null
+++ b/keyboards/nek_type_a/readme.md
@@ -0,0 +1,30 @@
+# nek_type_a
+
+![NEK Type A Keyboard](https://i.imgur.com/XFnjlQ9.jpg)
+
+Natural Ergonomic Keyboard, Type A
+
+Keyboard Maintainer: [Mike Roberts](https://github.com/ecopoesis)
+Hardware Supported: Custom PCBs from https://github.com/ecopoesis/nek-type-a
+Hardware Availability: https://github.com/ecopoesis/nek-type-a
+
+## Design
+
+This is a column-driven split keyboard using three custom PCBs connected with ribbon cables. The left and right PCBs are
+passive: they only have the diodes and switches needed to make the matrix. The center PCB has an Adafruit Feather 32u4 and
+MCP23017 expander.
+
+The left matrix has its rows and columns directly connected to the Feather. The right matrix has its rows connect to the
+Feather (using the same pins as the left matrix) and its columns connected to the expander. The expander uses the LUFA
+hardware TWI driver.
+
+Bluetooth is enabled.
+
+## Building
+
+Make and install this keyboard (after setting up your build environment):
+```
+make nek_type_a:default:avrdude
+```
+
+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).
diff --git a/keyboards/nek_type_a/rules.mk b/keyboards/nek_type_a/rules.mk
new file mode 100644
index 0000000000..6f172a9cea
--- /dev/null
+++ b/keyboards/nek_type_a/rules.mk
@@ -0,0 +1,33 @@
+SRC = matrix.c mcp23017.c
+
+MCU = atmega32u4
+F_CPU = 8000000
+
+ARCH = AVR8
+F_USB = $(F_CPU)
+
+# Interrupt driven control endpoint task(+60)
+OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
+
+BOOTLOADER = caterina
+
+BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
+MOUSEKEY_ENABLE = no # Mouse keys(+4700)
+EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
+CONSOLE_ENABLE = yes # Console for debug(+400)
+COMMAND_ENABLE = yes # Commands for debug and configuration
+# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
+SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
+# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
+NKRO_ENABLE = no # USB Nkey Rollover
+BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality on B7 by default
+RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
+MIDI_ENABLE = no # MIDI support (+2400 to 4200, depending on config)
+UNICODE_ENABLE = no # Unicode
+BLUETOOTH_ENABLE = yes # Enable Bluetooth with the Adafruit EZ-Key HID
+AUDIO_ENABLE = no # Audio output on port C6
+FAUXCLICKY_ENABLE = no # Use buzzer to emulate clicky switches
+HD44780_ENABLE = no # Enable support for HD44780 based LCDs (+400)
+CUSTOM_MATRIX = yes
+DEBUG_ENABLE = yes
+BLUETOOTH = AdafruitBLE \ No newline at end of file