]> git.donarmstrong.com Git - qmk_firmware.git/commitdiff
NEK Type A (#5175)
authorMike Roberts <miker@miker.org>
Sun, 12 May 2019 01:12:06 +0000 (21:12 -0400)
committerMechMerlin <30334081+mechmerlin@users.noreply.github.com>
Sun, 12 May 2019 01:12:06 +0000 (18:12 -0700)
* 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

12 files changed:
keyboards/nek_type_a/config.h [new file with mode: 0644]
keyboards/nek_type_a/info.json [new file with mode: 0644]
keyboards/nek_type_a/keymaps/default/config.h [new file with mode: 0644]
keyboards/nek_type_a/keymaps/default/keymap.c [new file with mode: 0644]
keyboards/nek_type_a/keymaps/default/readme.md [new file with mode: 0644]
keyboards/nek_type_a/matrix.c [new file with mode: 0644]
keyboards/nek_type_a/mcp23017.c [new file with mode: 0644]
keyboards/nek_type_a/mcp23017.h [new file with mode: 0644]
keyboards/nek_type_a/nek_type_a.c [new file with mode: 0644]
keyboards/nek_type_a/nek_type_a.h [new file with mode: 0644]
keyboards/nek_type_a/readme.md [new file with mode: 0644]
keyboards/nek_type_a/rules.mk [new file with mode: 0644]

diff --git a/keyboards/nek_type_a/config.h b/keyboards/nek_type_a/config.h
new file mode 100644 (file)
index 0000000..782b91d
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/keyboards/nek_type_a/keymaps/default/config.h b/keyboards/nek_type_a/keymaps/default/config.h
new file mode 100644 (file)
index 0000000..5c2aaa2
--- /dev/null
@@ -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 (file)
index 0000000..627aa45
--- /dev/null
@@ -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 (file)
index 0000000..763125c
--- /dev/null
@@ -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 (file)
index 0000000..525296b
--- /dev/null
@@ -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 (file)
index 0000000..e242316
--- /dev/null
@@ -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 (file)
index 0000000..41c747b
--- /dev/null
@@ -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 (file)
index 0000000..ec76a20
--- /dev/null
@@ -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 (file)
index 0000000..9bf6028
--- /dev/null
@@ -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 (file)
index 0000000..49f4a46
--- /dev/null
@@ -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 (file)
index 0000000..6f172a9
--- /dev/null
@@ -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