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;