From d3216aed95b947addb702e49c838f115f1db723d Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Tue, 16 Nov 2021 12:43:17 -0800 Subject: [PATCH] Initial implemetation of Model 100 hardware driver --- .../Devices/Keyboardio/Model100/Model100.ino | 16 +-- .../device/keyboardio/Model100.cpp | 43 ++++--- .../kaleidoscope/device/keyboardio/Model100.h | 25 +++- .../driver/keyboardio/Model100Side.cpp | 117 +++++++++++++----- .../driver/keyboardio/Model100Side.h | 10 +- 5 files changed, 147 insertions(+), 64 deletions(-) diff --git a/examples/Devices/Keyboardio/Model100/Model100.ino b/examples/Devices/Keyboardio/Model100/Model100.ino index 8eb85b8b..70e37798 100644 --- a/examples/Devices/Keyboardio/Model100/Model100.ino +++ b/examples/Devices/Keyboardio/Model100/Model100.ino @@ -21,7 +21,7 @@ #include "Kaleidoscope-EEPROM-Keymap.h" // Support for communicating with the host via a simple Serial protocol -#include "Kaleidoscope-FocusSerial.h" +//#include "Kaleidoscope-FocusSerial.h" // Support for keys that move the mouse #include "Kaleidoscope-MouseKeys.h" @@ -430,16 +430,16 @@ KALEIDOSCOPE_INIT_PLUGINS( // Focus allows bi-directional communication with the host, and is the // interface through which the keymap in EEPROM can be edited. - Focus, + //Focus, // FocusSettingsCommand adds a few Focus commands, intended to aid in // changing some settings of the keyboard, such as the default layer (via the // `settings.defaultLayer` command) - FocusSettingsCommand, + //FocusSettingsCommand, // FocusEEPROMCommand adds a set of Focus commands, which are very helpful in // both debugging, and in backing up one's EEPROM contents. - FocusEEPROMCommand, +// FocusEEPROMCommand, // The boot greeting effect pulses the LED button for 10 seconds after the // keyboard is first connected @@ -530,8 +530,8 @@ void setup() { // We set the brightness of the rainbow effects to 150 (on a scale of 0-255) // This draws more than 500mA, but looks much nicer than a dimmer effect - LEDRainbowEffect.brightness(150); - LEDRainbowWaveEffect.brightness(150); + LEDRainbowEffect.brightness(255); + LEDRainbowWaveEffect.brightness(255); // Set the action key the test mode should listen for to Left Fn HardwareTestMode.setActionKey(R3C6); @@ -551,12 +551,12 @@ void setup() { // one wants to use these layers, just set the default layer to one in EEPROM, // by using the `settings.defaultLayer` Focus command, or by using the // `keymap.onlyCustom` command to use EEPROM layers only. - EEPROMKeymap.setup(5); + //EEPROMKeymap.setup(5); // We need to tell the Colormap plugin how many layers we want to have custom // maps for. To make things simple, we set it to five layers, which is how // many editable layers we have (see above). - ColormapEffect.max_layers(5); + //ColormapEffect.max_layers(5); } /** loop is the second of the standard Arduino sketch functions. diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp index 9ccb38d9..e4c5db78 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp @@ -15,16 +15,16 @@ * this program. If not, see . */ -#ifdef ARDUINO_GD32_keyboardio_model_100 +#ifdef ARDUINO_keyboardio_model_100 #include "Arduino.h" // for PROGMEM #include "kaleidoscope/device/keyboardio/Model100.h" // for Model100LEDDriver... #include "kaleidoscope/key_events.h" #include "kaleidoscope/driver/keyscanner/Base_Impl.h" +#include "Wire.h" + #ifndef KALEIDOSCOPE_VIRTUAL_BUILD -#include -#include #endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD namespace kaleidoscope { @@ -48,14 +48,13 @@ driver::keyboardio::Model100Side Model100Hands::leftHand(0); driver::keyboardio::Model100Side Model100Hands::rightHand(3); void Model100Hands::setup(void) { - // This lets the keyboard pull up to 1.6 amps from the host. - // That violates the USB spec. But it sure is pretty looking - DDRE |= _BV(6); - PORTE &= ~_BV(6); + delay(100); + pinMode(PB9, OUTPUT_OPEN_DRAIN); + digitalWrite(PB9, LOW); + delay(105); // TODO remove this when we remove it from the attiny code + Wire.begin(); + Wire.setClock(400000); - // Set B4, the overcurrent check to an input with an internal pull-up - DDRB &= ~_BV(4); // set bit, input - PORTB &= ~_BV(4); // set bit, enable pull-up resistor } /********* LED Driver *********/ @@ -103,7 +102,6 @@ cRGB Model100LEDDriver::getCrgbAt(uint8_t i) { void Model100LEDDriver::syncLeds() { if (!isLEDChanged) return; - // LED Data is stored in four "banks" for each side // We send it all at once to make it look nicer. // We alternate left and right hands because otherwise @@ -125,10 +123,6 @@ void Model100LEDDriver::syncLeds() { isLEDChanged = false; } -boolean Model100LEDDriver::ledPowerFault() { - // TODO remove - obsolete -} - /********* Key scanner *********/ driver::keyboardio::keydata_t Model100KeyScanner::leftHandState; @@ -137,16 +131,26 @@ driver::keyboardio::keydata_t Model100KeyScanner::previousLeftHandState; driver::keyboardio::keydata_t Model100KeyScanner::previousRightHandState; void Model100KeyScanner::enableScannerPower(void) { +// Turn on the switched 5V network. +// make sure this happens at least 100ms after USB connect +// to satisfy inrush limits + // + pinMode(PB9, OUTPUT_OPEN_DRAIN); + digitalWrite(PB9, LOW); +} + +void Model100KeyScanner::disableScannerPower(void) { // Turn on power to the 5V net // - pinMode(PC13, OUTPUT_OPEN_DRAIN); - digitalWrite(PC13, LOW); + pinMode(PB9, OUTPUT_OPEN_DRAIN); + digitalWrite(PB9, HIGH); } + + void Model100KeyScanner::setup() { - wdt_disable(); - delay(100); enableScannerPower(); + delay(250); } void Model100KeyScanner::readMatrix() { @@ -228,6 +232,7 @@ uint8_t Model100KeyScanner::previousPressedKeyswitchCount() { /********* Hardware plugin *********/ void Model100::setup() { + Model100KeyScanner::setup(); Model100Hands::setup(); kaleidoscope::device::Base::setup(); } diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h index 606f3e93..817f53cd 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h @@ -17,7 +17,11 @@ #pragma once -#ifdef ARDUINO_GD32_keyboardio_model_100 +#ifdef ARDUINO_keyboardio_model_100 + +#ifndef EEPROM_EMULATION_SIZE +#define EEPROM_EMULATION_SIZE 4096 +#endif #include @@ -29,17 +33,24 @@ struct cRGB { uint8_t r; }; -#include "kaleidoscope/device/ATmega32U4Keyboard.h" #include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/driver/storage/GD32Flash.h" #include "kaleidoscope/driver/keyboardio/Model100Side.h" #include "kaleidoscope/driver/led/Base.h" +#include "kaleidoscope/device/Base.h" +#include "kaleidoscope/driver/hid/Keyboardio.h" #include "kaleidoscope/driver/bootloader/gd32/Base.h" namespace kaleidoscope { namespace device { namespace keyboardio { +struct Model100StorageProps: public kaleidoscope::driver::storage::GD32FlashProps { + static constexpr uint16_t length = EEPROM_EMULATION_SIZE; +}; + + struct Model100LEDDriverProps : public kaleidoscope::driver::led::BaseProps { static constexpr uint8_t led_count = 64; static constexpr uint8_t key_led_map[] PROGMEM = { @@ -60,7 +71,6 @@ class Model100LEDDriver : public kaleidoscope::driver::led::Base HID; + typedef Model100LEDDriverProps LEDDriverProps; typedef Model100LEDDriver LEDDriver; typedef Model100KeyScannerProps KeyScannerProps; typedef Model100KeyScanner KeyScanner; + typedef Model100StorageProps StorageProps; + typedef kaleidoscope::driver::storage::GD32Flash Storage; + typedef kaleidoscope::driver::bootloader::gd32::Base BootLoader; static constexpr const char *short_name = "kbio100"; }; diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp index 8d4c6e31..11a48eb6 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp @@ -24,10 +24,8 @@ #include #include "Model100Side.h" - -extern "C" { -#include "kaleidoscope/device/keyboardio/twi.h" -} +#include +#include #include "kaleidoscope/driver/color/GammaCorrection.h" @@ -43,9 +41,7 @@ uint8_t twi_uninitialized = 1; Model100Side::Model100Side(byte setAd01) { ad01 = setAd01; addr = SCANNER_I2C_ADDR_BASE | ad01; - if (twi_uninitialized--) { - twi_init(); - } + markDeviceUnavailable(); } // Returns the relative controller addresss. The expected range is 0-3 @@ -72,8 +68,7 @@ uint8_t Model100Side::controllerAddress() { // https://www.arduino.cc/en/Reference/WireEndTransmission byte Model100Side::setKeyscanInterval(byte delay) { uint8_t data[] = {TWI_CMD_KEYSCAN_INTERVAL, delay}; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); - + uint8_t result = writeData(data, ELEMENTS(data)); return result; } @@ -103,34 +98,84 @@ int Model100Side::readLEDSPIFrequency() { // https://www.arduino.cc/en/Reference/WireEndTransmission byte Model100Side::setLEDSPIFrequency(byte frequency) { uint8_t data[] = {TWI_CMD_LED_SPI_FREQUENCY, frequency}; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); return result; } +// GD32 I2C implements timeouts which will cause a stall when a device does not answer. +// This method will verify that the device is around and ready to talk. +bool Model100Side::isDeviceAvailable() { + return true; + // if the counter is zero, that's the special value that means "we know it's there" + if (unavailable_device_check_countdown_ == 0) { + return true; + } -int Model100Side::readRegister(uint8_t cmd) { + // if the time to check counter is 1, check for the device + + else if (--unavailable_device_check_countdown_ == 0) { + uint8_t wire_result; + Wire.beginTransmission(addr); + wire_result = Wire.endTransmission(); + //if the check succeeds + if (wire_result == 0) { + // unavailable_device_check_countdown_ = 0; // TODO this is already true + return true; + } else { + // set the time to check counter to max + unavailable_device_check_countdown_ = UNAVAILABLE_DEVICE_COUNTDOWN_MAX; + return false; + } + } else { + // we've decremented the counter, but it's not time to probe for the device yet. + return false; + } - byte return_value = 0; +} - uint8_t data[] = {cmd}; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); +void Model100Side::markDeviceUnavailable() { + unavailable_device_check_countdown_ = 1; // We think there was a comms problem. Check on the next cycle +} +uint8_t Model100Side::writeData(uint8_t *data, uint8_t length) { + if (isDeviceAvailable() == false) { + return 1; + } + Wire.beginTransmission(addr); + Wire.write(data, length); + uint8_t result = Wire.endTransmission(); + if (result) { + markDeviceUnavailable(); + } + return result; +} + +int Model100Side::readRegister(uint8_t cmd) { + byte return_value = 0; + uint8_t data[] = {cmd}; + uint8_t result = writeData(data, ELEMENTS(data)); + // If the setup failed, return. This means there was a problem asking for the register + if (result) { + return -1; + } - delayMicroseconds(15); // We may be able to drop this in the future + delayMicroseconds(50); // TODO 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]; + uint8_t rxBuffer[1] = {0}; // perform blocking read into buffer - uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true); - if (read > 0) { - return rxBuffer[0]; + + Wire.requestFrom(addr, 1); // request 1 byte from the keyscanner + if (Wire.available()) { + return Wire.read(); } else { + markDeviceUnavailable(); return -1; } @@ -139,20 +184,28 @@ int Model100Side::readRegister(uint8_t cmd) { // gives information on the key that was just pressed or released. bool Model100Side::readKeys() { + if (isDeviceAvailable() == false) { + return false; + } - uint8_t rxBuffer[5]; - + uint8_t row_counter = 0; // 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 { + uint8_t read = 0; + uint8_t bytes_returned = 0; + bytes_returned = Wire.requestFrom(addr, 5); // request 5 bytes from the keyscanner + if (bytes_returned < 5) { return false; } + if (Wire.available()) { + read = Wire.read(); + if (TWI_REPLY_KEYDATA == read) { + while (Wire.available()) { + keyData.rows[row_counter++] = Wire.read(); + } + return true; + } + } + return false; } keydata_t Model100Side::getKeyData() { @@ -184,7 +237,7 @@ void Model100Side::sendLEDBank(byte bank) { data[i + 1] = pgm_read_byte(&gamma8[c]); } - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); } void Model100Side::setAllLEDsTo(cRGB color) { @@ -193,7 +246,7 @@ void Model100Side::setAllLEDsTo(cRGB color) { pgm_read_byte(&gamma8[color.g]), pgm_read_byte(&gamma8[color.r]) }; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); } void Model100Side::setOneLEDTo(byte led, cRGB color) { @@ -203,7 +256,7 @@ void Model100Side::setOneLEDTo(byte led, cRGB color) { pgm_read_byte(&gamma8[color.g]), pgm_read_byte(&gamma8[color.r]) }; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); } diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h index 1841095b..2d7838e4 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h @@ -78,9 +78,12 @@ class Model100Side { void setAllLEDsTo(cRGB color); keydata_t getKeyData(); bool readKeys(); + LEDData_t ledData; - uint8_t controllerAddress(); + uint8_t controllerAddress(); + bool isDeviceAvailable(); + void markDeviceUnavailable(); void setBrightness(uint8_t brightness) { brightness_adjustment_ = 255 - brightness; } @@ -93,9 +96,14 @@ class Model100Side { int addr; int ad01; keydata_t keyData; + // a value of 0 is "device seen" - anything else is how many cycles before we should + // check for the device + uint16_t unavailable_device_check_countdown_ = 0; + static const uint16_t UNAVAILABLE_DEVICE_COUNTDOWN_MAX = 0x00FFU; byte nextLEDBank = 0; void sendLEDBank(byte bank); int readRegister(uint8_t cmd); + uint8_t writeData(uint8_t* data, uint8_t length); }; #else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD class Model100Side;