Merge remote-tracking branch 'plugin/Hardware-EZ-ErgoDox/f/monorepo' into f/monorepo-stage3

pull/427/head
Gergely Nagy 6 years ago
commit 2b80ecbde3
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -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

@ -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"

@ -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();
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/hardware/ErgoDox.h"

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifdef ARDUINO_AVR_ERGODOX
#include <Kaleidoscope.h>
#include <KeyboardioHID.h>
#include <avr/wdt.h>
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef ARDUINO_AVR_ERGODOX
#include <Arduino.h>
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifdef ARDUINO_AVR_ERGODOX
#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h"
#include <avr/wdt.h>
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef ARDUINO_AVR_ERGODOX
#include <Arduino.h>
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

@ -0,0 +1,203 @@
/*************************************************************************
* Title: I2C master library using hardware TWI interface
* Author: Peter Fleury <pfleury@gmx.ch> 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 <inttypes.h>
#include <compat/twi.h>
#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

@ -0,0 +1,180 @@
#pragma once
/*************************************************************************
* Title: C include file for the I2C master interface
* (i2cmaster.S or twimaster.c)
* Author: Peter Fleury <pfleury@gmx.ch> 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 <i2cmaster.h> @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 <i2cmaster.h>
#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 <avr/io.h>
/** 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<br>
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
Loading…
Cancel
Save