diff --git a/src/Kaleidoscope-Hardware-Keyboardio-SAMDTestbed.h b/src/Kaleidoscope-Hardware-Keyboardio-SAMDTestbed.h
new file mode 100644
index 00000000..8e083ea1
--- /dev/null
+++ b/src/Kaleidoscope-Hardware-Keyboardio-SAMDTestbed.h
@@ -0,0 +1,21 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-Hardware-Keyboardio-Atreus -- Keyboardio Atreus hardware support for Kaleidoscope
+ * Copyright (C) 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/device/keyboardio/SAMDTestbed.h"
diff --git a/src/kaleidoscope/device/SAMDKeyboard.h b/src/kaleidoscope/device/SAMDKeyboard.h
new file mode 100644
index 00000000..ff1f04d0
--- /dev/null
+++ b/src/kaleidoscope/device/SAMDKeyboard.h
@@ -0,0 +1,58 @@
+/* -*- mode: c++ -*-
+ * device::ATmega32U4Keyboard -- Generic ATmega32U4 keyboard base class
+ * Copyright (C) 2019 Keyboard.io, Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+
+#if defined(__SAMD51__) || defined(__SAMD21__) || defined(KALEIDOSCOPE_VIRTUAL_BUILD)
+
+#include
+#include "kaleidoscope/device/Base.h"
+
+#include "kaleidoscope/driver/mcu/SAMD.h"
+#include "kaleidoscope/driver/keyscanner/SAMD.h"
+#include "kaleidoscope/driver/storage/Flash.h"
+
+namespace kaleidoscope {
+namespace device {
+
+struct SAMDStorageProps : public kaleidoscope::driver::storage::FlashProps {
+ static constexpr uint16_t length = EEPROM_EMULATION_SIZE;
+};
+
+struct SAMDKeyboardProps : kaleidoscope::device::BaseProps {
+ typedef kaleidoscope::driver::mcu::SAMD MCU;
+ typedef SAMDStorageProps StorageProps;
+ typedef kaleidoscope::driver::storage::Flash Storage;
+};
+
+#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+template
+class SAMDKeyboard : public kaleidoscope::device::Base<_DeviceProps> {
+ public:
+ auto serialPort() -> decltype(Serial) & {
+ return Serial;
+ }
+};
+#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+template
+class SAMDKeyboard;
+#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+
+}
+}
+
+#endif
diff --git a/src/kaleidoscope/device/keyboardio/SAMDTestbed.cpp b/src/kaleidoscope/device/keyboardio/SAMDTestbed.cpp
new file mode 100644
index 00000000..39686785
--- /dev/null
+++ b/src/kaleidoscope/device/keyboardio/SAMDTestbed.cpp
@@ -0,0 +1,67 @@
+/* -*- mode: c++ -*-
+ * Keyboardio SAMD Testbed hardware support for Kaleidoscope
+ * Copyright (C) 2019, 2020 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 .
+ */
+
+#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+#ifdef KEYBOARDIO_SAMD_TESTBED
+
+#include "kaleidoscope/Runtime.h"
+#include "kaleidoscope/driver/keyscanner/Base_Impl.h"
+
+
+// Here, we set up aliases to the device's KeyScanner and KeyScannerProps
+// in the global namespace within the scope of this file. We'll use these
+// aliases to simplify some template initialization code below.
+using KeyScannerProps = typename kaleidoscope::device::keyboardio::SAMDTestbedProps::KeyScannerProps;
+using KeyScanner = typename kaleidoscope::device::keyboardio::SAMDTestbedProps::KeyScanner;
+
+namespace kaleidoscope {
+namespace device {
+namespace keyboardio {
+
+// `KeyScannerProps` here refers to the alias set up above. We do not need to
+// prefix the `matrix_rows` and `matrix_columns` names within the array
+// declaration, because those are resolved within the context of the class, so
+// the `matrix_rows` in `KeyScannerProps::matrix_row_pins[matrix_rows]` gets
+// resolved as `KeyScannerProps::matrix_rows`.
+const uint8_t KeyScannerProps::matrix_rows;
+const uint8_t KeyScannerProps::matrix_columns;
+constexpr uint8_t KeyScannerProps::matrix_row_pins[matrix_rows];
+constexpr uint8_t KeyScannerProps::matrix_col_pins[matrix_columns];
+
+// `KeyScanner` here refers to the alias set up above, just like in the
+// `KeyScannerProps` case above.
+template<> KeyScanner::row_state_t KeyScanner::matrix_state_[KeyScannerProps::matrix_rows] = {};
+
+// We set up the TIMER1 interrupt vector here. Due to dependency reasons, this
+// cannot be in a header-only driver, and must be placed here.
+//
+// Timer1 is responsible for setting a property on the KeyScanner, which will
+// tell it to do a scan. We use this to make sure that scans happen at roughly
+// the intervals we want. We do the scan outside of the interrupt scope for
+// practical reasons: guarding every codepath against interrupts that can be
+// reached from the scan is far too tedious, for very little gain.
+ISR(TIMER1_OVF_vect) {
+ Runtime.device().keyScanner().do_scan_ = true;
+}
+
+}
+}
+}
+
+#endif
+#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
diff --git a/src/kaleidoscope/device/keyboardio/SAMDTestbed.h b/src/kaleidoscope/device/keyboardio/SAMDTestbed.h
new file mode 100644
index 00000000..3bb67045
--- /dev/null
+++ b/src/kaleidoscope/device/keyboardio/SAMDTestbed.h
@@ -0,0 +1,97 @@
+/* -*- mode: c++ -*-
+ * Keyboardio SAMD Testbed hardware support for Kaleidoscope
+ * Copyright (C) 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+
+#ifdef KEYBOARDIO_SAMD_TESTBED
+
+#include
+
+#include "kaleidoscope/driver/bootloader/samd/Bossac.h"
+#include "kaleidoscope/device/SAMDKeyboard.h"
+
+namespace kaleidoscope {
+namespace device {
+namespace keyboardio {
+
+
+struct SAMDTestbedProps : kaleidoscope::device::SAMDKeyboardProps {
+ struct KeyScannerProps : public kaleidoscope::driver::keyscanner::SAMDProps {
+
+
+ static constexpr uint8_t matrix_rows = 4;
+ static constexpr uint8_t matrix_columns = 12;
+ typedef MatrixAddr KeyAddr;
+#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+ static constexpr uint8_t matrix_row_pins[matrix_rows] = {PIN_F6, PIN_F5, PIN_F4, PIN_F1};
+ static constexpr uint8_t matrix_col_pins[matrix_columns] = {PIN_F7, PIN_E2, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_D7, PIN_D6, PIN_D4, PIN_D5, PIN_D3, PIN_D2};
+#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+ };
+
+ typedef kaleidoscope::driver::keyscanner::SAMD KeyScanner;
+ typedef kaleidoscope::driver::bootloader::samd::Bossac BootLoader;
+ static constexpr const char *short_name = "testbed";
+};
+
+#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+class SAMDTestbed: public kaleidoscope::device::SAMDKeyboard {};
+#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+/* Device definition omitted for virtual device builds.
+ * We need to forward declare the device name, though, as there are
+ * some legacy extern references to boards whose definition
+ * depends on this.
+ */
+class SAMDTestbed;
+
+#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+
+
+#define PER_KEY_DATA(dflt, \
+ R0C0, R0C1, R0C2, R0C3, R0C4, R0C7, R0C8, R0C9, R0C10, R0C11, \
+ R1C0, R1C1, R1C2, R1C3, R1C4, R1C7, R1C8, R1C9, R1C10, R1C11, \
+ R2C0, R2C1, R2C2, R2C3, R2C4, R2C5, R2C6, R2C7, R2C8, R2C9, R2C10, R2C11, \
+ R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11 \
+ ) \
+ R0C0, R0C1, R0C2, R0C3, R0C4, XXX, XXX, R0C7, R0C8, R0C9, R0C10, R0C11, \
+ R1C0, R1C1, R1C2, R1C3, R1C4, XXX, XXX, R1C7, R1C8, R1C9, R1C10, R1C11, \
+ R2C0, R2C1, R2C2, R2C3, R2C4, R2C5, R2C6, R2C7, R2C8, R2C9, R2C10, R2C11, \
+ R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11
+
+#define PER_KEY_DATA_STACKED(dflt, \
+ R0C0, R0C1, R0C2, R0C3, R0C4, \
+ R1C0, R1C1, R1C2, R1C3, R1C4, \
+ R2C0, R2C1, R2C2, R2C3, R2C4, R2C5, \
+ R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, \
+ \
+ R0C7, R0C8, R0C9, R0C10, R0C11, \
+ R1C7, R1C8, R1C9, R1C10, R1C11, \
+ R2C6, R2C7, R2C8, R2C9, R2C10, R2C11, \
+ R3C6, R3C7, R3C8, R3C9, R3C10, R3C11 \
+ ) \
+ R0C0, R0C1, R0C2, R0C3, R0C4, XXX, XXX, R0C7, R0C8, R0C9, R0C10, R0C11, \
+ R1C0, R1C1, R1C2, R1C3, R1C4, XXX, XXX, R1C7, R1C8, R1C9, R1C10, R1C11, \
+ R2C0, R2C1, R2C2, R2C3, R2C4, R2C5, R2C6, R2C7, R2C8, R2C9, R2C10, R2C11, \
+ R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11
+}
+}
+
+EXPORT_DEVICE(kaleidoscope::device::keyboardio::SAMDTestbed)
+
+}
+
+#endif
diff --git a/src/kaleidoscope/device/samd/pins_and_ports.h b/src/kaleidoscope/device/samd/pins_and_ports.h
new file mode 100644
index 00000000..a3cebf15
--- /dev/null
+++ b/src/kaleidoscope/device/samd/pins_and_ports.h
@@ -0,0 +1,144 @@
+/* This file originated in QMK. https://github.com/qmk/qmk_firmware
+ * quantum/config_common.h 73e634482ea8f57d1f1a5f1e16bc3ffd74f84b8e
+ *
+ * Original copyright statement:
+ *
+ * Copyright 2015-2018 Jack Humbert
+ *
+ * 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+
+#if !defined(__ASSEMBLER__) && !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
+//#include
+#endif
+#define PORT_SHIFTER 4 // this may be 4 for all AVR chips
+
+// If you want to add more to this list, reference the PINx definitions in these header
+// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr
+
+#if defined(__SAMD21__) || defined(__SAMD51__)
+// TODO - these numbers aren't right
+#define ADDRESS_BASE 0x00
+#define PINA_ADDRESS 0x9
+#define PINB_ADDRESS 0x3
+#define PINC_ADDRESS 0x6
+#define PIND_ADDRESS 0x9
+#define PINE_ADDRESS 0xC
+#define PINF_ADDRESS 0xF
+#elif !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
+#error "Pins are not defined"
+#endif
+
+/* I/O pins */
+#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+#define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)
+#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+#define PINDEF(port, pin) 0
+#define PORTA
+#define PORTB
+#define PORTC
+#define PORTD
+#define PORTE
+#define PORTF
+#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+
+#ifdef PORTA
+#define PIN_A0 PINDEF(A, 0)
+#define PIN_A1 PINDEF(A, 1)
+#define PIN_A2 PINDEF(A, 2)
+#define PIN_A3 PINDEF(A, 3)
+#define PIN_A4 PINDEF(A, 4)
+#define PIN_A5 PINDEF(A, 5)
+#define PIN_A6 PINDEF(A, 6)
+#define PIN_A7 PINDEF(A, 7)
+#endif
+#ifdef PORTB
+#define PIN_B0 PINDEF(B, 0)
+#define PIN_B1 PINDEF(B, 1)
+#define PIN_B2 PINDEF(B, 2)
+#define PIN_B3 PINDEF(B, 3)
+#define PIN_B4 PINDEF(B, 4)
+#define PIN_B5 PINDEF(B, 5)
+#define PIN_B6 PINDEF(B, 6)
+#define PIN_B7 PINDEF(B, 7)
+#endif
+#ifdef PORTC
+#define PIN_C0 PINDEF(C, 0)
+#define PIN_C1 PINDEF(C, 1)
+#define PIN_C2 PINDEF(C, 2)
+#define PIN_C3 PINDEF(C, 3)
+#define PIN_C4 PINDEF(C, 4)
+#define PIN_C5 PINDEF(C, 5)
+#define PIN_C6 PINDEF(C, 6)
+#define PIN_C7 PINDEF(C, 7)
+#endif
+#ifdef PORTD
+#define PIN_D0 PINDEF(D, 0)
+#define PIN_D1 PINDEF(D, 1)
+#define PIN_D2 PINDEF(D, 2)
+#define PIN_D3 PINDEF(D, 3)
+#define PIN_D4 PINDEF(D, 4)
+#define PIN_D5 PINDEF(D, 5)
+#define PIN_D6 PINDEF(D, 6)
+#define PIN_D7 PINDEF(D, 7)
+#endif
+#ifdef PORTE
+#define PIN_E0 PINDEF(E, 0)
+#define PIN_E1 PINDEF(E, 1)
+#define PIN_E2 PINDEF(E, 2)
+#define PIN_E3 PINDEF(E, 3)
+#define PIN_E4 PINDEF(E, 4)
+#define PIN_E5 PINDEF(E, 5)
+#define PIN_E6 PINDEF(E, 6)
+#define PIN_E7 PINDEF(E, 7)
+#endif
+#ifdef PORTF
+#define PIN_F0 PINDEF(F, 0)
+#define PIN_F1 PINDEF(F, 1)
+#define PIN_F2 PINDEF(F, 2)
+#define PIN_F3 PINDEF(F, 3)
+#define PIN_F4 PINDEF(F, 4)
+#define PIN_F5 PINDEF(F, 5)
+#define PIN_F6 PINDEF(F, 6)
+#define PIN_F7 PINDEF(F, 7)
+#endif
+
+/* converting pins to ports */
+enum { PIN_OFFSET, DDR_OFFSET, PORT_OFFSET};
+
+#define PIN_ADDRESS_MASK 0xF
+
+#define PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + (p >> PORT_SHIFTER) + offset)
+#define PIN_REG_FOR_PIN(pin) PIN_ADDRESS(pin, PIN_OFFSET )
+#define DDR_REG_FOR_PIN(pin) PIN_ADDRESS(pin, DDR_OFFSET )
+#define PORT_REG_FOR_PIN(pin) PIN_ADDRESS(pin, PORT_OFFSET )
+#define PIN_NUM_FOR_PIN(pin) ( pin & PIN_ADDRESS_MASK )
+#define PIN_MASK_FOR_PIN(pin) _BV(PIN_NUM_FOR_PIN(pin))
+
+#define DDR_INPUT(pin) (DDR_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
+#define DDR_OUTPUT(pin) (DDR_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
+
+
+#define ENABLE_PULLUP(pin) (PORT_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
+#define DISABLE_PULLUP(pin) (PORT_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
+
+
+#define OUTPUT_HIGH(pin) (PORT_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
+#define OUTPUT_LOW(pin) (PORT_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
+#define OUTPUT_TOGGLE(pin) (PORT_REG_FOR_PIN(pin) ^= (PIN_MASK_FOR_PIN(pin)))
+
+#define READ_PIN(pin) (!!(PIN_REG_FOR_PIN(pin) & PIN_MASK_FOR_PIN(pin)))
+
diff --git a/src/kaleidoscope/driver/keyscanner/SAMD.h b/src/kaleidoscope/driver/keyscanner/SAMD.h
new file mode 100644
index 00000000..9beb3852
--- /dev/null
+++ b/src/kaleidoscope/driver/keyscanner/SAMD.h
@@ -0,0 +1,265 @@
+/* -*- mode: c++ -*-
+ * kaleidoscope::driver::keyscanner::SAMD -- Microchip SAMD-based keyscanner component
+ * Copyright (C) 2018-2020 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/macro_helpers.h"
+#include "kaleidoscope/driver/keyscanner/Base.h"
+#include "kaleidoscope/driver/keyscanner/None.h"
+
+#include "kaleidoscope/device/samd/pins_and_ports.h"
+
+namespace kaleidoscope {
+namespace driver {
+namespace keyscanner {
+
+struct SAMDProps: kaleidoscope::driver::keyscanner::BaseProps {
+ static const uint16_t keyscan_interval = 1500;
+ typedef uint16_t RowState;
+
+ /*
+ * The following two lines declare an empty array. Both of these must be
+ * shadowed by the descendant keyscanner description class.
+ */
+ static constexpr uint8_t matrix_row_pins[] = {};
+ static constexpr uint8_t matrix_col_pins[] = {};
+};
+
+
+#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+template
+class SAMD: public kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> {
+ private:
+ typedef SAMD<_KeyScannerProps> ThisType;
+
+ public:
+ void setup() {
+ static_assert(
+ sizeof(_KeyScannerProps::matrix_row_pins) > 0,
+ "The key scanner description has an empty array of matrix row pins."
+ );
+ static_assert(
+ sizeof(_KeyScannerProps::matrix_col_pins) > 0,
+ "The key scanner description has an empty array of matrix column pins."
+ );
+
+ //TODO XXX WDT_Disable(WDT);
+
+ for (uint8_t i = 0; i < _KeyScannerProps::matrix_columns; i++) {
+ DDR_INPUT(_KeyScannerProps::matrix_col_pins[i]);
+ ENABLE_PULLUP(_KeyScannerProps::matrix_col_pins[i]);
+ }
+
+ for (uint8_t i = 0; i < _KeyScannerProps::matrix_rows; i++) {
+ DDR_OUTPUT(_KeyScannerProps::matrix_row_pins[i]);
+ OUTPUT_HIGH(_KeyScannerProps::matrix_row_pins[i]);
+ }
+
+ setScanCycleTime(_KeyScannerProps::keyscan_interval);
+ }
+
+
+ /* setScanCycleTime takes a value of between 0 and 8192. This corresponds (roughly) to the number of microseconds to wait between scanning the key matrix. Our debouncing algorithm does four checks before deciding that a result is valid. Most normal mechanical switches specify a 5ms debounce period. On an ATMega32U4, 1700 gets you about 5ms of debouncing.
+
+ Because keycanning is triggered by an interrupt but not run in that interrupt, the actual amount of time between scans is prone to a little bit of jitter.
+
+ */
+ void setScanCycleTime(uint16_t c) {
+/* TODO
+ TCCR1B = _BV(WGM13);
+ TCCR1A = 0;
+*/
+
+ const uint32_t cycles = (F_CPU / 2000000) * c;
+/* TODO
+ ICR1 = cycles;
+ TCCR1B = _BV(WGM13) | _BV(CS10);
+ TIMSK1 = _BV(TOIE1);
+
+*/
+ }
+
+ __attribute__((optimize(3)))
+ void readMatrix(void) {
+ typename _KeyScannerProps::RowState any_debounced_changes = 0;
+
+ for (uint8_t current_row = 0; current_row < _KeyScannerProps::matrix_rows; current_row++) {
+ OUTPUT_TOGGLE(_KeyScannerProps::matrix_row_pins[current_row]);
+ typename _KeyScannerProps::RowState hot_pins = readCols();
+
+ OUTPUT_TOGGLE(_KeyScannerProps::matrix_row_pins[current_row]);
+
+ any_debounced_changes |= debounce(hot_pins, &matrix_state_[current_row].debouncer);
+
+ if (any_debounced_changes) {
+ for (uint8_t current_row = 0; current_row < _KeyScannerProps::matrix_rows; current_row++) {
+ matrix_state_[current_row].current = matrix_state_[current_row].debouncer.debounced_state;
+ }
+ }
+ }
+ }
+ void scanMatrix() {
+ if (do_scan_) {
+ do_scan_ = false;
+ readMatrix();
+ }
+ actOnMatrixScan();
+ }
+
+ void __attribute__((optimize(3))) actOnMatrixScan() {
+ for (byte row = 0; row < _KeyScannerProps::matrix_rows; row++) {
+ for (byte col = 0; col < _KeyScannerProps::matrix_columns; col++) {
+ uint8_t keyState = (bitRead(matrix_state_[row].previous, col) << 0) | (bitRead(matrix_state_[row].current, col) << 1);
+ if (keyState) {
+ ThisType::handleKeyswitchEvent(Key_NoKey, typename _KeyScannerProps::KeyAddr(row, col), keyState);
+ }
+ }
+ matrix_state_[row].previous = matrix_state_[row].current;
+ }
+ }
+
+ uint8_t pressedKeyswitchCount() {
+ uint8_t count = 0;
+
+ for (int8_t r = 0; r < _KeyScannerProps::matrix_rows; r++) {
+ count += __builtin_popcount(matrix_state_[r].current);
+ }
+ return count;
+ }
+ bool isKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) {
+ return (bitRead(matrix_state_[key_addr.row()].current, key_addr.col()) != 0);
+ }
+
+ uint8_t previousPressedKeyswitchCount() {
+ uint8_t count = 0;
+
+ for (int8_t r = 0; r < _KeyScannerProps::matrix_rows; r++) {
+ count += __builtin_popcount(matrix_state_[r].previous);
+ }
+ return count;
+ }
+ bool wasKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) {
+ return (bitRead(matrix_state_[key_addr.row()].previous,
+ key_addr.col()) != 0);
+ }
+
+ void maskKey(typename _KeyScannerProps::KeyAddr key_addr) {
+ if (!key_addr.isValid())
+ return;
+
+ bitWrite(matrix_state_[key_addr.row()].masks, key_addr.col(), 1);
+ }
+ void unMaskKey(typename _KeyScannerProps::KeyAddr key_addr) {
+ if (!key_addr.isValid())
+ return;
+
+ bitWrite(matrix_state_[key_addr.row()].masks, key_addr.col(), 0);
+ }
+ bool isKeyMasked(typename _KeyScannerProps::KeyAddr key_addr) {
+ if (!key_addr.isValid())
+ return false;
+
+ return bitRead(matrix_state_[key_addr.row()].masks,
+ key_addr.col());
+ }
+
+ bool do_scan_;
+
+
+ protected:
+ /*
+ each of these variables are storing the state for a row of keys
+
+ so for key 0, the counter is represented by db0[0] and db1[0]
+ and the state in debounced_state[0].
+ */
+ struct debounce_t {
+ typename _KeyScannerProps::RowState db0; // counter bit 0
+ typename _KeyScannerProps::RowState db1; // counter bit 1
+ typename _KeyScannerProps::RowState debounced_state; // debounced state
+ };
+
+ struct row_state_t {
+ typename _KeyScannerProps::RowState previous;
+ typename _KeyScannerProps::RowState current;
+ typename _KeyScannerProps::RowState masks;
+ debounce_t debouncer;
+ };
+
+ private:
+ typedef _KeyScannerProps KeyScannerProps_;
+ static row_state_t matrix_state_[_KeyScannerProps::matrix_rows];
+
+ /*
+ * This function has loop unrolling disabled on purpose: we want to give the
+ * hardware enough time to produce stable PIN reads for us. If we unroll the
+ * loop, we will not have that, because even with the NOP, the codepath is too
+ * fast. If we don't have stable reads, then entire rows or columns will behave
+ * erratically.
+ *
+ * For this reason, we ask the compiler to not unroll our loop, which in turn,
+ * gives hardware enough time to produce stable reads, at the cost of a little
+ * bit of speed.
+ *
+ * Do not remove the attribute!
+ */
+ __attribute__((optimize("no-unroll-loops")))
+ typename _KeyScannerProps::RowState readCols() {
+ typename _KeyScannerProps::RowState hot_pins = 0;
+ for (uint8_t i = 0; i < _KeyScannerProps::matrix_columns; i++) {
+ asm("NOP"); // We need to pause a beat before reading or we may read before the pin is hot
+ hot_pins |= (!READ_PIN(_KeyScannerProps::matrix_col_pins[i]) << i);
+ }
+
+ return hot_pins;
+ }
+
+ static inline typename _KeyScannerProps::RowState debounce(
+ typename _KeyScannerProps::RowState sample, debounce_t *debouncer
+ ) {
+ typename _KeyScannerProps::RowState delta, changes;
+
+ // Use xor to detect changes from last stable state:
+ // if a key has changed, it's bit will be 1, otherwise 0
+ delta = sample ^ debouncer->debounced_state;
+
+ // Increment counters and reset any unchanged bits:
+ // increment bit 1 for all changed keys
+ debouncer->db1 = ((debouncer->db1) ^ (debouncer->db0)) & delta;
+ // increment bit 0 for all changed keys
+ debouncer->db0 = ~(debouncer->db0) & delta;
+
+ // Calculate returned change set: if delta is still true
+ // and the counter has wrapped back to 0, the key is changed.
+
+ changes = ~(~delta | (debouncer->db0) | (debouncer->db1));
+ // Update state: in this case use xor to flip any bit that is true in changes.
+ debouncer->debounced_state ^= changes;
+
+ return changes;
+ }
+};
+#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+template
+class SAMD : public keyscanner::None {};
+#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
+
+}
+}
+}
diff --git a/src/kaleidoscope/driver/storage/Flash.h b/src/kaleidoscope/driver/storage/Flash.h
index a8dc0617..a097b634 100644
--- a/src/kaleidoscope/driver/storage/Flash.h
+++ b/src/kaleidoscope/driver/storage/Flash.h
@@ -25,7 +25,7 @@
#pragma once
-#ifdef __SAMD21G18A__
+#if defined(__SAMD21__) || defined(__SAMD51__)
#include "kaleidoscope/driver/storage/Base.h"
#include