Start to sketch out generic samd support

f/samd-testbed
Jesse Vincent 4 years ago
parent 8f23161d19
commit b25b9b02f4
No known key found for this signature in database
GPG Key ID: 122F5DF7108E4046

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/device/keyboardio/SAMDTestbed.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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined(__SAMD51__) || defined(__SAMD21__) || defined(KALEIDOSCOPE_VIRTUAL_BUILD)
#include <Arduino.h>
#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<StorageProps> Storage;
};
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
template <typename _DeviceProps>
class SAMDKeyboard : public kaleidoscope::device::Base<_DeviceProps> {
public:
auto serialPort() -> decltype(Serial) & {
return Serial;
}
};
#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
template <typename _DeviceProps>
class SAMDKeyboard;
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
}
}
#endif

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef KEYBOARDIO_SAMD_TESTBED
#include <Arduino.h>
#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<matrix_rows, matrix_columns> 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<KeyScannerProps> 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<SAMDTestbedProps> {};
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__ASSEMBLER__) && !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
//#include <avr/io.h>
#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)))

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Arduino.h>
#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 <typename _KeyScannerProps>
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 <typename _KeyScannerProps>
class SAMD : public keyscanner::None {};
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
}
}
}

@ -25,7 +25,7 @@
#pragma once #pragma once
#ifdef __SAMD21G18A__ #if defined(__SAMD21__) || defined(__SAMD51__)
#include "kaleidoscope/driver/storage/Base.h" #include "kaleidoscope/driver/storage/Base.h"
#include <FlashStorage.h> #include <FlashStorage.h>

Loading…
Cancel
Save