From 83c5e127ce61fcf3b62baac674890cc2409d271d Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 15 Dec 2020 03:16:30 +0100 Subject: [PATCH] Integrate KeyboardioScanner as a driver In the process of moving towards a single repository for everything Kaleidoscope, integrate KeyboardioScanner as a driver. This is a direct copy of KeyboardioScanner as of 2090cd426cae25b07c0ce3a6b7365b95c21dd87b, renamed and namespaced to fit into Kaleidoscope. Signed-off-by: Gergely Nagy --- LICENSE.MIT | 21 ++ .../device/keyboardio/Model01.cpp | 20 +- src/kaleidoscope/device/keyboardio/Model01.h | 17 +- .../driver/keyboardio/Model01Side.cpp | 214 ++++++++++++++++++ .../driver/keyboardio/Model01Side.h | 106 +++++++++ .../keyboardio/wire-protocol-constants.h | 51 +++++ 6 files changed, 409 insertions(+), 20 deletions(-) create mode 100644 LICENSE.MIT create mode 100644 src/kaleidoscope/driver/keyboardio/Model01Side.cpp create mode 100644 src/kaleidoscope/driver/keyboardio/Model01Side.h create mode 100644 src/kaleidoscope/driver/keyboardio/wire-protocol-constants.h diff --git a/LICENSE.MIT b/LICENSE.MIT new file mode 100644 index 00000000..d50c2ad7 --- /dev/null +++ b/LICENSE.MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2015 Jesse Vincent + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/kaleidoscope/device/keyboardio/Model01.cpp b/src/kaleidoscope/device/keyboardio/Model01.cpp index 522f439d..26473eae 100644 --- a/src/kaleidoscope/device/keyboardio/Model01.cpp +++ b/src/kaleidoscope/device/keyboardio/Model01.cpp @@ -38,14 +38,14 @@ constexpr uint8_t Model01LEDDriverProps::key_led_map[] PROGMEM; /********* Model01Hands *********/ struct Model01Hands { - static KeyboardioScanner leftHand; - static KeyboardioScanner rightHand; + static driver::keyboardio::Model01Side leftHand; + static driver::keyboardio::Model01Side rightHand; static void setup(); }; -KeyboardioScanner Model01Hands::leftHand(0); -KeyboardioScanner Model01Hands::rightHand(3); +driver::keyboardio::Model01Side Model01Hands::leftHand(0); +driver::keyboardio::Model01Side Model01Hands::rightHand(3); void Model01Hands::setup(void) { // This lets the keyboard pull up to 1.6 amps from the host. @@ -135,12 +135,12 @@ boolean Model01LEDDriver::ledPowerFault() { /********* Key scanner *********/ -keydata_t Model01KeyScanner::leftHandState; -keydata_t Model01KeyScanner::rightHandState; -keydata_t Model01KeyScanner::previousLeftHandState; -keydata_t Model01KeyScanner::previousRightHandState; -keydata_t Model01KeyScanner::leftHandMask; -keydata_t Model01KeyScanner::rightHandMask; +driver::keyboardio::keydata_t Model01KeyScanner::leftHandState; +driver::keyboardio::keydata_t Model01KeyScanner::rightHandState; +driver::keyboardio::keydata_t Model01KeyScanner::previousLeftHandState; +driver::keyboardio::keydata_t Model01KeyScanner::previousRightHandState; +driver::keyboardio::keydata_t Model01KeyScanner::leftHandMask; +driver::keyboardio::keydata_t Model01KeyScanner::rightHandMask; void Model01KeyScanner::enableScannerPower(void) { // Turn on power to the LED net diff --git a/src/kaleidoscope/device/keyboardio/Model01.h b/src/kaleidoscope/device/keyboardio/Model01.h index 0fcf0739..81ad8ad9 100644 --- a/src/kaleidoscope/device/keyboardio/Model01.h +++ b/src/kaleidoscope/device/keyboardio/Model01.h @@ -32,13 +32,10 @@ struct cRGB { #include "kaleidoscope/device/ATmega32U4Keyboard.h" #include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/driver/keyboardio/Model01Side.h" #include "kaleidoscope/driver/led/Base.h" #include "kaleidoscope/driver/bootloader/avr/Caterina.h" -#ifndef KALEIDOSCOPE_VIRTUAL_BUILD -#include "KeyboardioScanner.h" -#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD - namespace kaleidoscope { namespace device { namespace keyboardio { @@ -102,13 +99,13 @@ class Model01KeyScanner : public kaleidoscope::driver::keyscanner::Base +#include "Model01Side.h" + +extern "C" { +#include "kaleidoscope/device/keyboardio/twi.h" +} + +#include "kaleidoscope/driver/color/GammaCorrection.h" + +namespace kaleidoscope { +namespace driver { +namespace keyboardio { + +#define SCANNER_I2C_ADDR_BASE 0x58 +#define ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0])) + +uint8_t twi_uninitialized = 1; + +Model01Side::Model01Side(byte setAd01) { + ad01 = setAd01; + addr = SCANNER_I2C_ADDR_BASE | ad01; + if (twi_uninitialized--) { + twi_init(); + } +} + +// Returns the relative controller addresss. The expected range is 0-3 +uint8_t Model01Side::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 Model01Side::setKeyscanInterval(byte delay) { + uint8_t data[] = {TWI_CMD_KEYSCAN_INTERVAL, delay}; + uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + + return result; +} + + + + +// returns -1 on error, otherwise returns the scanner version integer +int Model01Side::readVersion() { + return readRegister(TWI_CMD_VERSION); +} + +// returns -1 on error, otherwise returns the scanner keyscan interval +int Model01Side::readKeyscanInterval() { + return readRegister(TWI_CMD_KEYSCAN_INTERVAL); +} + + +// returns -1 on error, otherwise returns the LED SPI Frequncy +int Model01Side::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 Model01Side::setLEDSPIFrequency(byte frequency) { + uint8_t data[] = {TWI_CMD_LED_SPI_FREQUENCY, frequency}; + uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + + return result; +} + + + +int Model01Side::readRegister(uint8_t cmd) { + + byte return_value = 0; + + uint8_t data[] = {cmd}; + uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + + + + delayMicroseconds(15); // We may be able to drop this in the future + // but will need to verify with correctly + // sized pull-ups on both the left and right + // hands' i2c SDA and SCL lines + + uint8_t rxBuffer[1]; + + // perform blocking read into buffer + uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true); + if (read > 0) { + return rxBuffer[0]; + } else { + return -1; + } + +} + + +// gives information on the key that was just pressed or released. +bool Model01Side::readKeys() { + + uint8_t rxBuffer[5]; + + // perform blocking read into buffer + uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true); + if (rxBuffer[0] == TWI_REPLY_KEYDATA) { + keyData.rows[0] = rxBuffer[1]; + keyData.rows[1] = rxBuffer[2]; + keyData.rows[2] = rxBuffer[3]; + keyData.rows[3] = rxBuffer[4]; + return true; + } else { + return false; + } +} + +keydata_t Model01Side::getKeyData() { + return keyData; +} + +void Model01Side::sendLEDData() { + sendLEDBank(nextLEDBank++); + if (nextLEDBank == LED_BANKS) { + nextLEDBank = 0; + } +} + +auto constexpr gamma8 = kaleidoscope::driver::color::gamma_correction; + +void Model01Side::sendLEDBank(byte bank) { + uint8_t data[LED_BYTES_PER_BANK + 1]; + data[0] = TWI_CMD_LED_BASE + bank; + for (uint8_t i = 0 ; i < LED_BYTES_PER_BANK; i++) { + /* While the ATTiny controller does have a global brightness command, it is + * limited to 32 levels, and those aren't nicely spread out either. For this + * reason, we're doing our own brightness adjustment on this side, because + * that results in a considerably smoother curve. */ + uint8_t c = ledData.bytes[bank][i]; + if (c > brightness_adjustment_) + c -= brightness_adjustment_; + else + c = 0; + + data[i + 1] = pgm_read_byte(&gamma8[c]); + } + uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); +} + +void Model01Side::setAllLEDsTo(cRGB color) { + uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO, + pgm_read_byte(&gamma8[color.b]), + pgm_read_byte(&gamma8[color.g]), + pgm_read_byte(&gamma8[color.r]) + }; + uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); +} + +void Model01Side::setOneLEDTo(byte led, cRGB color) { + uint8_t data[] = {TWI_CMD_LED_SET_ONE_TO, + led, + pgm_read_byte(&gamma8[color.b]), + pgm_read_byte(&gamma8[color.g]), + pgm_read_byte(&gamma8[color.r]) + }; + uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + +} + +} // namespace keyboardio +} // namespace driver +} // namespace kaleidoscope + +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD diff --git a/src/kaleidoscope/driver/keyboardio/Model01Side.h b/src/kaleidoscope/driver/keyboardio/Model01Side.h new file mode 100644 index 00000000..4a550b03 --- /dev/null +++ b/src/kaleidoscope/driver/keyboardio/Model01Side.h @@ -0,0 +1,106 @@ +// -*- mode: c++ -*- +/* kaleidoscope::driver::keyboardio::Model01Side + * Copyright (C) 2015-2020 Keyboard.io, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +namespace kaleidoscope { +namespace driver { +namespace keyboardio { + +#include +#include "wire-protocol-constants.h" + +// We allow cRGB/CRGB to be defined already when this is included. +// +#ifndef CRGB +struct cRGB { + uint8_t b; + uint8_t g; + uint8_t r; +}; +#define CRGB(r,g,b) (cRGB){b, g, r} +#endif + +#define LED_BANKS 4 + +#define LEDS_PER_HAND 32 +#define LED_BYTES_PER_BANK sizeof(cRGB) * LEDS_PER_HAND/LED_BANKS + +typedef union { + cRGB leds[LEDS_PER_HAND]; + byte bytes[LED_BANKS][LED_BYTES_PER_BANK]; +} LEDData_t; + +typedef union { + uint8_t rows[4]; + uint32_t all; +} keydata_t; + +// config options + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD +// used to configure interrupts, configuration for a particular controller +class Model01Side { + public: + explicit Model01Side(byte setAd01); + ~Model01Side() {} + + int readVersion(); + + byte setKeyscanInterval(byte delay); + int readKeyscanInterval(); + + byte setLEDSPIFrequency(byte frequency); + int readLEDSPIFrequency(); + + void sendLEDData(); + void setOneLEDTo(byte led, cRGB color); + void setAllLEDsTo(cRGB color); + keydata_t getKeyData(); + bool readKeys(); + LEDData_t ledData; + uint8_t controllerAddress(); + + void setBrightness(uint8_t brightness) { + brightness_adjustment_ = 255 - brightness; + } + uint8_t getBrightness() { + return 255 - brightness_adjustment_; + } + + private: + uint8_t brightness_adjustment_ = 0; + int addr; + int ad01; + keydata_t keyData; + byte nextLEDBank = 0; + void sendLEDBank(byte bank); + int readRegister(uint8_t cmd); +}; +#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD +class Model01Side; +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +} // namespace keyboardio +} // namespace driver +} // namespace kaleidoscope diff --git a/src/kaleidoscope/driver/keyboardio/wire-protocol-constants.h b/src/kaleidoscope/driver/keyboardio/wire-protocol-constants.h new file mode 100644 index 00000000..67c4ca0b --- /dev/null +++ b/src/kaleidoscope/driver/keyboardio/wire-protocol-constants.h @@ -0,0 +1,51 @@ +/* KeyboardioScanner + * Copyright (C) 2015-2018 Keyboard.io, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#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 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