This implements a new device plugin, to drive the Dygma Raise. A few helpers are also introduces, which are used by the Raise only for now, but are generic enough so that eventually, other boards may use them too. Signed-off-by: Gergely Nagy <algernon@keyboard.io>pull/722/head
parent
7ab65b94d5
commit
2c5f4a78a2
@ -0,0 +1,21 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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/device/dygma/Raise.h"
|
@ -0,0 +1,515 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-EEPROM-Settings.h>
|
||||
#include <Kaleidoscope-LEDControl.h>
|
||||
#include <KeyboardioHID.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#include "kaleidoscope/util/crc16.h"
|
||||
#include "kaleidoscope/driver/color/GammaCorrection.h"
|
||||
|
||||
#define I2C_CLOCK_KHZ 200
|
||||
#define I2C_FLASH_CLOCK_KHZ 100 // flashing doesn't work reliably at higher clock speeds
|
||||
|
||||
#define SIDE_POWER 1 // side power switch pa10
|
||||
|
||||
#define LAYOUT_ISO 0
|
||||
#define LAYOUT_ANSI 1
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
|
||||
/********* RaiseHands *********/
|
||||
|
||||
struct RaiseHands {
|
||||
static raise::Hand leftHand;
|
||||
static raise::Hand rightHand;
|
||||
|
||||
static void setup();
|
||||
static void initializeSides();
|
||||
|
||||
static uint8_t layout;
|
||||
|
||||
static void setSidePower(bool power);
|
||||
static bool getSidePower() {
|
||||
return side_power_;
|
||||
}
|
||||
|
||||
static void keyscanInterval(uint16_t interval);
|
||||
static uint16_t keyscanInterval() {
|
||||
return keyscan_interval_;
|
||||
}
|
||||
|
||||
private:
|
||||
static uint16_t keyscan_interval_;
|
||||
static bool side_power_;
|
||||
static uint16_t settings_base_;
|
||||
static constexpr uint8_t iso_only_led_ = 19;
|
||||
};
|
||||
|
||||
raise::Hand RaiseHands::leftHand(0);
|
||||
raise::Hand RaiseHands::rightHand(1);
|
||||
uint8_t RaiseHands::layout;
|
||||
bool RaiseHands::side_power_;
|
||||
uint16_t RaiseHands::settings_base_;
|
||||
uint16_t RaiseHands::keyscan_interval_ = 50;
|
||||
|
||||
void RaiseHands::setSidePower(bool power) {
|
||||
digitalWrite(SIDE_POWER, power ? HIGH : LOW);
|
||||
side_power_ = power;
|
||||
}
|
||||
|
||||
void RaiseHands::setup() {
|
||||
settings_base_ = ::EEPROMSettings.requestSlice(sizeof(keyscan_interval_));
|
||||
|
||||
// If keyscan is max, assume that EEPROM is uninitialized, and store the
|
||||
// defaults.
|
||||
uint16_t interval;
|
||||
Kaleidoscope.storage().get(settings_base_, interval);
|
||||
if (interval == 0xffff) {
|
||||
Kaleidoscope.storage().put(settings_base_, keyscan_interval_);
|
||||
Kaleidoscope.storage().commit();
|
||||
}
|
||||
Kaleidoscope.storage().get(settings_base_, keyscan_interval_);
|
||||
}
|
||||
|
||||
void RaiseHands::keyscanInterval(uint16_t interval) {
|
||||
leftHand.setKeyscanInterval(interval);
|
||||
rightHand.setKeyscanInterval(interval);
|
||||
keyscan_interval_ = interval;
|
||||
Kaleidoscope.storage().put(settings_base_, keyscan_interval_);
|
||||
Kaleidoscope.storage().commit();
|
||||
}
|
||||
|
||||
void RaiseHands::initializeSides() {
|
||||
// key scan interval from eeprom
|
||||
leftHand.setKeyscanInterval(keyscan_interval_);
|
||||
rightHand.setKeyscanInterval(keyscan_interval_);
|
||||
|
||||
// get ANSI/ISO at every side replug
|
||||
uint8_t l_layout = leftHand.readLayout();
|
||||
uint8_t r_layout = rightHand.readLayout();
|
||||
|
||||
// setup layout variable, this will affect led mapping - defaults to ISO if
|
||||
// nothing reported
|
||||
// FIXME
|
||||
if (l_layout == 1 || r_layout == 1)
|
||||
layout = 1;
|
||||
else
|
||||
layout = 0;
|
||||
|
||||
/*
|
||||
* if the neuron starts up with no sides connected, it will assume ISO. This
|
||||
* turns on an extra LED (hardware LED 19 on left side). If an ANSI left is
|
||||
* then plugged in, the keyboard will switch to ANSI, but LED 19 can't get
|
||||
* wiped because the ANSI LED map doesn't include this LED. It will be driven
|
||||
* from the SLED1735's memory with the same colour as before, which causes
|
||||
* weird looking colours to come on on other seemingly unrelated keys. So: on
|
||||
* a replug, set LED 19 to off to be safe.
|
||||
*/
|
||||
leftHand.led_data.leds[iso_only_led_] = {0, 0, 0};
|
||||
|
||||
// get activated LED plugin to refresh
|
||||
::LEDControl.refreshAll();
|
||||
}
|
||||
|
||||
/********* LED Driver *********/
|
||||
|
||||
bool RaiseLEDDriver::isLEDChangedNeuron;
|
||||
uint8_t RaiseLEDDriver::isLEDChangedLeft[LED_BANKS];
|
||||
uint8_t RaiseLEDDriver::isLEDChangedRight[LED_BANKS];
|
||||
cRGB RaiseLEDDriver::neuronLED;
|
||||
constexpr uint8_t RaiseLEDDriver::led_map[][RaiseLEDDriverProps::led_count + 1];
|
||||
|
||||
constexpr uint8_t RaiseLEDDriverProps::key_led_map[];
|
||||
|
||||
void RaiseLEDDriver::syncLeds() {
|
||||
// left and right sides
|
||||
for (uint8_t i = 0; i < LED_BANKS; i ++) {
|
||||
// only send the banks that have changed - try to improve jitter performance
|
||||
if (isLEDChangedLeft[i]) {
|
||||
RaiseHands::leftHand.sendLEDBank(i);
|
||||
isLEDChangedLeft[i] = false;
|
||||
}
|
||||
if (isLEDChangedRight[i]) {
|
||||
RaiseHands::rightHand.sendLEDBank(i);
|
||||
isLEDChangedRight[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLEDChangedNeuron) {
|
||||
updateNeuronLED();
|
||||
isLEDChangedNeuron = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseLEDDriver::updateNeuronLED() {
|
||||
static constexpr struct {
|
||||
uint8_t r, g, b;
|
||||
} pins = { 3, 5, 4 };
|
||||
auto constexpr gamma8 = kaleidoscope::driver::color::gamma_correction;
|
||||
|
||||
// invert as these are common anode, and make sure we reach 65535 to be able
|
||||
// to turn fully off.
|
||||
analogWrite(pins.r, ((256 - pgm_read_byte(&gamma8[neuronLED.r])) << 8) - 1);
|
||||
analogWrite(pins.g, ((256 - pgm_read_byte(&gamma8[neuronLED.g])) << 8) - 1);
|
||||
analogWrite(pins.b, ((256 - pgm_read_byte(&gamma8[neuronLED.b])) << 8) - 1);
|
||||
}
|
||||
|
||||
void RaiseLEDDriver::setCrgbAt(uint8_t i, cRGB crgb) {
|
||||
// prevent reading off the end of the led_map array
|
||||
if (i >= Props_::led_count)
|
||||
return;
|
||||
|
||||
// neuron LED
|
||||
if (i == Props_::led_count - 1) {
|
||||
isLEDChangedNeuron |= !(neuronLED.r == crgb.r &&
|
||||
neuronLED.g == crgb.g &&
|
||||
neuronLED.b == crgb.b);
|
||||
neuronLED = crgb;
|
||||
return;
|
||||
}
|
||||
|
||||
// get the SLED index
|
||||
uint8_t sled_num = led_map[RaiseHands::layout][i];
|
||||
if (sled_num < LEDS_PER_HAND) {
|
||||
cRGB oldColor = RaiseHands::leftHand.led_data.leds[sled_num];
|
||||
RaiseHands::leftHand.led_data.leds[sled_num] = crgb;
|
||||
isLEDChangedLeft[uint8_t(sled_num / 8)] |= !(oldColor.r == crgb.r &&
|
||||
oldColor.g == crgb.g &&
|
||||
oldColor.b == crgb.b);
|
||||
} else if (sled_num < 2 * LEDS_PER_HAND) {
|
||||
cRGB oldColor = RaiseHands::rightHand.led_data.leds[sled_num - LEDS_PER_HAND];
|
||||
RaiseHands::rightHand.led_data.leds[sled_num - LEDS_PER_HAND] = crgb;
|
||||
isLEDChangedRight[uint8_t((sled_num - LEDS_PER_HAND) / 8)] |=
|
||||
!(oldColor.r == crgb.r &&
|
||||
oldColor.g == crgb.g &&
|
||||
oldColor.b == crgb.b);
|
||||
} else {
|
||||
// TODO(anyone):
|
||||
// how do we want to handle debugging assertions about crazy user
|
||||
// code that would overwrite other memory?
|
||||
}
|
||||
}
|
||||
|
||||
cRGB RaiseLEDDriver::getCrgbAt(uint8_t i) {
|
||||
if (i >= Props_::led_count)
|
||||
return {0, 0, 0};
|
||||
|
||||
uint8_t sled_num = led_map[RaiseHands::layout][i];
|
||||
if (sled_num < LEDS_PER_HAND) {
|
||||
return RaiseHands::leftHand.led_data.leds[sled_num];
|
||||
} else if (sled_num < 2 * LEDS_PER_HAND) {
|
||||
return RaiseHands::rightHand.led_data.leds[sled_num - LEDS_PER_HAND];
|
||||
} else {
|
||||
return {0, 0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseLEDDriver::setup() {
|
||||
pinMode(SIDE_POWER, OUTPUT);
|
||||
RaiseHands::setSidePower(false);
|
||||
|
||||
// arduino zero analogWrite(255) isn't fully on as its actually working with a
|
||||
// 16bit counter and the mapping is a bit shift.
|
||||
// so change to 16 bit resolution to avoid the mapping and do the mapping
|
||||
// ourselves in updateHubleLED() to ensure LEDs can be set fully off
|
||||
analogWriteResolution(16);
|
||||
updateNeuronLED();
|
||||
|
||||
delay(10);
|
||||
RaiseHands::setSidePower(true);
|
||||
delay(500); // wait for sides to power up and finish bootloader
|
||||
}
|
||||
|
||||
/********* Key scanner *********/
|
||||
|
||||
raise::keydata_t RaiseKeyScanner::leftHandState;
|
||||
raise::keydata_t RaiseKeyScanner::rightHandState;
|
||||
raise::keydata_t RaiseKeyScanner::previousLeftHandState;
|
||||
raise::keydata_t RaiseKeyScanner::previousRightHandState;
|
||||
raise::keydata_t RaiseKeyScanner::leftHandMask;
|
||||
raise::keydata_t RaiseKeyScanner::rightHandMask;
|
||||
bool RaiseKeyScanner::lastLeftOnline;
|
||||
bool RaiseKeyScanner::lastRightOnline;
|
||||
|
||||
void RaiseKeyScanner::readMatrix() {
|
||||
previousLeftHandState = leftHandState;
|
||||
previousRightHandState = rightHandState;
|
||||
|
||||
if (RaiseHands::leftHand.readKeys()) {
|
||||
leftHandState = RaiseHands::leftHand.getKeyData();
|
||||
// if ANSI, then swap r3c0 and r3c1 to match the PCB
|
||||
if (RaiseHands::layout == LAYOUT_ANSI)
|
||||
// only swap if bits are different
|
||||
if ((leftHandState.rows[3] & (1 << 0)) ^ leftHandState.rows[3] & (1 << 1)) {
|
||||
leftHandState.rows[3] ^= (1 << 0); // flip the bit
|
||||
leftHandState.rows[3] ^= (1 << 1); // flip the bit
|
||||
}
|
||||
}
|
||||
|
||||
if (RaiseHands::rightHand.readKeys()) {
|
||||
rightHandState = RaiseHands::rightHand.getKeyData();
|
||||
// if ANSI, then swap r1c0 and r2c0 to match the PCB
|
||||
if (RaiseHands::layout == LAYOUT_ANSI)
|
||||
if ((rightHandState.rows[1] & (1 << 0)) ^ rightHandState.rows[2] & (1 << 0)) {
|
||||
rightHandState.rows[1] ^= (1 << 0);
|
||||
rightHandState.rows[2] ^= (1 << 0);
|
||||
}
|
||||
}
|
||||
|
||||
// if a side has just been replugged, initialise it
|
||||
if ((RaiseHands::leftHand.online && !lastLeftOnline) ||
|
||||
(RaiseHands::rightHand.online && !lastRightOnline))
|
||||
RaiseHands::initializeSides();
|
||||
|
||||
// if a side has just been unplugged, wipe its state
|
||||
if (!RaiseHands::leftHand.online && lastLeftOnline)
|
||||
leftHandState.all = 0;
|
||||
|
||||
if (!RaiseHands::rightHand.online && lastRightOnline)
|
||||
rightHandState.all = 0;
|
||||
|
||||
// store previous state of whether the sides are plugged in
|
||||
lastLeftOnline = RaiseHands::leftHand.online;
|
||||
lastRightOnline = RaiseHands::rightHand.online;
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::actOnMatrixScan() {
|
||||
for (byte row = 0; row < Props_::matrix_rows; row++) {
|
||||
for (byte col = 0; col < Props_::matrix_columns; col++) {
|
||||
uint8_t keynum = (row * Props_::matrix_rows) + (col);
|
||||
uint8_t keyState;
|
||||
|
||||
// left
|
||||
keyState = (bitRead(previousLeftHandState.all, keynum) << 0) |
|
||||
(bitRead(leftHandState.all, keynum) << 1);
|
||||
if (keyState)
|
||||
ThisType::handleKeyswitchEvent(Key_NoKey, KeyAddr(row, col), keyState);
|
||||
|
||||
// right
|
||||
keyState = (bitRead(previousRightHandState.all, keynum) << 0) |
|
||||
(bitRead(rightHandState.all, keynum) << 1);
|
||||
if (keyState)
|
||||
ThisType::handleKeyswitchEvent(Key_NoKey, KeyAddr(row, (Props_::matrix_columns - 1) - col), keyState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::scanMatrix() {
|
||||
readMatrix();
|
||||
actOnMatrixScan();
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::maskKey(KeyAddr key_addr) {
|
||||
if (!key_addr.isValid())
|
||||
return;
|
||||
|
||||
auto row = key_addr.row();
|
||||
auto col = key_addr.col();
|
||||
|
||||
if (col >= Props_::left_columns) {
|
||||
rightHandMask.rows[row] |= 1 << (Props_::right_columns - (col - Props_::left_columns));
|
||||
} else {
|
||||
leftHandMask.rows[row] |= 1 << (Props_::right_columns - col);
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::unMaskKey(KeyAddr key_addr) {
|
||||
if (!key_addr.isValid())
|
||||
return;
|
||||
|
||||
auto row = key_addr.row();
|
||||
auto col = key_addr.col();
|
||||
|
||||
if (col >= Props_::left_columns) {
|
||||
rightHandMask.rows[row] &= ~(1 << (Props_::right_columns - (col - Props_::left_columns)));
|
||||
} else {
|
||||
leftHandMask.rows[row] &= ~(1 << (Props_::right_columns - col));
|
||||
}
|
||||
}
|
||||
|
||||
bool RaiseKeyScanner::isKeyMasked(KeyAddr key_addr) {
|
||||
if (!key_addr.isValid())
|
||||
return false;
|
||||
|
||||
auto row = key_addr.row();
|
||||
auto col = key_addr.col();
|
||||
|
||||
if (col >= 8) {
|
||||
return rightHandMask.rows[row] & (1 << (7 - (col - 8)));
|
||||
} else {
|
||||
return leftHandMask.rows[row] & (1 << (7 - col));
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::maskHeldKeys() {
|
||||
memcpy(leftHandMask.rows, leftHandState.rows, sizeof(leftHandMask));
|
||||
memcpy(rightHandMask.rows, rightHandState.rows, sizeof(rightHandMask));
|
||||
}
|
||||
|
||||
bool RaiseKeyScanner::isKeyswitchPressed(KeyAddr key_addr) {
|
||||
auto row = key_addr.row();
|
||||
auto col = key_addr.col();
|
||||
|
||||
if (col >= Props_::left_columns) {
|
||||
return (bitRead(rightHandState.rows[row], (Props_::matrix_columns - 1) - col) != 0);
|
||||
} else {
|
||||
return (bitRead(leftHandState.rows[row], col) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool RaiseKeyScanner::wasKeyswitchPressed(KeyAddr key_addr) {
|
||||
auto row = key_addr.row();
|
||||
auto col = key_addr.col();
|
||||
|
||||
if (col >= Props_::left_columns) {
|
||||
return (bitRead(previousRightHandState.rows[row], (Props_::matrix_columns - 1) - col) != 0);
|
||||
} else {
|
||||
return (bitRead(previousLeftHandState.rows[row], col) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t RaiseKeyScanner::pressedKeyswitchCount() {
|
||||
return __builtin_popcountl(leftHandState.all) + __builtin_popcountl(rightHandState.all);
|
||||
}
|
||||
|
||||
uint8_t RaiseKeyScanner::previousPressedKeyswitchCount() {
|
||||
return __builtin_popcountl(previousLeftHandState.all) + __builtin_popcountl(previousRightHandState.all);
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::setKeyscanInterval(uint8_t interval) {
|
||||
RaiseHands::leftHand.setKeyscanInterval(interval);
|
||||
RaiseHands::rightHand.setKeyscanInterval(interval);
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::setup() {
|
||||
static constexpr uint8_t keyscanner_pins[] = {
|
||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42
|
||||
};
|
||||
for (int i = 0; i < sizeof(keyscanner_pins); i++) {
|
||||
pinMode(keyscanner_pins[i], OUTPUT);
|
||||
digitalWrite(keyscanner_pins[i], LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseKeyScanner::reset() {
|
||||
leftHandState.all = 0;
|
||||
rightHandState.all = 0;
|
||||
kaleidoscope::hid::releaseAllKeys();
|
||||
kaleidoscope::hid::sendKeyboardReport();
|
||||
}
|
||||
|
||||
/********* Hardware plugin *********/
|
||||
void Raise::setup() {
|
||||
RaiseHands::setup();
|
||||
KeyScanner::setup();
|
||||
LEDDriver::setup();
|
||||
|
||||
// initialise Wire of scanner - have to do this here to avoid problem with
|
||||
// static object intialisation ordering
|
||||
Wire.begin();
|
||||
Wire.setClock(I2C_CLOCK_KHZ * 1000);
|
||||
|
||||
RaiseHands::initializeSides();
|
||||
}
|
||||
|
||||
void Raise::side::prepareForFlash() {
|
||||
Wire.end();
|
||||
|
||||
setPower(LOW);
|
||||
// also turn off i2c pins to stop attiny from getting enough current through i2c to stay on
|
||||
pinMode(SCL, OUTPUT);
|
||||
pinMode(SDA, OUTPUT);
|
||||
digitalWrite(SCL, false);
|
||||
digitalWrite(SDA, false);
|
||||
|
||||
// wipe key states, to prevent accidental key repeats
|
||||
RaiseKeyScanner::reset();
|
||||
|
||||
setPower(HIGH);
|
||||
|
||||
Wire.begin();
|
||||
Wire.setClock(I2C_FLASH_CLOCK_KHZ * 1000);
|
||||
// wait for side bootloader to be ready
|
||||
delay(100);
|
||||
}
|
||||
|
||||
uint8_t Raise::side::getPower() {
|
||||
return RaiseHands::getSidePower();
|
||||
}
|
||||
void Raise::side::setPower(uint8_t power) {
|
||||
RaiseHands::setSidePower(power);
|
||||
}
|
||||
|
||||
uint8_t Raise::side::leftVersion() {
|
||||
return RaiseHands::leftHand.readVersion();
|
||||
}
|
||||
uint8_t Raise::side::rightVersion() {
|
||||
return RaiseHands::rightHand.readVersion();
|
||||
}
|
||||
|
||||
uint8_t Raise::side::leftCRCErrors() {
|
||||
return RaiseHands::leftHand.crc_errors();
|
||||
}
|
||||
uint8_t Raise::side::rightCRCErrors() {
|
||||
return RaiseHands::rightHand.crc_errors();
|
||||
}
|
||||
|
||||
uint8_t Raise::side::leftSLEDVersion() {
|
||||
return RaiseHands::leftHand.readSLEDVersion();
|
||||
}
|
||||
uint8_t Raise::side::rightSLEDVersion() {
|
||||
return RaiseHands::rightHand.readSLEDVersion();
|
||||
}
|
||||
|
||||
uint8_t Raise::side::leftSLEDCurrent() {
|
||||
return RaiseHands::leftHand.readSLEDCurrent();
|
||||
}
|
||||
uint8_t Raise::side::rightSLEDCurrent() {
|
||||
return RaiseHands::rightHand.readSLEDCurrent();
|
||||
}
|
||||
void Raise::side::setSLEDCurrent(uint8_t current) {
|
||||
RaiseHands::rightHand.setSLEDCurrent(current);
|
||||
RaiseHands::leftHand.setSLEDCurrent(current);
|
||||
}
|
||||
|
||||
Raise::settings::Layout Raise::settings::layout() {
|
||||
return RaiseHands::layout == LAYOUT_ANSI ? Layout::ANSI : Layout::ISO;
|
||||
}
|
||||
uint8_t Raise::settings::joint() {
|
||||
return RaiseHands::rightHand.readJoint();
|
||||
}
|
||||
|
||||
uint16_t Raise::settings::keyscanInterval() {
|
||||
return RaiseHands::keyscanInterval();
|
||||
}
|
||||
void Raise::settings::keyscanInterval(uint16_t interval) {
|
||||
RaiseHands::keyscanInterval(interval);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,272 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h"
|
||||
#include "kaleidoscope/device/dygma/raise/Hand.h"
|
||||
|
||||
#define CRGB(r,g,b) (cRGB){b, g, r}
|
||||
|
||||
#include "kaleidoscope/driver/keyscanner/Base.h"
|
||||
#include "kaleidoscope/driver/led/Base.h"
|
||||
#include "kaleidoscope/driver/bootloader/samd/Bossac.h"
|
||||
#include "kaleidoscope/driver/storage/Flash.h"
|
||||
#include "kaleidoscope/device/Base.h"
|
||||
#include "kaleidoscope/util/flasher/KeyboardioI2CBootloader.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
|
||||
// LHK = Left Hand Keys
|
||||
#define LHK 33
|
||||
|
||||
using kaleidoscope::driver::led::no_led;
|
||||
|
||||
struct RaiseLEDDriverProps : public kaleidoscope::driver::led::BaseProps {
|
||||
static constexpr uint8_t led_count = 132;
|
||||
static constexpr uint8_t key_led_map[] = {
|
||||
// ISO & ANSI (ANSI has no LED at 20, but this key can never be pressed so we can have just one map).
|
||||
0, 1, 2, 3, 4, 5, 6, no_led, no_led, 6 + LHK, 5 + LHK, 4 + LHK, 3 + LHK, 2 + LHK, 1 + LHK, 0 + LHK,
|
||||
7, 8, 9, 10, 11, 12, no_led, no_led, 14 + LHK, 13 + LHK, 12 + LHK, 11 + LHK, 10 + LHK, 9 + LHK, 8 + LHK, 7 + LHK,
|
||||
13, 14, 15, 16, 17, 18, no_led, no_led, no_led, 21 + LHK, 20 + LHK, 19 + LHK, 18 + LHK, 17 + LHK, 16 + LHK, 15 + LHK,
|
||||
19, 20, 21, 22, 23, 24, 25, no_led, no_led, no_led, 27 + LHK, 26 + LHK, 25 + LHK, 24 + LHK, 23 + LHK, 22 + LHK,
|
||||
26, 27, 28, 29, 30, no_led, 31, 32, 35 + LHK, 34 + LHK, 33 + LHK, 32 + LHK, 31 + LHK, 30 + LHK, 29 + LHK, 28 + LHK
|
||||
};
|
||||
};
|
||||
#undef LHK
|
||||
|
||||
class RaiseLEDDriver : public kaleidoscope::driver::led::Base<RaiseLEDDriverProps> {
|
||||
public:
|
||||
static void setup();
|
||||
|
||||
static void syncLeds();
|
||||
static void setCrgbAt(uint8_t i, cRGB crgb);
|
||||
static cRGB getCrgbAt(uint8_t i);
|
||||
|
||||
static void updateNeuronLED();
|
||||
private:
|
||||
static bool isLEDChangedNeuron;
|
||||
static uint8_t isLEDChangedLeft[LED_BANKS];
|
||||
static uint8_t isLEDChangedRight[LED_BANKS];
|
||||
static cRGB neuronLED;
|
||||
|
||||
static constexpr uint8_t lph = LEDS_PER_HAND;
|
||||
// led_count + 1, to account for the Neuron's LED. The last one is the
|
||||
// Neuron's LED, never send that to SLED.
|
||||
static constexpr uint8_t led_map[][RaiseLEDDriverProps::led_count + 1] = {
|
||||
// ISO
|
||||
{
|
||||
// left side - 33 keys includes LP
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 68, 69,
|
||||
|
||||
// right side - 36 keys includes LP
|
||||
0 + LPH, 1 + LPH, 2 + LPH, 3 + LPH, 4 + LPH, 5 + LPH, 6 + LPH, 7 + LPH, 8 + LPH, 9 + LPH, 10 + LPH, 11 + LPH, 12 + LPH, 13 + LPH, 14 + LPH, 15 + LPH, 16 + LPH, 17 + LPH, 18 + LPH, 19 + LPH,
|
||||
20 + LPH, 21 + LPH, 22 + LPH, 23 + LPH, 24 + LPH, 25 + LPH, 26 + LPH, 27 + LPH, 28 + LPH, 29 + LPH, 30 + LPH, 31 + LPH, 32 + LPH, 33 + LPH, 68 + LPH, 69 + LPH,
|
||||
|
||||
// left under glow - 30
|
||||
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
|
||||
// right underglow - 32
|
||||
34 + LPH, 35 + LPH, 36 + LPH, 37 + LPH, 38 + LPH, 39 + LPH, 40 + LPH, 41 + LPH, 42 + LPH, 43 + LPH, 44 + LPH, 45 + LPH, 46 + LPH, 47 + LPH, 48 + LPH, 49 + LPH, 50 + LPH, 51 + LPH,
|
||||
52 + LPH, 53 + LPH, 54 + LPH, 55 + LPH, 56 + LPH, 57 + LPH, 58 + LPH, 59 + LPH, 60 + LPH, 61 + LPH, 62 + LPH, 63 + LPH, 64 + LPH, 65 + LPH, 0xff
|
||||
},
|
||||
// ANSI
|
||||
{
|
||||
// left side - 32 keys includes LP: key 19 is missing for ANSI layout
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 0xff, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 68, 69,
|
||||
|
||||
// right side - 36 keys includes LP
|
||||
0 + LPH, 1 + LPH, 2 + LPH, 3 + LPH, 4 + LPH, 5 + LPH, 6 + LPH, 15 + LPH, 8 + LPH, 9 + LPH, 10 + LPH, 11 + LPH, 12 + LPH, 13 + LPH, 14 + LPH, 7 + LPH, 16 + LPH, 17 + LPH, 18 + LPH, 19 + LPH,
|
||||
20 + LPH, 21 + LPH, 22 + LPH, 23 + LPH, 24 + LPH, 25 + LPH, 26 + LPH, 27 + LPH, 28 + LPH, 29 + LPH, 30 + LPH, 31 + LPH, 32 + LPH, 33 + LPH, 68 + LPH, 69 + LPH,
|
||||
|
||||
// left under glow - 30
|
||||
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
|
||||
// right underglow - 32
|
||||
34 + LPH, 35 + LPH, 36 + LPH, 37 + LPH, 38 + LPH, 39 + LPH, 40 + LPH, 41 + LPH, 42 + LPH, 43 + LPH, 44 + LPH, 45 + LPH, 46 + LPH, 47 + LPH, 48 + LPH, 49 + LPH, 50 + LPH, 51 + LPH,
|
||||
52 + LPH, 53 + LPH, 54 + LPH, 55 + LPH, 56 + LPH, 57 + LPH, 58 + LPH, 59 + LPH, 60 + LPH, 61 + LPH, 62 + LPH, 63 + LPH, 64 + LPH, 65 + LPH, 0xff
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct RaiseKeyScannerProps : public kaleidoscope::driver::keyscanner::BaseProps {
|
||||
KEYSCANNER_PROPS(5, 16);
|
||||
static constexpr uint8_t left_columns = 8;
|
||||
static constexpr uint8_t right_columns = matrix_columns - left_columns;
|
||||
};
|
||||
|
||||
class RaiseKeyScanner : public kaleidoscope::driver::keyscanner::Base<RaiseKeyScannerProps> {
|
||||
private:
|
||||
typedef RaiseKeyScanner ThisType;
|
||||
typedef RaiseKeyScannerProps Props_;
|
||||
public:
|
||||
static void setup();
|
||||
static void scanMatrix();
|
||||
static void readMatrix();
|
||||
static void actOnMatrixScan();
|
||||
|
||||
static void maskKey(KeyAddr key_addr);
|
||||
static void unMaskKey(KeyAddr key_addr);
|
||||
static bool isKeyMasked(KeyAddr key_addr);
|
||||
static void maskHeldKeys();
|
||||
|
||||
static bool isKeyswitchPressed(KeyAddr key_addr);
|
||||
static uint8_t pressedKeyswitchCount();
|
||||
|
||||
static bool wasKeyswitchPressed(KeyAddr key_addr);
|
||||
static uint8_t previousPressedKeyswitchCount();
|
||||
|
||||
static void setKeyscanInterval(uint8_t interval);
|
||||
|
||||
static void reset();
|
||||
|
||||
protected:
|
||||
static raise::keydata_t leftHandState;
|
||||
static raise::keydata_t rightHandState;
|
||||
static raise::keydata_t previousLeftHandState;
|
||||
static raise::keydata_t previousRightHandState;
|
||||
|
||||
static raise::keydata_t leftHandMask;
|
||||
static raise::keydata_t rightHandMask;
|
||||
|
||||
static bool lastLeftOnline;
|
||||
static bool lastRightOnline;
|
||||
};
|
||||
|
||||
struct RaiseStorageProps : public kaleidoscope::driver::storage::FlashProps {
|
||||
static constexpr uint16_t length = EEPROM_EMULATION_SIZE;
|
||||
};
|
||||
|
||||
struct RaiseSideFlasherProps : public kaleidoscope::util::flasher::BaseProps {};
|
||||
|
||||
struct RaiseProps : kaleidoscope::device::BaseProps {
|
||||
typedef RaiseLEDDriverProps LEDDriverProps;
|
||||
typedef RaiseLEDDriver LEDDriver;
|
||||
typedef RaiseKeyScannerProps KeyScannerProps;
|
||||
typedef RaiseKeyScanner KeyScanner;
|
||||
typedef RaiseStorageProps StorageProps;
|
||||
typedef kaleidoscope::driver::storage::Flash<StorageProps> Storage;
|
||||
typedef kaleidoscope::driver::bootloader::samd::Bossac BootLoader;
|
||||
|
||||
typedef RaiseSideFlasherProps SideFlasherProps;
|
||||
typedef kaleidoscope::util::flasher::KeyboardioI2CBootloader<SideFlasherProps> SideFlasher;
|
||||
};
|
||||
|
||||
class Raise: public kaleidoscope::device::Base<RaiseProps> {
|
||||
private:
|
||||
static RaiseProps::SideFlasher SideFlasher;
|
||||
public:
|
||||
static void setup();
|
||||
|
||||
auto serialPort() -> decltype(SerialUSB) & {
|
||||
return SerialUSB;
|
||||
}
|
||||
|
||||
auto sideFlasher() -> decltype(SideFlasher) & {
|
||||
return SideFlasher;
|
||||
}
|
||||
|
||||
struct side {
|
||||
uint8_t getPower();
|
||||
void setPower(uint8_t power);
|
||||
|
||||
uint8_t leftVersion();
|
||||
uint8_t rightVersion();
|
||||
|
||||
uint8_t leftCRCErrors();
|
||||
uint8_t rightCRCErrors();
|
||||
|
||||
uint8_t leftSLEDVersion();
|
||||
uint8_t rightSLEDVersion();
|
||||
|
||||
uint8_t leftSLEDCurrent();
|
||||
uint8_t rightSLEDCurrent();
|
||||
void setSLEDCurrent(uint8_t current);
|
||||
|
||||
void prepareForFlash();
|
||||
|
||||
// Side bootloader addresses
|
||||
static constexpr uint8_t left_boot_address = 0x50;
|
||||
static constexpr uint8_t right_boot_address = 0x51;
|
||||
} side;
|
||||
|
||||
struct settings {
|
||||
enum class Layout {
|
||||
ISO,
|
||||
ANSI
|
||||
};
|
||||
Layout layout();
|
||||
uint8_t joint();
|
||||
|
||||
uint16_t keyscanInterval();
|
||||
void keyscanInterval(uint16_t interval);
|
||||
} settings;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
typedef kaleidoscope::device::dygma::Raise Device;
|
||||
|
||||
}
|
||||
|
||||
#define PER_KEY_DATA(dflt, \
|
||||
r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \
|
||||
r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \
|
||||
r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c9, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \
|
||||
r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c10, r3c11, r3c12, r3c13, r3c14, r3c15, \
|
||||
r4c0, r4c1, r4c2, r4c3, r4c4, r4c10, r4c11, r4c12, r4c13, r4c14, r4c15, \
|
||||
r4c6, r4c7, r4c8, r4c9 \
|
||||
) \
|
||||
\
|
||||
r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, dflt, dflt, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \
|
||||
r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, dflt, dflt, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \
|
||||
r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, dflt, dflt, dflt, r2c9, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \
|
||||
r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, dflt, dflt, dflt, r3c10, r3c11, r3c12, r3c13, r3c14, r3c15, \
|
||||
r4c0, r4c1, r4c2, r4c3, r4c4, dflt, r4c6, r4c7, r4c8, r4c9, r4c10, r4c11, r4c12, r4c13, r4c14, r4c15
|
||||
|
||||
|
||||
#define PER_KEY_DATA_STACKED(dflt, \
|
||||
r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, \
|
||||
r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, \
|
||||
r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, \
|
||||
r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, \
|
||||
r4c0, r4c1, r4c2, r4c3, r4c4, \
|
||||
r4c6, r4c7, \
|
||||
\
|
||||
r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \
|
||||
r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \
|
||||
r2c9, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \
|
||||
r3c10, r3c11, r3c12, r3c13, r3c14, r3c15, \
|
||||
r4c10, r4c11, r4c12, r4c13, r4c14, r4c15, \
|
||||
r4c8, r4c9 \
|
||||
) \
|
||||
\
|
||||
r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, dflt, dflt, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \
|
||||
r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, dflt, dflt, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \
|
||||
r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, dflt, dflt, dflt, r2c9, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \
|
||||
r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, dflt, dflt, dflt, r3c10, r3c11, r3c12, r3c13, r3c14, r3c15, \
|
||||
r4c0, r4c1, r4c2, r4c3, r4c4, dflt, r4c6, r4c7, r4c8, r4c9, r4c10, r4c11, r4c12, r4c13, r4c14, r4c15
|
||||
|
||||
#endif
|
@ -0,0 +1,133 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-FocusSerial.h>
|
||||
#include "kaleidoscope/device/dygma/raise/Focus.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
#ifndef RAISE_FIRMWARE_VERSION
|
||||
#define RAISE_FIRMWARE_VERSION "<unknown>"
|
||||
#endif
|
||||
|
||||
EventHandlerResult Focus::onFocusEvent(const char *command) {
|
||||
if (::Focus.handleHelp(command, PSTR("hardware.version\nhardware.side_power\nhardware.side_ver\nhardware.sled_ver\nhardware.sled_current\nhardware.layout\nhardware.joint\nhardware.keyscan\nhardware.crc_errors\nhardware.firmware")))
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (strncmp_P(command, PSTR("hardware."), 9) != 0)
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("version")) == 0) {
|
||||
::Focus.send("Dygma Raise");
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("firmware")) == 0) {
|
||||
::Focus.send(RAISE_FIRMWARE_VERSION);
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("side_power")) == 0)
|
||||
if (::Focus.isEOL()) {
|
||||
::Focus.send(Kaleidoscope.device().side.getPower());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
} else {
|
||||
uint8_t power;
|
||||
::Focus.read(power);
|
||||
Kaleidoscope.device().side.setPower(power);
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("side_ver")) == 0) {
|
||||
::Focus.send("left:");
|
||||
::Focus.send(Kaleidoscope.device().side.leftVersion());
|
||||
::Focus.send("\nright:");
|
||||
::Focus.send(Kaleidoscope.device().side.rightVersion());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("crc_errors")) == 0) {
|
||||
::Focus.send("left:");
|
||||
::Focus.send(Kaleidoscope.device().side.leftCRCErrors());
|
||||
::Focus.send("\nright:");
|
||||
::Focus.send(Kaleidoscope.device().side.rightCRCErrors());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("sled_ver")) == 0) {
|
||||
::Focus.send("left:");
|
||||
::Focus.send(Kaleidoscope.device().side.leftSLEDVersion());
|
||||
::Focus.send("\nright:");
|
||||
::Focus.send(Kaleidoscope.device().side.rightSLEDVersion());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("sled_current")) == 0)
|
||||
if (::Focus.isEOL()) {
|
||||
::Focus.send("left:");
|
||||
::Focus.send(Kaleidoscope.device().side.leftSLEDCurrent());
|
||||
::Focus.send("\nright:");
|
||||
::Focus.send(Kaleidoscope.device().side.rightSLEDCurrent());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
} else {
|
||||
uint8_t current;
|
||||
::Focus.read(current);
|
||||
Kaleidoscope.device().side.setSLEDCurrent(current);
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("layout")) == 0) {
|
||||
static const auto ANSI = Kaleidoscope.device().settings.Layout::ANSI;
|
||||
::Focus.send(Kaleidoscope.device().settings.layout() == ANSI ? "ANSI" : "ISO");
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("joint")) == 0) {
|
||||
::Focus.send(Kaleidoscope.device().settings.joint());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("keyscan")) == 0) {
|
||||
if (::Focus.isEOL()) {
|
||||
::Focus.send(Kaleidoscope.device().settings.keyscanInterval());
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
} else {
|
||||
uint8_t keyscan;
|
||||
::Focus.read(keyscan);
|
||||
Kaleidoscope.device().settings.keyscanInterval(keyscan);
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
}
|
||||
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kaleidoscope::device::dygma::raise::Focus RaiseFocus;
|
||||
|
||||
#endif
|
@ -0,0 +1,42 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
class Focus : public kaleidoscope::Plugin {
|
||||
public:
|
||||
EventHandlerResult onFocusEvent(const char *command);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern kaleidoscope::device::dygma::raise::Focus RaiseFocus;
|
||||
|
||||
#endif
|
@ -0,0 +1,254 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Hand.h"
|
||||
|
||||
#include "kaleidoscope/driver/color/GammaCorrection.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
#define TWI_CMD_NONE 0x00
|
||||
#define TWI_CMD_VERSION 0x01
|
||||
#define TWI_CMD_KEYSCAN_INTERVAL 0x02
|
||||
#define TWI_CMD_LED_SET_ALL_TO 0x03
|
||||
#define TWI_CMD_LED_SET_ONE_TO 0x04
|
||||
#define TWI_CMD_COLS_USE_PULLUPS 0x05
|
||||
#define TWI_CMD_LED_SPI_FREQUENCY 0x06
|
||||
#define TWI_CMD_LED_GLOBAL_BRIGHTNESS 0x07
|
||||
|
||||
#define TWI_CMD_SLED_STATUS 0x08
|
||||
#define TWI_CMD_LED_OPEN 0x09
|
||||
#define TWI_CMD_LED_SHORT 0x0A
|
||||
#define TWI_CMD_JOINED 0x0B
|
||||
#define TWI_CMD_LAYOUT 0x0C
|
||||
#define TWI_CMD_SLED_CURRENT 0x0D
|
||||
#define TWI_CMD_SLED_SELF_TEST 0x0E
|
||||
|
||||
#define LED_SPI_FREQUENCY_4MHZ 0x07
|
||||
#define LED_SPI_FREQUENCY_2MHZ 0x06
|
||||
#define LED_SPI_FREQUENCY_1MHZ 0x05
|
||||
#define LED_SPI_FREQUENCY_512KHZ 0x04
|
||||
#define LED_SPI_FREQUENCY_256KHZ 0x03
|
||||
#define LED_SPI_FREQUENCY_128KHZ 0x02
|
||||
#define LED_SPI_FREQUENCY_64KHZ 0x01
|
||||
#define LED_SPI_OFF 0x00
|
||||
|
||||
// 512KHZ seems to be the sweet spot in early testing
|
||||
// so make it the default
|
||||
#define LED_SPI_FREQUENCY_DEFAULT LED_SPI_FREQUENCY_512KHZ
|
||||
|
||||
#define TWI_CMD_LED_BASE 0x80
|
||||
|
||||
#define TWI_REPLY_NONE 0x00
|
||||
#define TWI_REPLY_KEYDATA 0x01
|
||||
|
||||
#define ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
// Returns the relative controller addresss. The expected range is 0-3
|
||||
uint8_t Hand::controllerAddress() {
|
||||
return ad01_;
|
||||
}
|
||||
|
||||
// Sets the keyscan interval. We currently do three reads.
|
||||
// before declaring a key event debounced.
|
||||
//
|
||||
// Takes an integer value representing a counter.
|
||||
//
|
||||
// 0 - 0.1-0.25ms
|
||||
// 1 - 0.125ms
|
||||
// 10 - 0.35ms
|
||||
// 25 - 0.8ms
|
||||
// 50 - 1.6ms
|
||||
// 100 - 3.15ms
|
||||
//
|
||||
// You should think of this as the _minimum_ keyscan interval.
|
||||
// LED updates can cause a bit of jitter.
|
||||
//
|
||||
// returns the Wire.endTransmission code (0 = success)
|
||||
// https://www.arduino.cc/en/Reference/WireEndTransmission
|
||||
byte Hand::setKeyscanInterval(byte delay) {
|
||||
uint8_t data[] = {TWI_CMD_KEYSCAN_INTERVAL, delay};
|
||||
return twi_.writeTo(data, ELEMENTS(data));
|
||||
}
|
||||
|
||||
// returns -1 on error, otherwise returns the scanner version integer
|
||||
int Hand::readVersion() {
|
||||
return readRegister(TWI_CMD_VERSION);
|
||||
}
|
||||
|
||||
// returns -1 on error, otherwise returns the sled version integer
|
||||
int Hand::readSLEDVersion() {
|
||||
return readRegister(TWI_CMD_SLED_STATUS);
|
||||
}
|
||||
// returns -1 on error, otherwise returns the sled current settings
|
||||
int Hand::readSLEDCurrent() {
|
||||
return readRegister(TWI_CMD_SLED_CURRENT);
|
||||
}
|
||||
|
||||
byte Hand::setSLEDCurrent(byte current) {
|
||||
uint8_t data[] = {TWI_CMD_SLED_CURRENT, current};
|
||||
return twi_.writeTo(data, ELEMENTS(data));
|
||||
}
|
||||
|
||||
// returns -1 on error, otherwise returns the scanner keyscan interval
|
||||
int Hand::readKeyscanInterval() {
|
||||
return readRegister(TWI_CMD_KEYSCAN_INTERVAL);
|
||||
}
|
||||
|
||||
// returns -1 on error, otherwise returns the layout (ANSI/ISO) setting
|
||||
int Hand::readLayout() {
|
||||
return readRegister(TWI_CMD_LAYOUT);
|
||||
}
|
||||
|
||||
// returns -1 on error, otherwise returns the LED SPI Frequncy
|
||||
int Hand::readLEDSPIFrequency() {
|
||||
return readRegister(TWI_CMD_LED_SPI_FREQUENCY);
|
||||
}
|
||||
|
||||
// Set the LED SPI Frequency. See wire-protocol-constants.h for
|
||||
// values.
|
||||
//
|
||||
// returns the Wire.endTransmission code (0 = success)
|
||||
// https://www.arduino.cc/en/Reference/WireEndTransmission
|
||||
byte Hand::setLEDSPIFrequency(byte frequency) {
|
||||
uint8_t data[] = {TWI_CMD_LED_SPI_FREQUENCY, frequency};
|
||||
return twi_.writeTo(data, ELEMENTS(data));
|
||||
}
|
||||
|
||||
// returns -1 on error, otherwise returns the value of the hall sensor integer
|
||||
int Hand::readJoint() {
|
||||
byte return_value = 0;
|
||||
|
||||
uint8_t data[] = {TWI_CMD_JOINED};
|
||||
uint8_t result = twi_.writeTo(data, ELEMENTS(data));
|
||||
if (result != 0)
|
||||
return -1;
|
||||
|
||||
// needs to be long enough for the slave to respond
|
||||
delayMicroseconds(40);
|
||||
|
||||
uint8_t rxBuffer[2];
|
||||
|
||||
// perform blocking read into buffer
|
||||
uint8_t read = twi_.readFrom(rxBuffer, ELEMENTS(rxBuffer));
|
||||
if (read == 2) {
|
||||
return rxBuffer[0] + (rxBuffer[1] << 8);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int Hand::readRegister(uint8_t cmd) {
|
||||
byte return_value = 0;
|
||||
|
||||
uint8_t data[] = {cmd};
|
||||
uint8_t result = twi_.writeTo(data, ELEMENTS(data));
|
||||
if (result != 0)
|
||||
return -1;
|
||||
|
||||
// needs to be long enough for the slave to respond
|
||||
delayMicroseconds(40);
|
||||
|
||||
uint8_t rxBuffer[1];
|
||||
|
||||
// perform blocking read into buffer
|
||||
uint8_t read = twi_.readFrom(rxBuffer, ELEMENTS(rxBuffer));
|
||||
if (read > 0) {
|
||||
return rxBuffer[0];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// gives information on the key that was just pressed or released.
|
||||
bool Hand::readKeys() {
|
||||
uint8_t rxBuffer[6] = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
// perform blocking read into buffer
|
||||
uint8_t result = twi_.readFrom(rxBuffer, ELEMENTS(rxBuffer));
|
||||
// if result isn't 6? this can happens if slave nacks while trying to read
|
||||
Hand::online = (result == 6) ? true : false;
|
||||
|
||||
if (result != 6)
|
||||
// could also try reset pressed keys here
|
||||
return false;
|
||||
|
||||
if (rxBuffer[0] == TWI_REPLY_KEYDATA) {
|
||||
key_data_.rows[0] = rxBuffer[1];
|
||||
key_data_.rows[1] = rxBuffer[2];
|
||||
key_data_.rows[2] = rxBuffer[3];
|
||||
key_data_.rows[3] = rxBuffer[4];
|
||||
key_data_.rows[4] = rxBuffer[5];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
keydata_t Hand::getKeyData() {
|
||||
return key_data_;
|
||||
}
|
||||
|
||||
void Hand::sendLEDData() {
|
||||
sendLEDBank(next_led_bank_++);
|
||||
if (next_led_bank_ == LED_BANKS) {
|
||||
next_led_bank_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto constexpr gamma8 = kaleidoscope::driver::color::gamma_correction;
|
||||
|
||||
void Hand::sendLEDBank(uint8_t bank) {
|
||||
uint8_t data[LED_BYTES_PER_BANK + 1]; // + 1 for the update LED command itself
|
||||
data[0] = TWI_CMD_LED_BASE + bank;
|
||||
for (uint8_t i = 0 ; i < LED_BYTES_PER_BANK; i++) {
|
||||
data[i + 1] = pgm_read_byte(&gamma8[led_data.bytes[bank][i]]);
|
||||
}
|
||||
uint8_t result = twi_.writeTo(data, ELEMENTS(data));
|
||||
}
|
||||
|
||||
void Hand::setAllLEDsTo(cRGB color) {
|
||||
uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO,
|
||||
pgm_read_byte(&gamma8[color.r]),
|
||||
pgm_read_byte(&gamma8[color.g]),
|
||||
pgm_read_byte(&gamma8[color.b])
|
||||
};
|
||||
twi_.writeTo(data, ELEMENTS(data));
|
||||
}
|
||||
|
||||
void Hand::setOneLEDTo(byte led, cRGB color) {
|
||||
uint8_t data[] = {TWI_CMD_LED_SET_ONE_TO,
|
||||
led,
|
||||
pgm_read_byte(&gamma8[color.r]),
|
||||
pgm_read_byte(&gamma8[color.g]),
|
||||
pgm_read_byte(&gamma8[color.b])
|
||||
};
|
||||
twi_.writeTo(data, ELEMENTS(data));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,105 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "TWI.h"
|
||||
|
||||
struct cRGB {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
#define LED_BANKS 9
|
||||
|
||||
#define LEDS_PER_HAND 72
|
||||
#define LPH LEDS_PER_HAND
|
||||
#define LEDS_PER_BANK 8
|
||||
#define LED_BYTES_PER_BANK (sizeof(cRGB) * LEDS_PER_BANK)
|
||||
|
||||
typedef union {
|
||||
cRGB leds[LEDS_PER_HAND];
|
||||
byte bytes[LED_BANKS][LED_BYTES_PER_BANK];
|
||||
} LEDData_t;
|
||||
|
||||
// return what bank the led is in
|
||||
#define LED_TO_BANK(led) (led / LEDS_PER_BANK)
|
||||
|
||||
typedef union {
|
||||
uint8_t rows[5];
|
||||
uint64_t all;
|
||||
} keydata_t;
|
||||
|
||||
class Hand {
|
||||
public:
|
||||
Hand(byte ad01) : ad01_(ad01), twi_(i2c_addr_base_ | ad01) {}
|
||||
|
||||
int readVersion();
|
||||
int readSLEDVersion();
|
||||
int readSLEDCurrent();
|
||||
byte setSLEDCurrent(byte current);
|
||||
int readJoint();
|
||||
int readLayout();
|
||||
|
||||
byte setKeyscanInterval(byte delay);
|
||||
int readKeyscanInterval();
|
||||
|
||||
byte setLEDSPIFrequency(byte frequency);
|
||||
int readLEDSPIFrequency();
|
||||
|
||||
bool moreKeysWaiting();
|
||||
void sendLEDData();
|
||||
void sendLEDBank(uint8_t bank);
|
||||
void setOneLEDTo(byte led, cRGB color);
|
||||
void setAllLEDsTo(cRGB color);
|
||||
keydata_t getKeyData();
|
||||
bool readKeys();
|
||||
uint8_t controllerAddress();
|
||||
uint8_t crc_errors() {
|
||||
return twi_.crc_errors();
|
||||
}
|
||||
|
||||
LEDData_t led_data;
|
||||
bool online = false;
|
||||
|
||||
private:
|
||||
int ad01_;
|
||||
TWI twi_;
|
||||
keydata_t key_data_;
|
||||
uint8_t next_led_bank_ = 0;
|
||||
|
||||
static constexpr uint8_t i2c_addr_base_ = 0x58;
|
||||
|
||||
int readRegister(uint8_t cmd);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,84 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
template <typename _Firmware>
|
||||
class SideFlash : public kaleidoscope::Plugin {
|
||||
private:
|
||||
_Firmware firmware;
|
||||
public:
|
||||
EventHandlerResult onFocusEvent(const char *command) {
|
||||
if (::Focus.handleHelp(command, PSTR("hardware.flash_left_side\nhardware.flash_right_side\nhardware.verify_left_side\nhardware.verify_right_side")))
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (strncmp_P(command, PSTR("hardware."), 9) != 0)
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
auto sideFlasher = Kaleidoscope.device().sideFlasher();
|
||||
uint8_t left_boot_address = Kaleidoscope.device().side.left_boot_address;
|
||||
uint8_t right_boot_address = Kaleidoscope.device().side.right_boot_address;
|
||||
enum {
|
||||
FLASH,
|
||||
VERIFY
|
||||
} sub_command;
|
||||
uint8_t address = 0;
|
||||
|
||||
if (strcmp_P(command + 9, PSTR("flash_left_side")) == 0) {
|
||||
sub_command = FLASH;
|
||||
address = left_boot_address;
|
||||
} else if (strcmp_P(command + 9, PSTR("flash_right_side")) == 0) {
|
||||
sub_command = FLASH;
|
||||
address = right_boot_address;
|
||||
} else if (strcmp_P(command + 9, PSTR("verify_left_side")) == 0) {
|
||||
sub_command = VERIFY;
|
||||
address = left_boot_address;
|
||||
} else if (strcmp_P(command + 9, PSTR("verify_right_side")) == 0) {
|
||||
sub_command = VERIFY;
|
||||
address = right_boot_address;
|
||||
} else {
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
bool result;
|
||||
Kaleidoscope.device().side.prepareForFlash();
|
||||
if (sub_command == FLASH)
|
||||
result = sideFlasher.flash(address, firmware);
|
||||
else
|
||||
result = sideFlasher.verify(address, firmware);
|
||||
::Focus.send(result);
|
||||
|
||||
return EventHandlerResult::EVENT_CONSUMED;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,99 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#include "TWI.h"
|
||||
#include "kaleidoscope/util/crc16.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
uint8_t TWI::writeTo(uint8_t *data, size_t length) {
|
||||
Wire.beginTransmission(addr_);
|
||||
|
||||
// calc cksum
|
||||
uint16_t crc16 = 0xffff;
|
||||
uint8_t *buffer = data;
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
crc16 = _crc_ccitt_update(crc16, *buffer);
|
||||
buffer++;
|
||||
}
|
||||
|
||||
// make cksum high byte and low byte
|
||||
uint8_t crc_bytes[2];
|
||||
crc_bytes[0] = crc16 >> 8;
|
||||
crc_bytes[1] = crc16;
|
||||
|
||||
if (!Wire.write(data, length)) return 1;
|
||||
if (!Wire.write(crc_bytes, 2)) return 1;
|
||||
if (Wire.endTransmission(true) != 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t TWI::readFrom(uint8_t* data, size_t length) {
|
||||
uint8_t counter = 0;
|
||||
uint32_t timeout;
|
||||
uint8_t *buffer = data;
|
||||
|
||||
if (!Wire.requestFrom(addr_, length + 2, true)) { // + 2 for the cksum
|
||||
// in case slave is not responding - return 0 (0 length of received data).
|
||||
return 0;
|
||||
}
|
||||
while (counter < length) {
|
||||
*data = Wire.read();
|
||||
data++;
|
||||
counter++;
|
||||
}
|
||||
|
||||
uint16_t crc16 = 0xffff;
|
||||
uint16_t rx_cksum = (Wire.read() << 8) + Wire.read();
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
crc16 = _crc_ccitt_update(crc16, *buffer);
|
||||
buffer++;
|
||||
}
|
||||
|
||||
// check received CRC16
|
||||
if (crc16 != rx_cksum) {
|
||||
crc_errors_++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void TWI::disable() {
|
||||
Wire.end();
|
||||
}
|
||||
|
||||
void TWI::init(uint16_t clock_khz) {
|
||||
Wire.begin();
|
||||
Wire.setClock(clock_khz * 1000);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,49 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::device::dygma::Raise -- Kaleidoscope device plugin for Dygma Raise
|
||||
* Copyright (C) 2017-2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2017-2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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_SAMD_RAISE
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace device {
|
||||
namespace dygma {
|
||||
namespace raise {
|
||||
|
||||
class TWI {
|
||||
public:
|
||||
TWI(int addr) : addr_(addr), crc_errors_(0) {};
|
||||
|
||||
uint8_t writeTo(uint8_t *data, size_t length);
|
||||
uint8_t readFrom(uint8_t* data, size_t length);
|
||||
void disable();
|
||||
void init(uint16_t clock_khz);
|
||||
uint8_t crc_errors() {
|
||||
return crc_errors_;
|
||||
}
|
||||
|
||||
private:
|
||||
int addr_;
|
||||
uint8_t crc_errors_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,47 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::driver::led::Gamma -- Gamma correction table
|
||||
* Copyright (C) 2017-2019 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, version 3.
|
||||
*
|
||||
* 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 <Arduino.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace driver {
|
||||
namespace color {
|
||||
|
||||
const uint8_t PROGMEM gamma_correction[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||||
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
|
||||
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
|
||||
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
|
||||
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
|
||||
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
|
||||
115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
|
||||
144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
|
||||
177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
|
||||
215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
// CRC compatibility, adapted from the C-only comments here:
|
||||
// http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/include/util/crc16.h?revision=933&root=avr-libc&view=markup
|
||||
|
||||
/* Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#ifndef _UTIL_CRC16_H_
|
||||
#define _UTIL_CRC16_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint16_t _crc16_update(uint16_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint16_t _crc16_update(uint16_t crc, uint8_t data) {
|
||||
unsigned int i;
|
||||
|
||||
crc ^= data;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (crc & 1) {
|
||||
crc = (crc >> 1) ^ 0xA001;
|
||||
} else {
|
||||
crc = (crc >> 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static inline uint16_t _crc_xmodem_update(uint16_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint16_t _crc_xmodem_update(uint16_t crc, uint8_t data) {
|
||||
unsigned int i;
|
||||
|
||||
crc = crc ^ ((uint16_t)data << 8);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static inline uint16_t _crc_ccitt_update(uint16_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint16_t _crc_ccitt_update(uint16_t crc, uint8_t data) {
|
||||
data ^= (crc & 255);
|
||||
data ^= data << 4;
|
||||
|
||||
return ((((uint16_t)data << 8) | (crc >> 8)) ^ (uint8_t)(data >> 4)
|
||||
^ ((uint16_t)data << 3));
|
||||
}
|
||||
|
||||
static inline uint8_t _crc_ibutton_update(uint8_t crc, uint8_t data) __attribute__((always_inline, unused));
|
||||
static inline uint8_t _crc_ibutton_update(uint8_t crc, uint8_t data) {
|
||||
unsigned int i;
|
||||
|
||||
crc = crc ^ data;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (crc & 0x01) {
|
||||
crc = (crc >> 1) ^ 0x8C;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,60 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::util::flasher::Base -- Base flasher utility class
|
||||
* Copyright (C) 2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace util {
|
||||
namespace flasher {
|
||||
|
||||
struct BaseProps {
|
||||
static constexpr uint8_t page_size = 64;
|
||||
static constexpr uint8_t frame_size = 16;
|
||||
static constexpr uint8_t blank = 0xff;
|
||||
static constexpr uint8_t delay = 1;
|
||||
|
||||
static struct {
|
||||
static constexpr uint8_t page_address = 0x01;
|
||||
static constexpr uint8_t continue_page = 0x02;
|
||||
static constexpr uint8_t execute = 0x03;
|
||||
static constexpr uint8_t erase_program = 0x04;
|
||||
static constexpr uint8_t get_version_and_crc = 0x06;
|
||||
} command;
|
||||
};
|
||||
|
||||
template <typename _Props>
|
||||
class Base {
|
||||
public:
|
||||
Base() {}
|
||||
|
||||
template <typename T>
|
||||
static uint8_t flash(uint8_t address, T& firmware) {
|
||||
return 0;
|
||||
}
|
||||
template <typename T>
|
||||
static uint8_t verify(uint8_t address, T& firmware) {
|
||||
return 0;
|
||||
}
|
||||
static uint8_t command(uint8_t address, uint8_t command) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* kaleidoscope::util::flasher::KeyboardioI2CBootloader -- Flasher for Keyboardio's I2C Bootloader
|
||||
* Copyright (C) 2019 Keyboard.io, Inc
|
||||
* Copyright (C) 2019 Dygma Lab S.L.
|
||||
*
|
||||
* 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, version 3.
|
||||
*
|
||||
* 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
|
||||
|
||||
// TODO(@algernon): We should support AVR here, too.
|
||||
#ifdef __SAMD21G18A__
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#include "kaleidoscope/util/flasher/Base.h"
|
||||
#include "kaleidoscope/util/crc16.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace util {
|
||||
namespace flasher {
|
||||
|
||||
template <typename _Props>
|
||||
class KeyboardioI2CBootloader: kaleidoscope::util::flasher::Base<_Props> {
|
||||
public:
|
||||
template <typename T>
|
||||
static bool flash(uint8_t address, T& firmware) {
|
||||
if (!verify(address, firmware)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!erase_program(address)) {
|
||||
return false;
|
||||
}
|
||||
if (!write_firmware(address, firmware)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_firmware(address, firmware)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return command(address, _Props::command.execute) == 0 ? true : false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool verify(uint8_t address, T& firmware) {
|
||||
CRCAndVersion crc_and_version = get_version(address, firmware);
|
||||
return (crc_and_version.version != 0xff) && (crc_and_version.crc != 0xffff);
|
||||
}
|
||||
|
||||
static uint8_t command(uint8_t address, uint8_t command) {
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(command);
|
||||
Wire.write(0x00);
|
||||
uint8_t result = Wire.endTransmission();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
struct CRCAndVersion {
|
||||
uint8_t version;
|
||||
uint16_t crc;
|
||||
};
|
||||
|
||||
static uint8_t read_crc16(uint8_t addr,
|
||||
CRCAndVersion *crc_and_version,
|
||||
uint16_t offset, uint16_t length) {
|
||||
uint8_t result;
|
||||
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(_Props::command.get_version_and_crc);
|
||||
Wire.write(offset & 0xff); // addr (lo)
|
||||
Wire.write(offset >> 8); // addr (hi)
|
||||
Wire.write(length & 0xff); // len (lo)
|
||||
Wire.write(length >> 8); // len (hi)
|
||||
result = Wire.endTransmission(false);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// wait for cksum to be calculated - takes about 20ms
|
||||
delay(100);
|
||||
|
||||
Wire.requestFrom(addr, 3);
|
||||
uint8_t v = Wire.read();
|
||||
crc_and_version->version = v;
|
||||
if (Wire.available() == 0) {
|
||||
return 0xFF;
|
||||
}
|
||||
uint8_t crc16_lo = Wire.read();
|
||||
if (Wire.available() == 0) {
|
||||
return 0xFF;
|
||||
}
|
||||
uint8_t crc16_hi = Wire.read();
|
||||
while (Wire.available()) {
|
||||
uint8_t c = Wire.read();
|
||||
}
|
||||
crc_and_version->crc = (crc16_hi << 8) | crc16_lo;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static CRCAndVersion get_version(uint8_t addr, T& firmware) {
|
||||
static CRCAndVersion crc_and_version = {0xff, 0xff};
|
||||
|
||||
// This here to resolve some weird I2C startup bug.
|
||||
// Usually in the RHS, get_version fails with the I2C master writing the
|
||||
// address and the CRC request (0x06), the CRC parameters are never written
|
||||
// doing a read first seems to let things settle in a way that allows the
|
||||
// right to respond correctly
|
||||
Wire.requestFrom(addr, (uint8_t) 3);
|
||||
while (Wire.available()) {
|
||||
// throw away the info, as cksum calculation request has yet to be issued.
|
||||
Wire.read();
|
||||
}
|
||||
|
||||
int result = read_crc16(addr, &crc_and_version,
|
||||
firmware.offsets[0] + 4,
|
||||
firmware.length - 4);
|
||||
return crc_and_version;
|
||||
}
|
||||
|
||||
static bool erase_program(uint8_t addr) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(_Props::command.erase_program);
|
||||
uint8_t result = Wire.endTransmission();
|
||||
|
||||
// wait for erase
|
||||
delay(1000);
|
||||
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool write_firmware(uint8_t addr, T& firmware) {
|
||||
uint8_t result;
|
||||
uint8_t o = 0;
|
||||
|
||||
for (uint16_t i = 0; i < firmware.length; i += _Props::page_size) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(_Props::command.page_address);
|
||||
Wire.write(firmware.offsets[o] & 0xff);
|
||||
Wire.write(firmware.offsets[o] >> 8);
|
||||
result = Wire.endTransmission();
|
||||
delay(_Props::delay);
|
||||
|
||||
// got something other than ACK. Start over.
|
||||
if (result != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// transmit each frame separately
|
||||
for (uint8_t frame = 0; frame < _Props::page_size / _Props::frame_size; frame++) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(_Props::command.continue_page);
|
||||
uint16_t crc16 = 0xffff;
|
||||
for (uint8_t j = frame * _Props::frame_size;
|
||||
j < (frame + 1) * _Props::frame_size;
|
||||
j++) {
|
||||
if (i + j < firmware.length) {
|
||||
uint8_t b = pgm_read_byte(&firmware.data[i + j]);
|
||||
Wire.write(b);
|
||||
crc16 = _crc16_update(crc16, b);
|
||||
} else {
|
||||
Wire.write(_Props::blank);
|
||||
crc16 = _crc16_update(crc16, _Props::blank);
|
||||
}
|
||||
}
|
||||
// write the CRC16, little end first
|
||||
Wire.write(crc16 & 0xff);
|
||||
Wire.write(crc16 >> 8);
|
||||
Wire.write(0x00); // dummy end uint8_t
|
||||
result = Wire.endTransmission();
|
||||
// got something other than NACK. Start over.
|
||||
if (result != 3) {
|
||||
return false;
|
||||
}
|
||||
delay(_Props::delay);
|
||||
}
|
||||
o++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool verify_firmware(uint8_t addr, T& firmware) {
|
||||
uint8_t result = 3;
|
||||
CRCAndVersion crc_and_version;
|
||||
|
||||
while (result != 0) {
|
||||
// skip the first 4 uint8_ts, are they were probably overwritten by the
|
||||
// reset vector preservation
|
||||
result = read_crc16(addr, &crc_and_version,
|
||||
firmware.offsets[0] + 4, firmware.length - 4);
|
||||
if (result != 0) {
|
||||
delay(100);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate our own CRC16
|
||||
uint16_t check_crc16 = 0xffff;
|
||||
for (uint16_t i = 4; i < firmware.length; i++) {
|
||||
check_crc16 = _crc16_update(check_crc16, pgm_read_byte(&(firmware.data[i])));
|
||||
}
|
||||
|
||||
return crc_and_version.crc == check_crc16;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in new issue