commit
ea6c133dea
@ -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,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/
|
@ -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"
|
@ -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();
|
||||
}
|
@ -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,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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "kaleidoscope/hardware/Atreus.h"
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef ARDUINO_AVR_ATREUS
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include <KeyboardioHID.h>
|
||||
#include <avr/wdt.h>
|
||||
|
||||
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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_AVR_ATREUS
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#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
|
@ -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…
Reference in new issue