This keyscanner driver is for devices that have keys directly connected to pins, rather than having them arranged in a matrix. It's a lot like the ATmega scanner, but more compact. Signed-off-by: Gergely Nagy <algernon@keyboard.io>hardware/ploopy
parent
4ef189c0b1
commit
0944fc3b1e
@ -0,0 +1,205 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope - Firmware for computer input devices
|
||||||
|
* Copyright (C) 2022 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 <stdint.h> // for uint16_t, uint8_t
|
||||||
|
|
||||||
|
#include "kaleidoscope/device/avr/pins_and_ports.h" // IWYU pragma: keep
|
||||||
|
#include "kaleidoscope/driver/keyscanner/Base.h" // for BaseProps
|
||||||
|
#include "kaleidoscope/driver/keyscanner/None.h" // for None
|
||||||
|
|
||||||
|
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace driver {
|
||||||
|
namespace keyscanner {
|
||||||
|
|
||||||
|
struct ATmegaDirectProps : public kaleidoscope::driver::keyscanner::BaseProps {
|
||||||
|
static const uint16_t keyscan_interval = 1500;
|
||||||
|
typedef uint16_t PinState;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following lines declares empty arrays, and both must be shadowed by the
|
||||||
|
* descendant keyscanner description class.
|
||||||
|
*/
|
||||||
|
static constexpr uint8_t direct_pins[] = {};
|
||||||
|
static constexpr uint8_t unused_pins[] = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
|
||||||
|
template<typename _KeyScannerProps>
|
||||||
|
class ATmegaDirect : kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> {
|
||||||
|
private:
|
||||||
|
typedef ATmegaDirect<_KeyScannerProps> ThisType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setup() {
|
||||||
|
static_assert(
|
||||||
|
sizeof(_KeyScannerProps::direct_pins) > 0,
|
||||||
|
"The key scanner description has an empty array of direct pins.");
|
||||||
|
static_assert(
|
||||||
|
_KeyScannerProps::matrix_rows == 1,
|
||||||
|
"ATmegaDirect requires matrix_rows to be 1.");
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(_KeyScannerProps::unused_pins); i++) {
|
||||||
|
DDR_OUTPUT(_KeyScannerProps::unused_pins[i]);
|
||||||
|
DISABLE_PULLUP(_KeyScannerProps::unused_pins[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < sizeof(_KeyScannerProps::direct_pins); i++) {
|
||||||
|
DDR_INPUT(_KeyScannerProps::direct_pins[i]);
|
||||||
|
ENABLE_PULLUP(_KeyScannerProps::direct_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) {
|
||||||
|
TCCR1B = _BV(WGM13);
|
||||||
|
TCCR1A = 0;
|
||||||
|
|
||||||
|
const uint32_t cycles = (F_CPU / 2000000) * c;
|
||||||
|
|
||||||
|
ICR1 = cycles;
|
||||||
|
TCCR1B = _BV(WGM13) | _BV(CS10);
|
||||||
|
TIMSK1 = _BV(TOIE1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void readMatrix() {
|
||||||
|
typename _KeyScannerProps::PinState hot_pins = readPins();
|
||||||
|
typename _KeyScannerProps::PinState debounced_changes =
|
||||||
|
debounce(hot_pins, &(pin_state_.debouncer));
|
||||||
|
|
||||||
|
if (debounced_changes) {
|
||||||
|
pin_state_.current = pin_state_.debouncer.debounced_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scanMatrix() {
|
||||||
|
if (do_scan_) {
|
||||||
|
do_scan_ = false;
|
||||||
|
readMatrix();
|
||||||
|
}
|
||||||
|
actOnMatrixScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
void actOnMatrixScan() {
|
||||||
|
for (uint8_t pin = 0; pin < sizeof(_KeyScannerProps::direct_pins); pin++) {
|
||||||
|
uint8_t keyState = (bitRead(pin_state_.previous, pin) << 0) |
|
||||||
|
(bitRead(pin_state_.current, pin) << 1);
|
||||||
|
if (keyState) {
|
||||||
|
ThisType::handleKeyswitchEvent(Key_NoKey, typename _KeyScannerProps::KeyAddr(0, pin), keyState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pin_state_.previous = pin_state_.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pressedKeyswitchCount() {
|
||||||
|
return __builtin_popcount(pin_state_.current);
|
||||||
|
}
|
||||||
|
bool isKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) {
|
||||||
|
return (bitRead(pin_state_.current, key_addr.col()) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t previousPressedKeyswitchCount() {
|
||||||
|
return __builtin_popcount(pin_state_.previous);
|
||||||
|
}
|
||||||
|
bool wasKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) {
|
||||||
|
return (bitRead(pin_state_.previous, key_addr.col()) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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::PinState db0; // counter bit 0
|
||||||
|
typename _KeyScannerProps::PinState db1; // counter bit 1
|
||||||
|
typename _KeyScannerProps::PinState debounced_state; // debounced state
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pin_state_t {
|
||||||
|
typename _KeyScannerProps::PinState previous;
|
||||||
|
typename _KeyScannerProps::PinState current;
|
||||||
|
debounce_t debouncer;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef _KeyScannerProps KeyScannerProps_;
|
||||||
|
static pin_state_t pin_state_;
|
||||||
|
|
||||||
|
typename _KeyScannerProps::PinState
|
||||||
|
readPins() {
|
||||||
|
typename _KeyScannerProps::PinState hot_pins = 0;
|
||||||
|
for (uint8_t i = 0; i < sizeof(_KeyScannerProps::direct_pins); i++) {
|
||||||
|
hot_pins |= (!READ_PIN(_KeyScannerProps::direct_pins[i]) << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hot_pins;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline typename _KeyScannerProps::PinState debounce(
|
||||||
|
typename _KeyScannerProps::PinState sample, debounce_t *debouncer) {
|
||||||
|
typename _KeyScannerProps::PinState 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 ATmegaDirect : public keyscanner::None {};
|
||||||
|
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
|
||||||
|
|
||||||
|
} // namespace keyscanner
|
||||||
|
} // namespace driver
|
||||||
|
} // namespace kaleidoscope
|
Loading…
Reference in new issue