diff --git a/src/Kaleidoscope-HardwareTestMode.h b/src/Kaleidoscope-HardwareTestMode.h new file mode 100644 index 00000000..8e7a8811 --- /dev/null +++ b/src/Kaleidoscope-HardwareTestMode.h @@ -0,0 +1,19 @@ +/* Kaleidoscope-Hardware-TestMode - A factory test mode for the Model 01. + * 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 . + */ + +#pragma once + +#include "kaleidoscope/plugin/HardwareTestMode.h" diff --git a/src/kaleidoscope/Hardware.h b/src/kaleidoscope/Hardware.h index 35a303a4..0f275f0d 100644 --- a/src/kaleidoscope/Hardware.h +++ b/src/kaleidoscope/Hardware.h @@ -253,6 +253,40 @@ class Hardware { return 0; } + /** + * Check if a key was pressed at a given position on the previous scan + * + * @param row is the row the key is located at in the matrix. + * @param col is the column the key is located at in the matrix. + * + * @returns true if the key was pressed, false otherwise. + */ + bool wasKeyswitchPressed(byte row, byte col) { + return false; + } + /** + * Check if a key was pressed at a given position on the previous scan. + * + * @param keyIndex is the key index, as calculated by `keyIndex`. + * + * @note Key indexes start at 1, not 0! + * + * @returns true if the key was pressed, false otherwise. + */ + bool wasKeyswitchPressed(uint8_t keyIndex) { + return false; + } + + /** + * Check the number of key switches pressed in the previous scan. + * + * @returns the number of keys pressed. + */ + uint8_t previousPressedKeyswitchCount() { + return 0; + } + + /** @} */ /** @@ -266,6 +300,16 @@ class Hardware { * bring it up into a useful state. */ void setup() {} + + /** + * Method to configure the device for a hardware test mode + * + * Called by the Kaleidoscope Hardware test plugin, this method should + * do any device-specific initialization needed for factory hardware testing + * + */ + void enableHardwareTestMode() {} + /** @} */ }; } diff --git a/src/kaleidoscope/hardware/ATMegaKeyboard.cpp b/src/kaleidoscope/hardware/ATMegaKeyboard.cpp index 724319e8..b3801d2a 100644 --- a/src/kaleidoscope/hardware/ATMegaKeyboard.cpp +++ b/src/kaleidoscope/hardware/ATMegaKeyboard.cpp @@ -85,6 +85,28 @@ bool ATMegaKeyboard::isKeyswitchPressed(uint8_t keyIndex) { keyIndex % KeyboardHardware.matrix_columns); } + +uint8_t ATMegaKeyboard::previousPressedKeyswitchCount() { + uint8_t count = 0; + + for (int8_t r = 0; r < KeyboardHardware.matrix_rows; r++) { + count += __builtin_popcount(KeyboardHardware.previousKeyState_[r]); + } + return count; +} + +bool ATMegaKeyboard::wasKeyswitchPressed(uint8_t row, byte col) { + return (bitRead(KeyboardHardware.previousKeyState_[row], col) != 0); + +} + +bool ATMegaKeyboard::wasKeyswitchPressed(uint8_t keyIndex) { + keyIndex--; + return wasKeyswitchPressed(keyIndex / KeyboardHardware.matrix_columns, + keyIndex % KeyboardHardware.matrix_columns); +} + + void __attribute__((optimize(3))) ATMegaKeyboard::actOnMatrixScan() { for (byte row = 0; row < KeyboardHardware.matrix_rows; row++) { for (byte col = 0; col < KeyboardHardware.matrix_columns; col++) { diff --git a/src/kaleidoscope/hardware/ATMegaKeyboard.h b/src/kaleidoscope/hardware/ATMegaKeyboard.h index 32191883..079ba0c2 100644 --- a/src/kaleidoscope/hardware/ATMegaKeyboard.h +++ b/src/kaleidoscope/hardware/ATMegaKeyboard.h @@ -87,6 +87,10 @@ class ATMegaKeyboard : public kaleidoscope::Hardware { bool isKeyswitchPressed(uint8_t row, byte col); bool isKeyswitchPressed(uint8_t keyIndex); + uint8_t previousPressedKeyswitchCount(); + bool wasKeyswitchPressed(uint8_t row, byte col); + bool wasKeyswitchPressed(uint8_t keyIndex); + void maskKey(byte row, byte col); void unMaskKey(byte row, byte col); bool isKeyMasked(byte row, byte col); diff --git a/src/kaleidoscope/hardware/ez/ErgoDox.cpp b/src/kaleidoscope/hardware/ez/ErgoDox.cpp index b5228780..b7d87a0c 100644 --- a/src/kaleidoscope/hardware/ez/ErgoDox.cpp +++ b/src/kaleidoscope/hardware/ez/ErgoDox.cpp @@ -232,6 +232,24 @@ bool ErgoDox::isKeyswitchPressed(uint8_t keyIndex) { return isKeyswitchPressed(keyIndex / COLS, keyIndex % COLS); } +bool ErgoDox::wasKeyswitchPressed(byte row, byte col) { + return (bitRead(previousKeyState_[row], col) != 0); +} + +bool ErgoDox::wasKeyswitchPressed(uint8_t keyIndex) { + keyIndex--; + return wasKeyswitchPressed(keyIndex / COLS, keyIndex % COLS); +} + +uint8_t ErgoDox::previousPressedKeyswitchCount() { + uint8_t count = 0; + + for (uint8_t r = 0; r < ROWS; r++) { + count += __builtin_popcount(previousKeyState_[r]); + } + return count; +} + uint8_t ErgoDox::pressedKeyswitchCount() { uint8_t count = 0; diff --git a/src/kaleidoscope/hardware/ez/ErgoDox.h b/src/kaleidoscope/hardware/ez/ErgoDox.h index 87249560..ec2da2bb 100644 --- a/src/kaleidoscope/hardware/ez/ErgoDox.h +++ b/src/kaleidoscope/hardware/ez/ErgoDox.h @@ -69,6 +69,10 @@ class ErgoDox : public kaleidoscope::Hardware { bool isKeyswitchPressed(uint8_t keyIndex); uint8_t pressedKeyswitchCount(); + bool wasKeyswitchPressed(byte row, byte col); + bool wasKeyswitchPressed(uint8_t keyIndex); + uint8_t previousPressedKeyswitchCount(); + // ErgoDox-specific stuff void setStatusLED(uint8_t led, bool state = true); void setStatusLEDBrightness(uint8_t led, uint8_t brightness); diff --git a/src/kaleidoscope/hardware/keyboardio/Model01.cpp b/src/kaleidoscope/hardware/keyboardio/Model01.cpp index 0db65458..55a697bc 100644 --- a/src/kaleidoscope/hardware/keyboardio/Model01.cpp +++ b/src/kaleidoscope/hardware/keyboardio/Model01.cpp @@ -85,6 +85,17 @@ void Model01::setup(void) { TWBR = 12; // This is 400mhz, which is the fastest we can drive the ATTiny } +void Model01::enableHardwareTestMode() { + + // Toggle the programming LEDS on + PORTD |= (1 << 5); + PORTB |= (1 << 0); + + // Disable the debouncer on the ATTinys + KeyboardHardware.setKeyscanInterval(2); +} + + void Model01::setCrgbAt(int8_t i, cRGB crgb) { if (i < 0) { @@ -305,10 +316,28 @@ bool Model01::isKeyswitchPressed(uint8_t keyIndex) { return isKeyswitchPressed(keyIndex / COLS, keyIndex % COLS); } + +bool Model01::wasKeyswitchPressed(byte row, byte col) { + if (col <= 7) { + return (bitRead(previousLeftHandState.rows[row], 7 - col) != 0); + } else { + return (bitRead(previousRightHandState.rows[row], 7 - (col - 8)) != 0); + } +} + +bool Model01::wasKeyswitchPressed(uint8_t keyIndex) { + keyIndex--; + return wasKeyswitchPressed(keyIndex / COLS, keyIndex % COLS); +} + uint8_t Model01::pressedKeyswitchCount() { return __builtin_popcountl(leftHandState.all) + __builtin_popcountl(rightHandState.all); } +uint8_t Model01::previousPressedKeyswitchCount() { + return __builtin_popcountl(previousLeftHandState.all) + __builtin_popcountl(previousRightHandState.all); +} + } } } diff --git a/src/kaleidoscope/hardware/keyboardio/Model01.h b/src/kaleidoscope/hardware/keyboardio/Model01.h index 5457f87b..60112673 100644 --- a/src/kaleidoscope/hardware/keyboardio/Model01.h +++ b/src/kaleidoscope/hardware/keyboardio/Model01.h @@ -72,6 +72,12 @@ class Model01 : public kaleidoscope::Hardware { bool isKeyswitchPressed(uint8_t keyIndex); uint8_t pressedKeyswitchCount(); + bool wasKeyswitchPressed(byte row, byte col); + bool wasKeyswitchPressed(uint8_t keyIndex); + uint8_t previousPressedKeyswitchCount(); + + void enableHardwareTestMode(); + keydata_t leftHandState; keydata_t rightHandState; keydata_t previousLeftHandState; diff --git a/src/kaleidoscope/plugin/HardwareTestMode.cpp b/src/kaleidoscope/plugin/HardwareTestMode.cpp new file mode 100644 index 00000000..04aece10 --- /dev/null +++ b/src/kaleidoscope/plugin/HardwareTestMode.cpp @@ -0,0 +1,129 @@ +/* Kaleidoscope-HardwareTestMode - A factory test mode for the Model 01. + * 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 . + */ + +#include "Kaleidoscope.h" +#include "Kaleidoscope-HardwareTestMode.h" +#include "Kaleidoscope-LEDEffect-Rainbow.h" + +namespace kaleidoscope { +namespace plugin { + + +constexpr uint8_t CHATTER_CYCLE_LIMIT = 30; + +uint8_t HardwareTestMode::actionKey; + +void HardwareTestMode::setActionKey(uint8_t key) { + actionKey = key; +} + +void HardwareTestMode::waitForKeypress() { + while (1) { + KeyboardHardware.readMatrix(); + if (KeyboardHardware.isKeyswitchPressed(actionKey) && + ! KeyboardHardware.wasKeyswitchPressed(actionKey)) { + break; + } + } +} + +void HardwareTestMode::setLeds(cRGB color) { + ::LEDControl.set_all_leds_to(color); + ::LEDControl.syncLeds(); + waitForKeypress(); +} + +void HardwareTestMode::testLeds(void) { + constexpr cRGB red = CRGB(201, 0, 0); + constexpr cRGB blue = CRGB(0, 0, 201); + constexpr cRGB green = CRGB(0, 201, 0); + constexpr cRGB brightWhite = CRGB(160, 160, 160); + + setLeds(brightWhite); + setLeds(blue); + setLeds(green); + setLeds(red); + // rainbow for 10 seconds + ::LEDRainbowEffect.update_delay(5); + for (auto i = 0; i < 300; i++) { + ::LEDRainbowEffect.update(); + ::LEDControl.syncLeds(); + } +} + + + +void HardwareTestMode::testMatrix() { + // Reset bad keys from previous tests. + chatter_data state[KeyboardHardware.matrix_columns * KeyboardHardware.matrix_rows] = {0, 0, 0}; + + constexpr cRGB red = CRGB(201, 0, 0); + constexpr cRGB blue = CRGB(0, 0, 201); + constexpr cRGB green = CRGB(0, 201, 0); + constexpr cRGB yellow = CRGB(201, 100, 0); + + while (1) { + KeyboardHardware.readMatrix(); + for (byte row = 0; row < KeyboardHardware.matrix_rows; row++) { + for (byte col = 0; col < KeyboardHardware.matrix_columns; col++) { + uint8_t keynum = (row * KeyboardHardware.matrix_columns) + (col); + + // If the key is toggled on + if (KeyboardHardware.isKeyswitchPressed(row, col) && ! KeyboardHardware.wasKeyswitchPressed(row, col)) { + // And it's too soon (in terms of cycles between changes) + state[keynum].tested = 1; + if (state[keynum].cyclesSinceStateChange < CHATTER_CYCLE_LIMIT) { + state[keynum].bad = 1; + } + state[keynum].cyclesSinceStateChange = 0; + } else if (state[keynum].cyclesSinceStateChange < CHATTER_CYCLE_LIMIT) { + state[keynum].cyclesSinceStateChange++; + } + // If the key is held down + if (KeyboardHardware.isKeyswitchPressed(row, col) && KeyboardHardware.wasKeyswitchPressed(row, col)) { + KeyboardHardware.setCrgbAt(row, col, green); + } + + // If we triggered chatter detection ever on this key + else if (state[keynum].bad == 1) { + KeyboardHardware.setCrgbAt(row, col, red); + } else if (state[keynum].tested == 0) { + KeyboardHardware.setCrgbAt(row, col, yellow); + } + // If the key is not currently pressed and was not just released and is not marked bad + else if (! KeyboardHardware.isKeyswitchPressed(row, col)) { + KeyboardHardware.setCrgbAt(row, col, blue); + } + } + } + ::LEDControl.syncLeds(); + } +} + +void HardwareTestMode::runTests() { + // When we start test mode, we -may- have some keys held, so empty it + // out and send a new report + kaleidoscope::hid::releaseAllKeys(); + kaleidoscope::hid::sendKeyboardReport(); + KeyboardHardware.enableHardwareTestMode(); + testLeds(); + testMatrix(); +} + +} +} + +kaleidoscope::plugin::HardwareTestMode HardwareTestMode; diff --git a/src/kaleidoscope/plugin/HardwareTestMode.h b/src/kaleidoscope/plugin/HardwareTestMode.h new file mode 100644 index 00000000..65ab36e4 --- /dev/null +++ b/src/kaleidoscope/plugin/HardwareTestMode.h @@ -0,0 +1,49 @@ +/* Kaleidoscope-HardwareTestMode - A factory test mode for the Model 01. + * 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 . + */ + +#pragma once + +#include +#include "Kaleidoscope.h" + +namespace kaleidoscope { +namespace plugin { + +class HardwareTestMode : public kaleidoscope::Plugin { + public: + + typedef struct { + uint8_t bad : 1, + tested : 1, + cyclesSinceStateChange: 6; + } chatter_data; + static uint8_t actionKey; + + HardwareTestMode(void) {} + + static void runTests(); + static void setActionKey(uint8_t key); + private: + static void testLeds(); + static void testMatrix(); + static void toggleProgrammingLedsOn(); + static void waitForKeypress(); + static void setLeds(cRGB color); +}; +} +} + +extern kaleidoscope::plugin::HardwareTestMode HardwareTestMode;