diff --git a/doc/plugin/Hardware-EZ-ErgoDox.md b/doc/plugin/Hardware-EZ-ErgoDox.md
new file mode 100644
index 00000000..e2ded6bb
--- /dev/null
+++ b/doc/plugin/Hardware-EZ-ErgoDox.md
@@ -0,0 +1,6 @@
+# Kaleidoscope-Hardware-ErgoDox
+
+This is a plugin for [Kaleidoscope][fw], that adds hardware support for
+the ErgoDox.
+
+ [fw]: https://github.com/keyboardio/Kaleidoscope
diff --git a/doc/plugin/Hardware-Technomancy-Atreus.md b/doc/plugin/Hardware-Technomancy-Atreus.md
new file mode 100644
index 00000000..fb7fcfc1
--- /dev/null
+++ b/doc/plugin/Hardware-Technomancy-Atreus.md
@@ -0,0 +1,11 @@
+# Kaleidoscope-Hardware-Technomancy-Atreus
+
+This is a plugin for [Kaleidoscope][fw], that adds hardware support for the
+[Atreus][atreus]. Supported models include post-2016 PCB variants with an A*
+MCU, and the hand-wired variant from [FalbaTech][falba] with a Teensy2.
+
+PCBs prior to 2016, and the legacy teensy2 variants are not supported.
+
+ [fw]: https://github.com/keyboardio/Kaleidoscope
+ [atreus]: https://atreus.technomancy.us/
+ [falba]: https://falba.tech/
diff --git a/examples/Atreus/.kaleidoscope-builder.conf b/examples/Atreus/.kaleidoscope-builder.conf
new file mode 100644
index 00000000..d80241fb
--- /dev/null
+++ b/examples/Atreus/.kaleidoscope-builder.conf
@@ -0,0 +1,24 @@
+flash_over_usb() {
+ sleep 1s
+
+ echo "Please reset your keyboard!"
+ teensy_loader_cli --mcu "${MCU}" -w "${HEX_FILE_PATH}"
+}
+
+flash () {
+ prepare_to_flash
+
+ # This is defined in the (optional) user config.
+ # shellcheck disable=SC2154
+ ${preFlash_HOOKS}
+
+ flash_over_usb || flash_over_usb
+
+ # This is defined in the (optional) user config.
+ # shellcheck disable=SC2154
+ ${postFlash_HOOKS}
+}
+
+NO_RESET=1
+DEFAULT_SKETCH="Atreus"
+BOARD="atreus"
diff --git a/examples/Atreus/Atreus.ino b/examples/Atreus/Atreus.ino
new file mode 100644
index 00000000..373f09ac
--- /dev/null
+++ b/examples/Atreus/Atreus.ino
@@ -0,0 +1,101 @@
+/* -*- mode: c++ -*-
+ * Atreus -- A very basic Kaleidoscope example for the Atreus
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "Kaleidoscope.h"
+#include "Kaleidoscope-Macros.h"
+
+#define MO(n) ShiftToLayer(n)
+#define TG(n) LockLayer(n)
+
+enum {
+ RESET
+};
+
+#define Key_Exclamation LSHIFT(Key_1)
+#define Key_At LSHIFT(Key_2)
+#define Key_Hash LSHIFT(Key_3)
+#define Key_Dollar LSHIFT(Key_4)
+#define Key_And LSHIFT(Key_7)
+#define Key_Star LSHIFT(Key_8)
+#define Key_Plus LSHIFT(Key_Equals)
+
+/* *INDENT-OFF* */
+KEYMAPS(
+ [0] = KEYMAP_STACKED
+ (
+ Key_Q ,Key_W ,Key_E ,Key_R ,Key_T
+ ,Key_A ,Key_S ,Key_D ,Key_F ,Key_G
+ ,Key_Z ,Key_X ,Key_C ,Key_V ,Key_B
+ ,Key_Esc ,Key_Tab ,Key_LeftGui ,Key_LeftShift ,Key_Backspace ,Key_LeftControl
+
+ ,Key_Y ,Key_U ,Key_I ,Key_O ,Key_P
+ ,Key_H ,Key_J ,Key_K ,Key_L ,Key_Semicolon
+ ,Key_N ,Key_M ,Key_Comma ,Key_Period ,Key_Slash
+ ,Key_LeftAlt ,Key_Space ,MO(1) ,Key_Minus ,Key_Quote ,Key_Enter
+ ),
+
+ [1] = KEYMAP_STACKED
+ (
+ Key_Exclamation ,Key_At ,Key_UpArrow ,Key_LeftCurlyBracket ,Key_RightCurlyBracket
+ ,Key_Hash ,Key_LeftArrow ,Key_DownArrow ,Key_RightArrow ,Key_Dollar
+ ,Key_LeftBracket ,Key_RightBracket ,Key_LeftParen ,Key_RightParen ,Key_And
+ ,TG(2) ,Key_Insert ,Key_LeftGui ,Key_LeftShift ,Key_Backspace ,Key_LeftControl
+
+ ,Key_PageUp ,Key_7 ,Key_8 ,Key_9 ,Key_Star
+ ,Key_PageDown ,Key_4 ,Key_5 ,Key_6 ,Key_Plus
+ ,Key_Backtick ,Key_1 ,Key_2 ,Key_3 ,Key_Backslash
+ ,Key_LeftAlt ,Key_Space ,MO(1) ,Key_Period ,Key_0 ,Key_Equals
+ ),
+
+ [2] = KEYMAP_STACKED
+ (
+ Key_Insert ,Key_Home ,Key_UpArrow ,Key_End ,Key_PageUp
+ ,Key_Delete ,Key_LeftArrow ,Key_DownArrow ,Key_RightArrow ,Key_PageDown
+ ,XXX ,Consumer_VolumeIncrement ,XXX ,XXX ,M(RESET)
+ ,XXX ,Consumer_VolumeDecrement ,___ ,___ ,___ ,___
+
+ ,Key_UpArrow ,Key_F7 ,Key_F8 ,Key_F9 ,Key_F10
+ ,Key_DownArrow ,Key_F4 ,Key_F5 ,Key_F6 ,Key_F11
+ ,XXX ,Key_F1 ,Key_F2 ,Key_F3 ,Key_F12
+ ,___ ,___ ,___ ,Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause
+ )
+)
+/* *INDENT-ON* */
+
+KALEIDOSCOPE_INIT_PLUGINS(Macros);
+
+const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
+ switch (macroIndex) {
+ case RESET:
+ Atreus.resetDevice();
+ break;
+ default:
+ break;
+ }
+
+ return MACRO_NONE;
+}
+
+void setup() {
+ Kaleidoscope.setup();
+}
+
+void loop() {
+ Kaleidoscope.loop();
+}
diff --git a/examples/ErgoDox/.kaleidoscope-builder.conf b/examples/ErgoDox/.kaleidoscope-builder.conf
new file mode 100644
index 00000000..8ce70f95
--- /dev/null
+++ b/examples/ErgoDox/.kaleidoscope-builder.conf
@@ -0,0 +1,24 @@
+flash_over_usb() {
+ sleep 1s
+
+ echo "Please reset your keyboard!"
+ teensy_loader_cli --mcu "${MCU}" -w "${HEX_FILE_PATH}"
+}
+
+flash () {
+ prepare_to_flash
+
+ # This is defined in the (optional) user config.
+ # shellcheck disable=SC2154
+ ${preFlash_HOOKS}
+
+ flash_over_usb || flash_over_usb
+
+ # This is defined in the (optional) user config.
+ # shellcheck disable=SC2154
+ ${postFlash_HOOKS}
+}
+
+NO_RESET=1
+DEFAULT_SKETCH="ErgoDox"
+BOARD="ergodox"
diff --git a/examples/ErgoDox/ErgoDox.ino b/examples/ErgoDox/ErgoDox.ino
new file mode 100644
index 00000000..d78779e2
--- /dev/null
+++ b/examples/ErgoDox/ErgoDox.ino
@@ -0,0 +1,57 @@
+/* -*- mode: c++ -*-
+ * ErgoDox -- A very basic Kaleidoscope example for the ErgoDox
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "Kaleidoscope.h"
+
+/* *INDENT-OFF* */
+KEYMAPS(
+ [0] = KEYMAP_STACKED
+ (
+ // left hand
+ Key_Equals, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LeftArrow,
+ Key_Delete, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_NoKey,
+ Key_Backspace, Key_A, Key_S, Key_D, Key_F, Key_G,
+ Key_LeftShift, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_LeftControl,
+ Key_Backtick, Key_Quote, Key_NoKey, Key_LeftArrow, Key_RightArrow,
+
+ Key_NoKey, Key_LeftGui,
+ Key_Home,
+ Key_Space, Key_Backspace, Key_End,
+
+ // right hand
+ Key_RightArrow, Key_6, Key_7, Key_8, Key_9, Key_0, Key_Minus,
+ Key_NoKey, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Backslash,
+ Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
+ Key_RightControl, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_RightShift,
+ Key_UpArrow, Key_DownArrow, XXX, XXX, Key_NoKey,
+
+ Key_LeftAlt, Key_Esc,
+ Key_PageUp,
+ Key_PageDown, Key_Tab, Key_Enter
+ ),
+)
+/* *INDENT-ON* */
+
+void setup() {
+ Kaleidoscope.setup();
+}
+
+void loop() {
+ Kaleidoscope.loop();
+}
diff --git a/src/Kaleidoscope-Hardware-EZ-ErgoDox.h b/src/Kaleidoscope-Hardware-EZ-ErgoDox.h
new file mode 100644
index 00000000..356e315d
--- /dev/null
+++ b/src/Kaleidoscope-Hardware-EZ-ErgoDox.h
@@ -0,0 +1,20 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * 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 .
+ */
+
+#pragma once
+#include "kaleidoscope/hardware/ErgoDox.h"
diff --git a/src/Kaleidoscope-Hardware-Technomancy-Atreus.h b/src/Kaleidoscope-Hardware-Technomancy-Atreus.h
new file mode 100644
index 00000000..f098619a
--- /dev/null
+++ b/src/Kaleidoscope-Hardware-Technomancy-Atreus.h
@@ -0,0 +1,20 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-Technomancy-Atreus -- Atreus hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * 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 .
+ */
+
+#pragma once
+#include "kaleidoscope/hardware/Atreus.h"
diff --git a/src/kaleidoscope/hardware/Atreus.cpp b/src/kaleidoscope/hardware/Atreus.cpp
new file mode 100644
index 00000000..db5219cb
--- /dev/null
+++ b/src/kaleidoscope/hardware/Atreus.cpp
@@ -0,0 +1,297 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-Technomancy-Atreus -- Atreus hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * Based on QMK (commit e9a67f8fd)
+ * (C) Jack Humbert, Phil Hagelberg, and others
+ * Original QMK sources:
+ * - keyboards/atreus/atreus.h
+ * - keyboards/atreus/config.h
+ * - quantum/matrix.c
+ * - tmk_core/common/avr/bootloader.c
+ *
+ * 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 .
+ */
+
+#ifdef ARDUINO_AVR_ATREUS
+
+#include
+#include
+#include
+
+static bool do_scan_ = true;
+
+ISR(TIMER1_OVF_vect) {
+ do_scan_ = true;
+}
+
+namespace kaleidoscope {
+namespace hardware {
+
+uint16_t Atreus::previousKeyState_[ROWS];
+uint16_t Atreus::keyState_[ROWS];
+uint16_t Atreus::masks_[ROWS];
+
+uint8_t Atreus::debounce_matrix_[ROWS][COLS];
+uint8_t Atreus::debounce = 3;
+
+void Atreus::setup(void) {
+ wdt_disable();
+ delay(100);
+
+ for (uint8_t i = 0; i < ROWS; i++) {
+ unselectRow(i);
+ keyState_[i] = previousKeyState_[i] = 0;
+ }
+
+ // Initialize columns
+ DDRB &= ~(_BV(5) | _BV(4) | _BV(6) | _BV(7));
+ PORTB |= (_BV(5) | _BV(4) | _BV(6) | _BV(7));
+
+ DDRD &= ~(_BV(7) | _BV(4) | _BV(6));
+ PORTD |= (_BV(7) | _BV(4) | _BV(6));
+
+ DDRC &= ~(_BV(6));
+ PORTC |= (_BV(6));
+
+ DDRE &= ~(_BV(6));
+ PORTE |= (_BV(6));
+
+ DDRF &= ~(_BV(6) | _BV(7));
+ PORTF |= (_BV(6) | _BV(7));
+
+ /* Set up Timer1 for 500usec */
+ TCCR1B = _BV(WGM13);
+ TCCR1A = 0;
+
+ const uint32_t cycles = (F_CPU / 2000000) * 500;
+
+ ICR1 = cycles;
+ TCCR1B = _BV(WGM13) | _BV(CS10);
+ TIMSK1 = _BV(TOIE1);
+}
+
+void Atreus::selectRow(uint8_t row) {
+ switch (row) {
+ case 0:
+ DDRD |= (_BV(0));
+ PORTD &= ~(_BV(0));
+ break;
+ case 1:
+ DDRD |= (_BV(1));
+ PORTD &= ~(_BV(1));
+ break;
+ case 2:
+ DDRD |= (_BV(3));
+ PORTD &= ~(_BV(3));
+ break;
+ case 3:
+ DDRD |= (_BV(2));
+ PORTD &= ~(_BV(2));
+ break;
+ default:
+ break;
+ }
+}
+
+void Atreus::unselectRow(uint8_t row) {
+ switch (row) {
+ case 0:
+ DDRD &= ~(_BV(0));
+ PORTD |= (_BV(0));
+ break;
+ case 1:
+ DDRD &= ~(_BV(1));
+ PORTD |= (_BV(1));
+ break;
+ case 2:
+ DDRD &= ~(_BV(3));
+ PORTD |= (_BV(3));
+ break;
+ case 3:
+ DDRD &= ~(_BV(2));
+ PORTD |= (_BV(2));
+ break;
+ default:
+ break;
+ }
+}
+
+uint16_t Atreus::readCols() {
+ return ((PINB & (_BV(4)) ? 0 : (_BV(0))) |
+ (PINB & (_BV(5)) ? 0 : (_BV(1))) |
+ (PINB & (_BV(6)) ? 0 : (_BV(2))) |
+ (PINB & (_BV(7)) ? 0 : (_BV(3))) |
+ (PINC & (_BV(6)) ? 0 : (_BV(4))) |
+ (PIND & (_BV(4)) ? 0 : (_BV(5))) |
+ (PIND & (_BV(6)) ? 0 : (_BV(6))) |
+ (PIND & (_BV(7)) ? 0 : (_BV(7))) |
+ (PINE & (_BV(6)) ? 0 : (_BV(8))) |
+ (PINF & (_BV(6)) ? 0 : (_BV(9))) |
+ (PINF & (_BV(7)) ? 0 : (_BV(10))));
+}
+
+void Atreus::readMatrixRow(uint8_t current_row) {
+ uint16_t mask, cols;
+
+ previousKeyState_[current_row] = keyState_[current_row];
+
+ mask = debounceMaskForRow(current_row);
+
+ selectRow(current_row);
+ cols = (readCols() & mask) | (keyState_[current_row] & ~mask);
+ unselectRow(current_row);
+ debounceRow(cols ^ keyState_[current_row], current_row);
+ keyState_[current_row] = cols;
+}
+
+void Atreus::readMatrix() {
+ do_scan_ = false;
+
+ for (uint8_t current_row = 0; current_row < ROWS; current_row++) {
+ readMatrixRow(current_row);
+ }
+}
+
+void Atreus::actOnMatrixScan() {
+ for (byte row = 0; row < ROWS; row++) {
+ for (byte col = 0; col < COLS; col++) {
+ uint8_t keyState = (bitRead(previousKeyState_[row], col) << 0) |
+ (bitRead(keyState_[row], col) << 1);
+ if (keyState) {
+ handleKeyswitchEvent(Key_NoKey, row, col, keyState);
+ }
+ }
+ previousKeyState_[row] = keyState_[row];
+ }
+}
+
+void Atreus::scanMatrix() {
+ if (!do_scan_)
+ return;
+
+ readMatrix();
+ actOnMatrixScan();
+}
+
+void Atreus::maskKey(byte row, byte col) {
+ if (row >= ROWS || col >= COLS)
+ return;
+
+ bitWrite(masks_[row], col, 1);
+}
+
+void Atreus::unMaskKey(byte row, byte col) {
+ if (row >= ROWS || col >= COLS)
+ return;
+
+ bitWrite(masks_[row], col, 0);
+}
+
+bool Atreus::isKeyMasked(byte row, byte col) {
+ if (row >= ROWS || col >= COLS)
+ return false;
+
+ return bitRead(masks_[row], col);
+}
+
+// Atreus-specific stuff
+
+void Atreus::resetDevice() {
+ cli();
+ UDCON = 1;
+ USBCON = (1 << FRZCLK);
+ UCSR1B = 0;
+ _delay_ms(5);
+
+ EIMSK = 0;
+ PCICR = 0;
+ SPCR = 0;
+ ACSR = 0;
+ EECR = 0;
+ ADCSRA = 0;
+ TIMSK0 = 0;
+ TIMSK1 = 0;
+ TIMSK3 = 0;
+ TIMSK4 = 0;
+ UCSR1B = 0;
+ TWCR = 0;
+ DDRB = 0;
+ DDRC = 0;
+ DDRD = 0;
+ DDRE = 0;
+ DDRF = 0;
+ TWCR = 0;
+ PORTB = 0;
+ PORTC = 0;
+ PORTD = 0;
+ PORTE = 0;
+ PORTF = 0;
+ asm volatile("jmp 0x7E00");
+}
+
+void Atreus::detachFromHost() {
+ UDCON |= (1 << DETACH);
+}
+
+void Atreus::attachToHost() {
+ UDCON &= ~(1 << DETACH);
+}
+
+bool Atreus::isKeyswitchPressed(byte row, byte col) {
+ return (bitRead(keyState_[row], col) != 0);
+}
+
+bool Atreus::isKeyswitchPressed(uint8_t keyIndex) {
+ keyIndex--;
+ return isKeyswitchPressed(keyIndex / COLS, keyIndex % COLS);
+}
+
+uint8_t Atreus::pressedKeyswitchCount() {
+ uint8_t count = 0;
+
+ for (uint8_t r = 0; r < ROWS; r++) {
+ count += __builtin_popcount(keyState_[r]);
+ }
+ return count;
+}
+
+uint16_t Atreus::debounceMaskForRow(uint8_t row) {
+ uint16_t result = 0;
+
+ for (uint16_t c = 0; c < COLS; ++c) {
+ if (debounce_matrix_[row][c]) {
+ --debounce_matrix_[row][c];
+ } else {
+ result |= (1 << c);
+ }
+ }
+ return result;
+}
+
+void Atreus::debounceRow(uint16_t change, uint8_t row) {
+ for (uint16_t i = 0; i < COLS; ++i) {
+ if (change & (1 << i)) {
+ debounce_matrix_[row][i] = debounce;
+ }
+ }
+}
+
+}
+}
+
+HARDWARE_IMPLEMENTATION KeyboardHardware;
+kaleidoscope::hardware::Atreus &Atreus = KeyboardHardware;
+
+#endif
diff --git a/src/kaleidoscope/hardware/Atreus.h b/src/kaleidoscope/hardware/Atreus.h
new file mode 100644
index 00000000..744ada74
--- /dev/null
+++ b/src/kaleidoscope/hardware/Atreus.h
@@ -0,0 +1,240 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-Technomancy-Atreus -- Atreus hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * Based on QMK (commit e9a67f8fd)
+ * (C) Jack Humbert, Jun Wako, Phil Hagelberg, and others
+ * Original QMK sources:
+ * - keyboards/atreus/atreus.h
+ * - keyboards/atreus/config.h
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#ifdef ARDUINO_AVR_ATREUS
+
+#include
+
+#define HARDWARE_IMPLEMENTATION kaleidoscope::hardware::Atreus
+#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h"
+
+#include "kaleidoscope/macro_helpers.h"
+
+struct cRGB {
+ uint8_t r, g, b;
+};
+
+#define COLS 11
+#define ROWS 4
+#define LED_COUNT 0
+
+#define CRGB(r,g,b) (cRGB){b, g, r}
+
+namespace kaleidoscope {
+namespace hardware {
+
+class Atreus {
+ public:
+ Atreus(void) {}
+
+ void syncLeds(void) {}
+ void setCrgbAt(byte row, byte col, cRGB color) {}
+ void setCrgbAt(uint8_t i, cRGB crgb) {}
+ cRGB getCrgbAt(uint8_t i) {
+ return CRGB(0, 0, 0);
+ }
+ uint8_t getLedIndex(byte row, byte col) {
+ return 0;
+ }
+
+ void scanMatrix(void);
+ void readMatrix(void);
+ void actOnMatrixScan(void);
+ void setup();
+
+ /** Detaching from / attaching to the host.
+ *
+ * These two functions should detach the device from (or attach it to) the
+ * host, preferably without rebooting the device. Their purpose is to allow
+ * one to do some configuration inbetween, so the re-attach happens with
+ * different properties. The device remains powered between these operations,
+ * only the connection to the host gets severed.
+ */
+ void detachFromHost();
+ void attachToHost();
+
+ /* Key masking
+ * -----------
+ *
+ * There are situations when one wants to ignore key events for a while, and
+ * mask them out. These functions help do that. In isolation, they do nothing,
+ * plugins and the core firmware is expected to make use of these.
+ *
+ * See `handleKeyswitchEvent` in the Kaleidoscope sources for a use-case.
+ */
+ void maskKey(byte row, byte col);
+ void unMaskKey(byte row, byte col);
+ bool isKeyMasked(byte row, byte col);
+
+ /** Key switch states
+ *
+ * These methods offer a way to peek at the key switch states, for those cases
+ * where we need to deal with the state closest to the hardware. Some methods
+ * offer a way to check if a key is pressed, others return the number of
+ * pressed keys.
+ */
+ /**
+ * Check if a key is pressed at a given position.
+ *
+ * @param row is the row the key is located at in the matrix.
+ * @param col is the column the key is located at in the matrix.
+ *
+ * @returns true if the key is pressed, false otherwise.
+ */
+ bool isKeyswitchPressed(byte row, byte col);
+ /**
+ * Check if a key is pressed at a given position.
+ *
+ * @param keyIndex is the key index, as calculated by `keyIndex`.
+ *
+ * @note Key indexes start at 1, not 0!
+ *
+ * @returns true if the key is pressed, false otherwise.
+ */
+ bool isKeyswitchPressed(uint8_t keyIndex);
+ /**
+ * Check the number of key switches currently pressed.
+ *
+ * @returns the number of keys pressed.
+ */
+ uint8_t pressedKeyswitchCount();
+
+ void resetDevice();
+
+ static uint8_t debounce;
+
+ private:
+ static uint16_t previousKeyState_[ROWS];
+ static uint16_t keyState_[ROWS];
+ static uint16_t masks_[ROWS];
+
+ static void readMatrixRow(uint8_t row);
+ static uint16_t readCols();
+ static void selectRow(uint8_t row);
+ static void unselectRow(uint8_t row);
+
+ static uint8_t debounce_matrix_[ROWS][COLS];
+ static uint16_t debounceMaskForRow(uint8_t row);
+ static void debounceRow(uint16_t change, uint8_t row);
+};
+
+#define KEYMAP( \
+ R0C0, R0C1, R0C2, R0C3, R0C4, R0C7, R0C8, R0C9, R0C10, R0C11, \
+ R1C0, R1C1, R1C2, R1C3, R1C4, R1C7, R1C8, R1C9, R1C10, R1C11, \
+ R2C0, R2C1, R2C2, R2C3, R2C4, R2C7, R2C8, R2C9, R2C10, R2C11, \
+ R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11 \
+ ) \
+ { \
+ { R0C3, R0C2, R0C7, R0C11, R0C1, XXX, R0C10, R0C0, R0C4, R0C8, R0C9 }, \
+ { R1C3, R1C2, R1C7, R1C11, R1C1, XXX, R1C10, R1C0, R1C4, R1C8, R1C9 }, \
+ { R2C3, R2C2, R2C7, R2C11, R2C1, R3C5, R2C10, R2C0, R2C4, R2C8, R2C9 }, \
+ { R3C3, R3C2, R3C7, R3C11, R3C1, R3C6, R3C10, R3C0, R3C4, R3C8, R3C9 } \
+ }
+
+#define KEYMAP_STACKED( \
+ R0C0, R0C1, R0C2, R0C3, R0C4, \
+ R1C0, R1C1, R1C2, R1C3, R1C4, \
+ R2C0, R2C1, R2C2, R2C3, R2C4, \
+ R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, \
+ \
+ R0C7, R0C8, R0C9, R0C10, R0C11, \
+ R1C7, R1C8, R1C9, R1C10, R1C11, \
+ R2C7, R2C8, R2C9, R2C10, R2C11, \
+ R3C6, R3C7, R3C8, R3C9, R3C10, R3C11 \
+ ) \
+ { \
+ { R0C3, R0C2, R0C7, R0C11, R0C1, XXX, R0C10, R0C0, R0C4, R0C8, R0C9 }, \
+ { R1C3, R1C2, R1C7, R1C11, R1C1, XXX, R1C10, R1C0, R1C4, R1C8, R1C9 }, \
+ { R2C3, R2C2, R2C7, R2C11, R2C1, R3C5, R2C10, R2C0, R2C4, R2C8, R2C9 }, \
+ { R3C3, R3C2, R3C7, R3C11, R3C1, R3C6, R3C10, R3C0, R3C4, R3C8, R3C9 } \
+ }
+}
+}
+
+/* To be used by the hardware implementations, `keyIndex` tells us the index of
+ * a key, from which we can figure out the row and column as needed. The index
+ * starts at one, so that plugins that work with a list of key indexes can use
+ * zero as a sentinel. This is important, because when we initialize arrays with
+ * fewer elements than the declared array size, the remaining elements will be
+ * zero. We can use this to avoid having to explicitly add a sentinel in
+ * user-facing code.
+ */
+constexpr byte keyIndex(byte row, byte col) {
+ return row * COLS + col + 1;
+}
+
+constexpr byte R0C0 = keyIndex(0, 0);
+constexpr byte R0C1 = keyIndex(0, 1);
+constexpr byte R0C2 = keyIndex(0, 2);
+constexpr byte R0C3 = keyIndex(0, 3);
+constexpr byte R0C4 = keyIndex(0, 4);
+
+constexpr byte R0C7 = keyIndex(0, 7);
+constexpr byte R0C8 = keyIndex(0, 8);
+constexpr byte R0C9 = keyIndex(0, 9);
+constexpr byte R0C10 = keyIndex(0, 10);
+constexpr byte R0C11 = keyIndex(0, 11);
+
+constexpr byte R1C0 = keyIndex(1, 0);
+constexpr byte R1C1 = keyIndex(1, 1);
+constexpr byte R1C2 = keyIndex(1, 2);
+constexpr byte R1C3 = keyIndex(1, 3);
+constexpr byte R1C4 = keyIndex(1, 4);
+
+constexpr byte R1C7 = keyIndex(1, 7);
+constexpr byte R1C8 = keyIndex(1, 8);
+constexpr byte R1C9 = keyIndex(1, 9);
+constexpr byte R1C10 = keyIndex(1, 10);
+constexpr byte R1C11 = keyIndex(1, 11);
+
+constexpr byte R2C0 = keyIndex(2, 0);
+constexpr byte R2C1 = keyIndex(2, 1);
+constexpr byte R2C2 = keyIndex(2, 2);
+constexpr byte R2C3 = keyIndex(2, 3);
+constexpr byte R2C4 = keyIndex(2, 4);
+
+constexpr byte R2C7 = keyIndex(2, 7);
+constexpr byte R2C8 = keyIndex(2, 8);
+constexpr byte R2C9 = keyIndex(2, 9);
+constexpr byte R2C10 = keyIndex(2, 10);
+constexpr byte R2C11 = keyIndex(2, 11);
+
+constexpr byte R3C0 = keyIndex(3, 0);
+constexpr byte R3C1 = keyIndex(3, 1);
+constexpr byte R3C2 = keyIndex(3, 2);
+constexpr byte R3C3 = keyIndex(3, 3);
+constexpr byte R3C4 = keyIndex(3, 4);
+constexpr byte R3C5 = keyIndex(3, 5);
+constexpr byte R3C6 = keyIndex(3, 6);
+constexpr byte R3C7 = keyIndex(3, 7);
+constexpr byte R3C8 = keyIndex(3, 8);
+constexpr byte R3C9 = keyIndex(3, 9);
+constexpr byte R3C10 = keyIndex(3, 10);
+constexpr byte R3C11 = keyIndex(3, 11);
+
+extern kaleidoscope::hardware::Atreus &Atreus;
+
+#endif
diff --git a/src/kaleidoscope/hardware/ErgoDox.cpp b/src/kaleidoscope/hardware/ErgoDox.cpp
new file mode 100644
index 00000000..706e3a61
--- /dev/null
+++ b/src/kaleidoscope/hardware/ErgoDox.cpp
@@ -0,0 +1,254 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * Based on QMK (commit 8cdb4a915)
+ * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk
+ * Original QMK sources:
+ * - keyboards/ergodox_ez/ergodox_ez.c
+ * - keyboards/ergodox_ez/ergodox_ez.h
+ * - keyboards/ergodox_ez/matrix.c
+ * - tmk_core/common/avr/bootloader.c
+ *
+ * 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 .
+ */
+
+#ifdef ARDUINO_AVR_ERGODOX
+
+#include
+#include
+#include
+#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h"
+
+namespace kaleidoscope {
+namespace hardware {
+
+ErgoDoxScanner ErgoDox::scanner_;
+uint8_t ErgoDox::previousKeyState_[ROWS];
+uint8_t ErgoDox::keyState_[ROWS];
+uint8_t ErgoDox::masks_[ROWS];
+uint8_t ErgoDox::debounce_matrix_[ROWS][COLS];
+uint8_t ErgoDox::debounce = 5;
+
+static bool do_scan_ = 1;
+
+void ErgoDox::setup(void) {
+ wdt_disable();
+ delay(100);
+
+ TCCR1A = 0b10101001;
+ TCCR1B = 0b00001001;
+
+ DDRB &= ~(1 << 4);
+ PORTB &= ~(1 << 4);
+
+ DDRC &= ~(1 << 7);
+ DDRD &= ~(1 << 5 | 1 << 4);
+ DDRE &= ~(1 << 6);
+ PORTC |= (1 << 7);
+ PORTD |= (1 << 5 | 1 << 4);
+ PORTE |= (1 << 6);
+
+ scanner_.begin();
+
+ setStatusLEDBrightness(1, 15);
+ setStatusLEDBrightness(2, 15);
+ setStatusLEDBrightness(3, 15);
+
+ /* Set up Timer1 for 500usec */
+ TCCR1B = _BV(WGM13);
+ TCCR1A = 0;
+
+ const uint32_t cycles = (F_CPU / 2000000) * 500;
+
+ ICR1 = cycles;
+ TCCR1B = _BV(WGM13) | _BV(CS10);
+ TIMSK1 = _BV(TOIE1);
+}
+
+ISR(TIMER1_OVF_vect) {
+ do_scan_ = true;
+}
+
+void ErgoDox::readMatrixRow(uint8_t row) {
+ uint8_t mask, cols;
+
+ previousKeyState_[row] = keyState_[row];
+ mask = debounceMaskForRow(row);
+ cols = (scanner_.readCols(row) & mask) | (keyState_[row] & ~mask);
+ debounceRow(cols ^ keyState_[row], row);
+ keyState_[row] = cols;
+}
+
+void ErgoDox::readMatrix() {
+ do_scan_ = false;
+
+ scanner_.reattachExpanderOnError();
+
+ for (uint8_t row = 0; row < ROWS / 2; row++) {
+ scanner_.selectRow(row);
+ scanner_.selectRow(row + ROWS / 2);
+
+ readMatrixRow(row);
+ readMatrixRow(row + ROWS / 2);
+
+ scanner_.unselectRows();
+ }
+}
+
+void ErgoDox::actOnMatrixScan() {
+ for (byte row = 0; row < ROWS; row++) {
+ for (byte col = 0; col < COLS; col++) {
+ uint8_t keyState = (bitRead(previousKeyState_[row], col) << 0) |
+ (bitRead(keyState_[row], col) << 1);
+ if (keyState)
+ handleKeyswitchEvent(Key_NoKey, row, col, keyState);
+ }
+ }
+}
+
+void ErgoDox::scanMatrix() {
+ if (!do_scan_)
+ return;
+
+ readMatrix();
+ actOnMatrixScan();
+}
+
+void ErgoDox::maskKey(byte row, byte col) {
+ if (row >= ROWS || col >= COLS)
+ return;
+
+ bitWrite(masks_[row], col, 1);
+}
+
+void ErgoDox::unMaskKey(byte row, byte col) {
+ if (row >= ROWS || col >= COLS)
+ return;
+
+ bitWrite(masks_[row], col, 0);
+}
+
+bool ErgoDox::isKeyMasked(byte row, byte col) {
+ if (row >= ROWS || col >= COLS)
+ return false;
+
+ return bitRead(masks_[row], col);
+}
+
+// ErgoDox-specific stuff
+void ErgoDox::setStatusLED(uint8_t led, bool state) {
+ if (state) {
+ DDRB |= (1 << (led + 4));
+ PORTB |= (1 << (led + 4));
+ } else {
+ DDRB &= ~(1 << (led + 4));
+ PORTB &= ~(1 << (led + 4));
+ }
+}
+
+void ErgoDox::setStatusLEDBrightness(uint8_t led, uint8_t brightness) {
+ (led == 1) ? (OCR1A = brightness) :
+ (led == 2) ? (OCR1B = brightness) :
+ (OCR1C = brightness);
+}
+
+void ErgoDox::resetDevice() {
+ cli();
+ UDCON = 1;
+ USBCON = (1 << FRZCLK);
+ UCSR1B = 0;
+ _delay_ms(5);
+
+ EIMSK = 0;
+ PCICR = 0;
+ SPCR = 0;
+ ACSR = 0;
+ EECR = 0;
+ ADCSRA = 0;
+ TIMSK0 = 0;
+ TIMSK1 = 0;
+ TIMSK3 = 0;
+ TIMSK4 = 0;
+ UCSR1B = 0;
+ TWCR = 0;
+ DDRB = 0;
+ DDRC = 0;
+ DDRD = 0;
+ DDRE = 0;
+ DDRF = 0;
+ TWCR = 0;
+ PORTB = 0;
+ PORTC = 0;
+ PORTD = 0;
+ PORTE = 0;
+ PORTF = 0;
+ asm volatile("jmp 0x7E00");
+}
+
+
+uint8_t ErgoDox::debounceMaskForRow(uint8_t row) {
+ uint8_t result = 0;
+
+ for (uint8_t c = 0; c < COLS; ++c) {
+ if (debounce_matrix_[row][c]) {
+ --debounce_matrix_[row][c];
+ } else {
+ result |= (1 << c);
+ }
+ }
+ return result;
+}
+
+void ErgoDox::debounceRow(uint8_t change, uint8_t row) {
+ for (uint8_t i = 0; i < COLS; ++i) {
+ if (change & (1 << i)) {
+ debounce_matrix_[row][i] = debounce;
+ }
+ }
+}
+
+void ErgoDox::detachFromHost() {
+ UDCON |= (1 << DETACH);
+}
+
+void ErgoDox::attachToHost() {
+ UDCON &= ~(1 << DETACH);
+}
+
+bool ErgoDox::isKeyswitchPressed(byte row, byte col) {
+ return (bitRead(keyState_[row], col) != 0);
+}
+
+bool ErgoDox::isKeyswitchPressed(uint8_t keyIndex) {
+ keyIndex--;
+ return isKeyswitchPressed(keyIndex / COLS, keyIndex % COLS);
+}
+
+uint8_t ErgoDox::pressedKeyswitchCount() {
+ uint8_t count = 0;
+
+ for (uint8_t r = 0; r < ROWS; r++) {
+ count += __builtin_popcount(keyState_[r]);
+ }
+ return count;
+}
+
+}
+}
+
+HARDWARE_IMPLEMENTATION KeyboardHardware;
+kaleidoscope::hardware::ErgoDox &ErgoDox = KeyboardHardware;
+
+#endif
diff --git a/src/kaleidoscope/hardware/ErgoDox.h b/src/kaleidoscope/hardware/ErgoDox.h
new file mode 100644
index 00000000..5fb36ee3
--- /dev/null
+++ b/src/kaleidoscope/hardware/ErgoDox.h
@@ -0,0 +1,294 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * Based on QMK (commit 8cdb4a915)
+ * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk
+ * Original QMK sources:
+ * - keyboards/ergodox_ez/ergodox_ez.c
+ * - keyboards/ergodox_ez/ergodox_ez.h
+ * - keyboards/ergodox_ez/matrix.c
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#ifdef ARDUINO_AVR_ERGODOX
+
+#include
+
+#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h"
+
+#define HARDWARE_IMPLEMENTATION kaleidoscope::hardware::ErgoDox
+#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h"
+
+#include "kaleidoscope/macro_helpers.h"
+
+struct cRGB {
+ uint8_t r, g, b;
+};
+
+#define COLS 6
+#define ROWS 14
+#define LED_COUNT 0
+
+#define CRGB(r,g,b) (cRGB){b, g, r}
+
+namespace kaleidoscope {
+namespace hardware {
+
+class ErgoDox {
+ public:
+ ErgoDox(void) {}
+
+ void syncLeds(void) {}
+ void setCrgbAt(byte row, byte col, cRGB color) {}
+ void setCrgbAt(uint8_t i, cRGB crgb) {}
+ cRGB getCrgbAt(uint8_t i) {
+ return CRGB(0, 0, 0);
+ }
+ uint8_t getLedIndex(byte row, byte col) {
+ return 0;
+ }
+
+ void scanMatrix(void);
+ void readMatrix(void);
+ void actOnMatrixScan(void);
+ void setup();
+
+ /** Detaching from / attaching to the host.
+ *
+ * These two functions should detach the device from (or attach it to) the
+ * host, preferably without rebooting the device. Their purpose is to allow
+ * one to do some configuration inbetween, so the re-attach happens with
+ * different properties. The device remains powered between these operations,
+ * only the connection to the host gets severed.
+ */
+ void detachFromHost();
+ void attachToHost();
+
+ /* Key masking
+ * -----------
+ *
+ * There are situations when one wants to ignore key events for a while, and
+ * mask them out. These functions help do that. In isolation, they do nothing,
+ * plugins and the core firmware is expected to make use of these.
+ *
+ * See `handleKeyswitchEvent` in the Kaleidoscope sources for a use-case.
+ */
+ void maskKey(byte row, byte col);
+ void unMaskKey(byte row, byte col);
+ bool isKeyMasked(byte row, byte col);
+
+ /** Key switch states
+ *
+ * These methods offer a way to peek at the key switch states, for those cases
+ * where we need to deal with the state closest to the hardware. Some methods
+ * offer a way to check if a key is pressed, others return the number of
+ * pressed keys.
+ */
+ /**
+ * Check if a key is pressed at a given position.
+ *
+ * @param row is the row the key is located at in the matrix.
+ * @param col is the column the key is located at in the matrix.
+ *
+ * @returns true if the key is pressed, false otherwise.
+ */
+ bool isKeyswitchPressed(byte row, byte col);
+ /**
+ * Check if a key is pressed at a given position.
+ *
+ * @param keyIndex is the key index, as calculated by `keyIndex`.
+ *
+ * @note Key indexes start at 1, not 0!
+ *
+ * @returns true if the key is pressed, false otherwise.
+ */
+ bool isKeyswitchPressed(uint8_t keyIndex);
+ /**
+ * Check the number of key switches currently pressed.
+ *
+ * @returns the number of keys pressed.
+ */
+ uint8_t pressedKeyswitchCount();
+
+ // ErgoDox-specific stuff
+ void setStatusLED(uint8_t led, bool state = true);
+ void setStatusLEDBrightness(uint8_t led, uint8_t brightness);
+
+ void resetDevice();
+
+ static uint8_t debounce;
+
+ private:
+ static ErgoDoxScanner scanner_;
+ static uint8_t previousKeyState_[ROWS];
+ static uint8_t keyState_[ROWS];
+ static uint8_t masks_[ROWS];
+ static uint8_t debounce_matrix_[ROWS][COLS];
+
+ static uint8_t debounceMaskForRow(uint8_t row);
+ static void debounceRow(uint8_t change, uint8_t row);
+ static void readMatrixRow(uint8_t row);
+};
+
+#define KEYMAP_STACKED( \
+ /* left hand, spatial positions */ \
+ r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, \
+ r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, \
+ r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, \
+ r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, \
+ r4c0, r4c1, r4c2, r4c3, r4c4, \
+ r5c5, r5c6, \
+ r5c4, \
+ r5c3, r5c2, r5c1, \
+ \
+ /* right hand, spatial positions */ \
+ r0c7, r0c8, r0c9, r0c10, r0c11, r0c12, r0c13, \
+ r1c7, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, \
+ r2c8, r2c9, r2c10, r2c11, r2c12, r2c13, \
+ r3c7, r3c8, r3c9, r3c10, r3c11, r3c12, r3c13, \
+ r4c9, r4c10, r4c11, r4c12, r4c13, \
+ r5c7, r5c8, \
+ r5c9, \
+ r5c12, r5c11, r5c10 ) \
+ \
+ /* matrix positions */ \
+ { \
+ { r0c0, r1c0, r2c0, r3c0, r4c0, XXX }, \
+ { r0c1, r1c1, r2c1, r3c1, r4c1, r5c1 }, \
+ { r0c2, r1c2, r2c2, r3c2, r4c2, r5c2 }, \
+ { r0c3, r1c3, r2c3, r3c3, r4c3, r5c3 }, \
+ { r0c4, r1c4, r2c4, r3c4, r4c4, r5c4 }, \
+ { r0c5, r1c5, r2c5, r3c5, XXX, r5c5 }, \
+ { r0c6, r1c6, XXX, r3c6, XXX, r5c6 }, \
+ \
+ { r0c7, r1c7, XXX, r3c7, XXX, r5c7 }, \
+ { r0c8, r1c8, r2c8, r3c8, XXX, r5c8 }, \
+ { r0c9, r1c9, r2c9, r3c9, r4c9, r5c9 }, \
+ { r0c10, r1c10, r2c10, r3c10, r4c10, r5c10 }, \
+ { r0c11, r1c11, r2c11, r3c11, r4c11, r5c11 }, \
+ { r0c12, r1c12, r2c12, r3c12, r4c12, r5c12 }, \
+ { r0c13, r1c13, r2c13, r3c13, r4c13, XXX } \
+ }
+}
+}
+
+/* To be used by the hardware implementations, `keyIndex` tells us the index of
+ * a key, from which we can figure out the row and column as needed. The index
+ * starts at one, so that plugins that work with a list of key indexes can use
+ * zero as a sentinel. This is important, because when we initialize arrays with
+ * fewer elements than the declared array size, the remaining elements will be
+ * zero. We can use this to avoid having to explicitly add a sentinel in
+ * user-facing code.
+ */
+constexpr byte keyIndex(byte row, byte col) {
+ return row * COLS + col + 1;
+}
+
+constexpr byte R0C0 = keyIndex(0, 0);
+constexpr byte R0C1 = keyIndex(0, 1);
+constexpr byte R0C2 = keyIndex(0, 2);
+constexpr byte R0C3 = keyIndex(0, 3);
+constexpr byte R0C4 = keyIndex(0, 4);
+constexpr byte R0C5 = keyIndex(0, 5);
+constexpr byte R0C6 = keyIndex(0, 6);
+constexpr byte R0C7 = keyIndex(0, 7);
+constexpr byte R0C8 = keyIndex(0, 8);
+constexpr byte R0C9 = keyIndex(0, 9);
+constexpr byte R0C10 = keyIndex(0, 10);
+constexpr byte R0C11 = keyIndex(0, 11);
+constexpr byte R0C12 = keyIndex(0, 12);
+constexpr byte R0C13 = keyIndex(0, 13);
+
+constexpr byte R1C0 = keyIndex(1, 0);
+constexpr byte R1C1 = keyIndex(1, 1);
+constexpr byte R1C2 = keyIndex(1, 2);
+constexpr byte R1C3 = keyIndex(1, 3);
+constexpr byte R1C4 = keyIndex(1, 4);
+constexpr byte R1C5 = keyIndex(1, 5);
+constexpr byte R1C6 = keyIndex(1, 6);
+constexpr byte R1C7 = keyIndex(1, 7);
+constexpr byte R1C8 = keyIndex(1, 8);
+constexpr byte R1C9 = keyIndex(1, 9);
+constexpr byte R1C10 = keyIndex(1, 10);
+constexpr byte R1C11 = keyIndex(1, 11);
+constexpr byte R1C12 = keyIndex(1, 12);
+constexpr byte R1C13 = keyIndex(1, 13);
+
+constexpr byte R2C0 = keyIndex(2, 0);
+constexpr byte R2C1 = keyIndex(2, 1);
+constexpr byte R2C2 = keyIndex(2, 2);
+constexpr byte R2C3 = keyIndex(2, 3);
+constexpr byte R2C4 = keyIndex(2, 4);
+constexpr byte R2C5 = keyIndex(2, 5);
+constexpr byte R2C6 = keyIndex(2, 6);
+constexpr byte R2C7 = keyIndex(2, 7);
+constexpr byte R2C8 = keyIndex(2, 8);
+constexpr byte R2C9 = keyIndex(2, 9);
+constexpr byte R2C10 = keyIndex(2, 10);
+constexpr byte R2C11 = keyIndex(2, 11);
+constexpr byte R2C12 = keyIndex(2, 12);
+constexpr byte R2C13 = keyIndex(2, 13);
+
+constexpr byte R3C0 = keyIndex(3, 0);
+constexpr byte R3C1 = keyIndex(3, 1);
+constexpr byte R3C2 = keyIndex(3, 2);
+constexpr byte R3C3 = keyIndex(3, 3);
+constexpr byte R3C4 = keyIndex(3, 4);
+constexpr byte R3C5 = keyIndex(3, 5);
+constexpr byte R3C6 = keyIndex(3, 6);
+constexpr byte R3C7 = keyIndex(3, 7);
+constexpr byte R3C8 = keyIndex(3, 8);
+constexpr byte R3C9 = keyIndex(3, 9);
+constexpr byte R3C10 = keyIndex(3, 10);
+constexpr byte R3C11 = keyIndex(3, 11);
+constexpr byte R3C12 = keyIndex(3, 12);
+constexpr byte R3C13 = keyIndex(3, 13);
+
+constexpr byte R4C0 = keyIndex(4, 0);
+constexpr byte R4C1 = keyIndex(4, 1);
+constexpr byte R4C2 = keyIndex(4, 2);
+constexpr byte R4C3 = keyIndex(4, 3);
+constexpr byte R4C4 = keyIndex(4, 4);
+constexpr byte R4C5 = keyIndex(4, 5);
+constexpr byte R4C6 = keyIndex(4, 6);
+constexpr byte R4C7 = keyIndex(4, 7);
+constexpr byte R4C8 = keyIndex(4, 8);
+constexpr byte R4C9 = keyIndex(4, 9);
+constexpr byte R4C10 = keyIndex(4, 10);
+constexpr byte R4C11 = keyIndex(4, 11);
+constexpr byte R4C12 = keyIndex(4, 12);
+constexpr byte R4C13 = keyIndex(4, 13);
+
+constexpr byte R5C0 = keyIndex(5, 0);
+constexpr byte R5C1 = keyIndex(5, 1);
+constexpr byte R5C2 = keyIndex(5, 2);
+constexpr byte R5C3 = keyIndex(5, 3);
+constexpr byte R5C4 = keyIndex(5, 4);
+constexpr byte R5C5 = keyIndex(5, 5);
+constexpr byte R5C6 = keyIndex(5, 6);
+constexpr byte R5C7 = keyIndex(5, 7);
+constexpr byte R5C8 = keyIndex(5, 8);
+constexpr byte R5C9 = keyIndex(5, 9);
+constexpr byte R5C10 = keyIndex(5, 10);
+constexpr byte R5C11 = keyIndex(5, 11);
+constexpr byte R5C12 = keyIndex(5, 12);
+constexpr byte R5C13 = keyIndex(5, 13);
+
+extern kaleidoscope::hardware::ErgoDox &ErgoDox;
+
+#endif
diff --git a/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.cpp b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.cpp
new file mode 100644
index 00000000..8969e042
--- /dev/null
+++ b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.cpp
@@ -0,0 +1,206 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * Based on QMK (commit 8cdb4a915)
+ * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk
+ * Original QMK sources:
+ * - keyboards/ergodox_ez/ergodox_ez.c
+ * - keyboards/ergodox_ez/ergodox_ez.h
+ * - keyboards/ergodox_ez/matrix.c
+ *
+ * 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 .
+ */
+
+#ifdef ARDUINO_AVR_ERGODOX
+
+#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h"
+#include
+#include "kaleidoscope/hardware/ErgoDox/i2cmaster.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
+#define IODIRB 0x01
+#define GPPUA 0x0C
+#define GPPUB 0x0D
+#define GPIOA 0x12
+#define GPIOB 0x13
+#define OLATA 0x14
+#define OLATB 0x15
+
+namespace kaleidoscope {
+namespace hardware {
+
+uint8_t ErgoDoxScanner::initExpander() {
+ uint8_t status = 0x20;
+
+ i2c_init();
+ delay(1000);
+
+ status = i2c_start(I2C_ADDR_WRITE);
+ if (status)
+ goto out;
+ status = i2c_write(IODIRA);
+ if (status)
+ goto out;
+ status = i2c_write(0b00000000);
+ if (status)
+ goto out;
+ status = i2c_write(0b00111111);
+ if (status)
+ goto out;
+
+ i2c_stop();
+
+ status = i2c_start(I2C_ADDR_WRITE);
+ if (status)
+ goto out;
+ status = i2c_write(GPPUA);
+ if (status)
+ goto out;
+ status = i2c_write(0b00000000);
+ if (status)
+ goto out;
+ status = i2c_write(0b00111111);
+ if (status)
+ goto out;
+
+out:
+ i2c_stop();
+
+ return status;
+}
+
+void
+ErgoDoxScanner::initCols() {
+ DDRF &= ~(1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0);
+ PORTF |= (1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0);
+}
+
+void
+ErgoDoxScanner::begin() {
+ expander_error_ = initExpander();
+
+ unselectRows();
+ initCols();
+}
+
+void
+ErgoDoxScanner::selectRow(int row) {
+ if (row < 7) {
+ if (!expander_error_) {
+ expander_error_ = i2c_start(I2C_ADDR_WRITE);
+ if (expander_error_)
+ goto out;
+ expander_error_ = i2c_write(GPIOA);
+ if (expander_error_)
+ goto out;
+ expander_error_ = i2c_write(0xFF & ~(1 << row));
+ if (expander_error_)
+ goto out;
+out:
+ i2c_stop();
+ }
+ } else {
+ switch (row) {
+ case 7:
+ DDRB |= (1 << 0);
+ PORTB &= ~(1 << 0);
+ break;
+ case 8:
+ DDRB |= (1 << 1);
+ PORTB &= ~(1 << 1);
+ break;
+ case 9:
+ DDRB |= (1 << 2);
+ PORTB &= ~(1 << 2);
+ break;
+ case 10:
+ DDRB |= (1 << 3);
+ PORTB &= ~(1 << 3);
+ break;
+ case 11:
+ DDRD |= (1 << 2);
+ PORTD &= ~(1 << 3);
+ break;
+ case 12:
+ DDRD |= (1 << 3);
+ PORTD &= ~(1 << 3);
+ break;
+ case 13:
+ DDRC |= (1 << 6);
+ PORTC &= ~(1 << 6);
+ break;
+ }
+ }
+}
+
+void
+ErgoDoxScanner::unselectRows() {
+ DDRB &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
+ PORTB &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
+ DDRD &= ~(1 << 2 | 1 << 3);
+ PORTD &= ~(1 << 2 | 1 << 3);
+ DDRC &= ~(1 << 6);
+ PORTC &= ~(1 << 6);
+}
+
+uint8_t
+ErgoDoxScanner::readCols(int row) {
+ if (row < 7) {
+ if (expander_error_) {
+ return 0;
+ }
+ uint8_t data = 0;
+
+ expander_error_ = i2c_start(I2C_ADDR_WRITE);
+ if (expander_error_)
+ goto out;
+ expander_error_ = i2c_write(GPIOB);
+ if (expander_error_)
+ goto out;
+ expander_error_ = i2c_start(I2C_ADDR_READ);
+ if (expander_error_)
+ goto out;
+
+ data = i2c_readNak();
+ data = ~data;
+out:
+ i2c_stop();
+ return data;
+ } else {
+ return (~((PINF & 0x03) | ((PINF & 0xF0) >> 2))) & ~0b11000000;
+ }
+}
+
+void
+ErgoDoxScanner::reattachExpanderOnError() {
+ static uint32_t start_time = millis();
+
+ if (!expander_error_)
+ return;
+
+ if (millis() - start_time < 5000)
+ return;
+
+ expander_error_ = initExpander();
+ start_time = millis();
+}
+
+}
+}
+
+#endif
diff --git a/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h
new file mode 100644
index 00000000..a3c63f06
--- /dev/null
+++ b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h
@@ -0,0 +1,56 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope
+ * Copyright (C) 2018 Keyboard.io, Inc
+ *
+ * Based on QMK (commit 8cdb4a915)
+ * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk
+ * Original QMK sources:
+ * - keyboards/ergodox_ez/ergodox_ez.c
+ * - keyboards/ergodox_ez/ergodox_ez.h
+ * - keyboards/ergodox_ez/matrix.c
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#ifdef ARDUINO_AVR_ERGODOX
+
+#include
+
+namespace kaleidoscope {
+namespace hardware {
+
+class ErgoDoxScanner {
+ public:
+ ErgoDoxScanner() {}
+
+ void begin();
+ void initCols();
+ void selectRow(int row);
+ void unselectRows();
+ uint8_t readCols(int row);
+
+ void reattachExpanderOnError();
+
+ private:
+ uint8_t expander_error_ = 0x20;
+
+ uint8_t initExpander();
+};
+
+}
+}
+
+#endif
diff --git a/src/kaleidoscope/hardware/ErgoDox/i2cmaster.cpp b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.cpp
new file mode 100644
index 00000000..74018792
--- /dev/null
+++ b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.cpp
@@ -0,0 +1,203 @@
+/*************************************************************************
+* Title: I2C master library using hardware TWI interface
+* Author: Peter Fleury http://jump.to/fleury
+* File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
+* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
+* Target: any AVR device with hardware TWI
+* Usage: API compatible with I2C Software Library i2cmaster.h
+**************************************************************************/
+
+#ifdef ARDUINO_AVR_ERGODOX
+
+#include
+#include
+
+#include "kaleidoscope/hardware/ErgoDox/i2cmaster.h"
+
+
+/* define CPU frequency in Mhz here if not defined in Makefile */
+#ifndef F_CPU
+#define F_CPU 16000000UL
+#endif
+
+/* I2C clock in Hz */
+#define SCL_CLOCK 400000L
+
+
+/*************************************************************************
+ Initialization of the I2C bus interface. Need to be called only once
+*************************************************************************/
+void i2c_init(void) {
+ /* initialize TWI clock
+ * minimal values in Bit Rate Register (TWBR) and minimal Prescaler
+ * bits in the TWI Status Register should give us maximal possible
+ * I2C bus speed - about 444 kHz
+ *
+ * for more details, see 20.5.2 in ATmega16/32 secification
+ */
+
+ TWSR = 0; /* no prescaler */
+ TWBR = 10; /* must be >= 10 for stable operation */
+
+}/* i2c_init */
+
+
+/*************************************************************************
+ Issues a start condition and sends address and transfer direction.
+ return 0 = device accessible, 1= failed to access device
+*************************************************************************/
+unsigned char i2c_start(unsigned char address) {
+ uint8_t twst;
+
+ // send START condition
+ TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
+
+ // wait until transmission completed
+ while (!(TWCR & (1 << TWINT)));
+
+ // check value of TWI Status Register. Mask prescaler bits.
+ twst = TW_STATUS & 0xF8;
+ if ((twst != TW_START) && (twst != TW_REP_START)) return 1;
+
+ // send device address
+ TWDR = address;
+ TWCR = (1 << TWINT) | (1 << TWEN);
+
+ // wail until transmission completed and ACK/NACK has been received
+ while (!(TWCR & (1 << TWINT)));
+
+ // check value of TWI Status Register. Mask prescaler bits.
+ twst = TW_STATUS & 0xF8;
+ if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) return 1;
+
+ return 0;
+
+}/* i2c_start */
+
+
+/*************************************************************************
+ Issues a start condition and sends address and transfer direction.
+ If device is busy, use ack polling to wait until device is ready
+
+ Input: address and transfer direction of I2C device
+*************************************************************************/
+void i2c_start_wait(unsigned char address) {
+ uint8_t twst;
+
+
+ while (1) {
+ // send START condition
+ TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
+
+ // wait until transmission completed
+ while (!(TWCR & (1 << TWINT)));
+
+ // check value of TWI Status Register. Mask prescaler bits.
+ twst = TW_STATUS & 0xF8;
+ if ((twst != TW_START) && (twst != TW_REP_START)) continue;
+
+ // send device address
+ TWDR = address;
+ TWCR = (1 << TWINT) | (1 << TWEN);
+
+ // wail until transmission completed
+ while (!(TWCR & (1 << TWINT)));
+
+ // check value of TWI Status Register. Mask prescaler bits.
+ twst = TW_STATUS & 0xF8;
+ if ((twst == TW_MT_SLA_NACK) || (twst == TW_MR_DATA_NACK)) {
+ /* device busy, send stop condition to terminate write operation */
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+
+ // wait until stop condition is executed and bus released
+ while (TWCR & (1 << TWSTO));
+
+ continue;
+ }
+ //if( twst != TW_MT_SLA_ACK) return 1;
+ break;
+ }
+
+}/* i2c_start_wait */
+
+
+/*************************************************************************
+ Issues a repeated start condition and sends address and transfer direction
+
+ Input: address and transfer direction of I2C device
+
+ Return: 0 device accessible
+ 1 failed to access device
+*************************************************************************/
+unsigned char i2c_rep_start(unsigned char address) {
+ return i2c_start(address);
+
+}/* i2c_rep_start */
+
+
+/*************************************************************************
+ Terminates the data transfer and releases the I2C bus
+*************************************************************************/
+void i2c_stop(void) {
+ /* send stop condition */
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+
+ // wait until stop condition is executed and bus released
+ while (TWCR & (1 << TWSTO));
+
+}/* i2c_stop */
+
+
+/*************************************************************************
+ Send one byte to I2C device
+
+ Input: byte to be transfered
+ Return: 0 write successful
+ 1 write failed
+*************************************************************************/
+unsigned char i2c_write(unsigned char data) {
+ uint8_t twst;
+
+ // send data to the previously addressed device
+ TWDR = data;
+ TWCR = (1 << TWINT) | (1 << TWEN);
+
+ // wait until transmission completed
+ while (!(TWCR & (1 << TWINT)));
+
+ // check value of TWI Status Register. Mask prescaler bits
+ twst = TW_STATUS & 0xF8;
+ if (twst != TW_MT_DATA_ACK) return 1;
+ return 0;
+
+}/* i2c_write */
+
+
+/*************************************************************************
+ Read one byte from the I2C device, request more data from device
+
+ Return: byte read from I2C device
+*************************************************************************/
+unsigned char i2c_readAck(void) {
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
+ while (!(TWCR & (1 << TWINT)));
+
+ return TWDR;
+
+}/* i2c_readAck */
+
+
+/*************************************************************************
+ Read one byte from the I2C device, read is followed by a stop condition
+
+ Return: byte read from I2C device
+*************************************************************************/
+unsigned char i2c_readNak(void) {
+ TWCR = (1 << TWINT) | (1 << TWEN);
+ while (!(TWCR & (1 << TWINT)));
+
+ return TWDR;
+
+}/* i2c_readNak */
+
+#endif
diff --git a/src/kaleidoscope/hardware/ErgoDox/i2cmaster.h b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.h
new file mode 100644
index 00000000..65533c59
--- /dev/null
+++ b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.h
@@ -0,0 +1,180 @@
+#pragma once
+/*************************************************************************
+* Title: C include file for the I2C master interface
+* (i2cmaster.S or twimaster.c)
+* Author: Peter Fleury http://jump.to/fleury
+* File: $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $
+* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
+* Target: any AVR device
+* Usage: see Doxygen manual
+**************************************************************************/
+
+#ifdef ARDUINO_AVR_ERGODOX
+
+#ifdef DOXYGEN
+/**
+ @defgroup pfleury_ic2master I2C Master library
+ @code #include @endcode
+
+ @brief I2C (TWI) Master Software Library
+
+ Basic routines for communicating with I2C slave devices. This single master
+ implementation is limited to one bus master on the I2C bus.
+
+ This I2c library is implemented as a compact assembler software implementation of the I2C protocol
+ which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c).
+ Since the API for these two implementations is exactly the same, an application can be linked either against the
+ software I2C implementation or the hardware I2C implementation.
+
+ Use 4.7k pull-up resistor on the SDA and SCL pin.
+
+ Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module
+ i2cmaster.S to your target when using the software I2C implementation !
+
+ Adjust the CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion.
+
+ @note
+ The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted
+ to GNU assembler and AVR-GCC C call interface.
+ Replaced the incorrect quarter period delays found in AVR300 with
+ half period delays.
+
+ @author Peter Fleury pfleury@gmx.ch http://jump.to/fleury
+
+ @par API Usage Example
+ The following code shows typical usage of this library, see example test_i2cmaster.c
+
+ @code
+
+ #include
+
+
+ #define Dev24C02 0xA2 // device address of EEPROM 24C02, see datasheet
+
+ int main(void)
+ {
+ unsigned char ret;
+
+ i2c_init(); // initialize I2C library
+
+ // write 0x75 to EEPROM address 5 (Byte Write)
+ i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
+ i2c_write(0x05); // write address = 5
+ i2c_write(0x75); // write value 0x75 to EEPROM
+ i2c_stop(); // set stop conditon = release bus
+
+
+ // read previously written value back from EEPROM address 5
+ i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
+
+ i2c_write(0x05); // write address = 5
+ i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode
+
+ ret = i2c_readNak(); // read one byte from EEPROM
+ i2c_stop();
+
+ for(;;);
+ }
+ @endcode
+
+*/
+#endif /* DOXYGEN */
+
+/**@{*/
+
+#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
+#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
+#endif
+
+#include
+
+/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */
+#define I2C_READ 1
+
+/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */
+#define I2C_WRITE 0
+
+
+/**
+ @brief initialize the I2C master interace. Need to be called only once
+ @param void
+ @return none
+ */
+extern void i2c_init(void);
+
+
+/**
+ @brief Terminates the data transfer and releases the I2C bus
+ @param void
+ @return none
+ */
+extern void i2c_stop(void);
+
+
+/**
+ @brief Issues a start condition and sends address and transfer direction
+
+ @param addr address and transfer direction of I2C device
+ @retval 0 device accessible
+ @retval 1 failed to access device
+ */
+extern unsigned char i2c_start(unsigned char addr);
+
+
+/**
+ @brief Issues a repeated start condition and sends address and transfer direction
+
+ @param addr address and transfer direction of I2C device
+ @retval 0 device accessible
+ @retval 1 failed to access device
+ */
+extern unsigned char i2c_rep_start(unsigned char addr);
+
+
+/**
+ @brief Issues a start condition and sends address and transfer direction
+
+ If device is busy, use ack polling to wait until device ready
+ @param addr address and transfer direction of I2C device
+ @return none
+ */
+extern void i2c_start_wait(unsigned char addr);
+
+
+/**
+ @brief Send one byte to I2C device
+ @param data byte to be transfered
+ @retval 0 write successful
+ @retval 1 write failed
+ */
+extern unsigned char i2c_write(unsigned char data);
+
+
+/**
+ @brief read one byte from the I2C device, request more data from device
+ @return byte read from I2C device
+ */
+extern unsigned char i2c_readAck(void);
+
+/**
+ @brief read one byte from the I2C device, read is followed by a stop condition
+ @return byte read from I2C device
+ */
+extern unsigned char i2c_readNak(void);
+
+/**
+ @brief read one byte from the I2C device
+
+ Implemented as a macro, which calls either i2c_readAck or i2c_readNak
+
+ @param ack 1 send ack, request more data from device
+ 0 send nak, read is followed by a stop condition
+ @return byte read from I2C device
+ */
+extern unsigned char i2c_read(unsigned char ack);
+#define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak();
+
+
+/**@}*/
+
+#endif