// Copyright 2009 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// 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 .
//
// -----------------------------------------------------------------------------
//
// Controller for an array of switches read through a parallel-in > serial out
// shift register. Includes debouncing.
#ifndef AVRLIB_DEVICES_SWITCH_ARRAY_H_
#define AVRLIB_DEVICES_SWITCH_ARRAY_H_
#include "avrlib/devices/shift_register.h"
#include "avrlib/size_to_type.h"
#include "avrlib/time.h"
namespace avrlib {
struct SwitchState {
uint8_t changed;
uint8_t state;
uint8_t debounced_state;
uint32_t time;
};
struct KeyEvent {
uint8_t id;
uint8_t shifted;
uint8_t hold_time;
};
template
class SwitchArray {
typedef typename DataTypeForSize::Type T;
typedef ShiftRegisterInput<
Load, Clock, Data,
8 * sizeof(T), LSB_FIRST> Register;
public:
SwitchArray() { }
static void Init() {
for (uint8_t i = 0; i < num_inputs; ++i) {
switch_state_[i].state = 0xff;
switch_state_[i].debounced_state = HIGH;
switch_state_[i].time = 0;
}
last_event_time_ = 0;
Register::Init();
}
static uint32_t last_event_time() { return last_event_time_; }
static uint32_t idle_time() { return milliseconds() - last_event_time_; }
static uint8_t shifted() { return switch_state_[shift].state == 0x00; }
static void InhibitShiftRelease() { inhibit_shift_release_ = 1; }
static const SwitchState& switch_state(uint8_t i) { return switch_state_[i]; }
static uint8_t released() {
for (uint8_t i = 0; i < num_inputs; ++i) {
if (switch_state_[i].state == 0x7f &&
(i != shift || !inhibit_shift_release_)) {
return 1;
}
}
return 0;
}
static void Touch() {
last_event_time_ = milliseconds();
}
static inline KeyEvent key_event() {
KeyEvent e;
e.id = num_inputs;
for (uint8_t i = 0; i < num_inputs; ++i) {
if (switch_state_[i].state == 0x7f) {
if (i == shift && inhibit_shift_release_) {
inhibit_shift_release_ = 0;
} else {
e.id = i;
e.shifted = shifted();
e.hold_time = static_cast(
last_event_time_ - switch_state_[i].time) >> 8;
if (e.shifted) {
inhibit_shift_release_ = 1;
}
}
}
}
return e;
}
static uint8_t Read() {
T value = Register::Read();
uint32_t now = milliseconds();
T mask = 1 << (num_inputs - 1);
for (uint8_t i = 0; i < num_inputs; ++i) {
switch_state_[i].state <<= 1;
if (value & mask) {
switch_state_[i].state |= 1;
}
if (switch_state_[i].state == 0x80) {
last_event_time_ = now;
switch_state_[i].debounced_state = LOW;
switch_state_[i].time = now;
inhibit_shift_release_ = 0;
} else if (switch_state_[i].state == 0x7f) {
last_event_time_ = now;
switch_state_[i].debounced_state = HIGH;
}
mask >>= 1;
}
}
private:
static uint32_t last_event_time_;
static SwitchState switch_state_[num_inputs];
static uint8_t inhibit_shift_release_;
DISALLOW_COPY_AND_ASSIGN(SwitchArray);
};
template
SwitchState SwitchArray::switch_state_[num_inputs];
template
uint32_t SwitchArray::last_event_time_;
template
uint8_t SwitchArray::inhibit_shift_release_;
} // namespace avrlib
#endif // AVRLIB_DEVICES_SWITCH_ARRAY_H_