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