From 8ebc1a55237d47a586fb5662dc38940923755d4d Mon Sep 17 00:00:00 2001 From: Anna Wiggins Date: Mon, 22 Nov 2021 19:12:44 +0000 Subject: [PATCH] Factor pin-reading logic into a new set of classes. --- Button.cpp | 55 +++++------------------------------------ Button.h | 38 +++++++--------------------- Joystick.cpp | 70 ++++++++++++++++++++++++++++++---------------------- Joystick.h | 8 ++++-- Reader.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++ Reader.h | 59 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 109 deletions(-) create mode 100644 Reader.cpp create mode 100644 Reader.h diff --git a/Button.cpp b/Button.cpp index 82482cd..5591517 100644 --- a/Button.cpp +++ b/Button.cpp @@ -5,60 +5,28 @@ using namespace admux; -Button::Button(uint8_t vbutton) { +Button::Button(uint8_t vbutton, Reader* reader) { this->vbutton = vbutton; + this->reader = reader; } void Button::ReleaseButtons(Joystick* js) { js->ReleaseButton(vbutton); } -// TODO: make analog_only work... how to handle that with debouncer? -SwitchButton::SwitchButton(uint8_t pin, uint8_t vbutton, bool pullup, Mux* mux, bool analog_only) : Button(vbutton) { - this->mux = mux; - - uint8_t mode = INPUT; - if (pullup) { - this->inverted = true; - mode = INPUT_PULLUP; - } - - if (mux == NULL) this->bouncer.attach(pin, mode); - else { - this->bouncer.attach(mux->signalPin().pin, mode); - this->channel_id = pin; - } -} - -bool SwitchButton::BouncerUpdate() { - if (mux != NULL) { - mux->channel(channel_id); - delayMicroseconds(500); - } - - return bouncer.update(); -} - -bool SwitchButton::On() { - bool state = bouncer.rose(); - if (inverted) state = bouncer.fell(); - return state; -} - - -PassthruButton::PassthruButton(uint8_t pin, uint8_t vbutton, bool pullup, Mux* mux, bool analog_only) : SwitchButton(pin, vbutton, pullup, mux, analog_only) { +PassthruButton::PassthruButton(uint8_t vbutton, Reader* reader) : Button(vbutton, reader) { this->type = BUTTON_PASSTHRU; } bool PassthruButton::Update(Joystick* js) { - if (!BouncerUpdate()) return false; - if (On()) js->PressButton(vbutton); + if (!reader->Update()) return false; + if (reader->On()) js->PressButton(vbutton); else js->ReleaseButton(vbutton); return true; } -LatchedButton::LatchedButton(uint8_t pin, uint8_t vbutton, bool pullup, Mux* mux, bool analog_only) : SwitchButton(pin, vbutton, pullup, mux, analog_only) { +LatchedButton::LatchedButton(uint8_t vbutton, Reader* reader) : Button(vbutton, reader) { this->type = BUTTON_LATCHED_MOMENTARY; this->pressed = false; } @@ -131,12 +99,6 @@ bool PulsedButton::Update(Joystick* js) { return true; } -void PulsedButton::ReleaseButtons(Joystick* js) { - Button::ReleaseButtons(js); - if (type == BUTTON_PULSED_DOUBLE_ACTION_SPLIT) js->ReleaseButton(vbutton2); -} - - EncoderButton::EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton) : Button(vbutton) { this->type = ENCODER_PULSED_SPLIT; @@ -173,8 +135,3 @@ bool EncoderButton::Update(Joystick* js) { last_value = new_value; return changed; } - -void EncoderButton::ReleaseButtons(Joystick* js) { - Button::ReleaseButtons(js); - js->ReleaseButton(vbutton2); -} diff --git a/Button.h b/Button.h index bf1912b..464f85b 100644 --- a/Button.h +++ b/Button.h @@ -2,8 +2,7 @@ #define _BUTTON_H_ #include -#include -#include +#include "Reader.h" using namespace admux; @@ -24,53 +23,35 @@ enum ButtonType { // * an update method. class Button { public: - Button(uint8_t vbutton); + Button(uint8_t vbutton, Reader* reader); virtual bool Update(Joystick* js) = 0; virtual void ReleaseButtons(Joystick* js); ButtonType type; protected: uint8_t vbutton; + Reader *reader; }; -// An abstract class for a momentary/pushbutton or toggle switch. Special properties: -// * needs to be explicitly debounced -// * may use the microcontroller's built-in pullup resistor mode to eliminate noise -// * may be attached to a multiplexer. In this case, the `pin` value will be treated as a multiplexer channel, -// and the multiplexer logic will be automatically invoked by Update() -class SwitchButton : public Button { +class PassthruButton : public Button { public: - SwitchButton(uint8_t pin, uint8_t vbutton, bool pullup, Mux* mux, bool analog_only); - bool BouncerUpdate(); // returns true if the pin's status has changed - bool On(); - - protected: - Bounce bouncer; - bool inverted; - Mux *mux; - uint8_t channel_id; -}; - -class PassthruButton : public SwitchButton { - public: - PassthruButton(uint8_t pin, uint8_t vbutton, bool pullup = true, Mux* mux = NULL, bool analog_only = false); + PassthruButton(uint8_t vbutton, Reader* reader); bool Update(Joystick* js); }; -class LatchedButton : public SwitchButton { +class LatchedButton : public Button { public: - LatchedButton(uint8_t pin, uint8_t vbutton, bool pullup = true, Mux* mux = NULL, bool analog_only = false); + LatchedButton(uint8_t vbutton, Reader* reader); bool Update(Joystick* js); protected: bool pressed; }; -class PulsedButton : public SwitchButton { +class PulsedButton : public Button { public: - PulsedButton(uint8_t pin, uint8_t vbutton, bool double_action = false, bool split = false, bool pullup = true, Mux* mux = NULL, bool analog_only = false); + PulsedButton(uint8_t vbutton, Reader* reader, bool double_action = false, bool split = false); bool Update(Joystick* js); - void ReleaseButtons(Joystick* js); protected: bool double_action; @@ -84,7 +65,6 @@ class EncoderButton : public Button { public: EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton); bool Update(Joystick* js); - void ReleaseButtons(Joystick* js); protected: Encoder* encoder; diff --git a/Joystick.cpp b/Joystick.cpp index 29bf0e3..a5eaaf1 100644 --- a/Joystick.cpp +++ b/Joystick.cpp @@ -1,5 +1,6 @@ #include "Joystick.h" #include "Button.h" +#include "Reader.h" #include #include #include @@ -39,36 +40,16 @@ void Joystick::Init() { delay(100); } -void Joystick::AddButton(uint8_t pin, ButtonType type, bool pullup, Mux* mux, bool analog_only) { - Button *button; - switch (type) { - case BUTTON_PASSTHRU: - button = new PassthruButton(pin, _virtual_buttons, pullup, mux, analog_only); - _virtual_buttons++; - break; - case BUTTON_PULSED: - button = new PulsedButton(pin, _virtual_buttons, false, false, pullup, mux, analog_only); - _virtual_buttons++; - break; - case BUTTON_PULSED_DOUBLE_ACTION: - button = new PulsedButton(pin, _virtual_buttons, true, false, pullup, mux, analog_only); - _virtual_buttons++; - break; - case BUTTON_PULSED_DOUBLE_ACTION_SPLIT: - button = new PulsedButton(pin, _virtual_buttons, true, true, pullup, mux, analog_only); - _virtual_buttons += 2; - break; - default: - return; - } +void Joystick::AddButton(uint8_t pin, ButtonType type, bool pullup) { + _addButton(type, new DirectReader(pin, pullup)); +} - _buttons[_num_buttons] = button; - _num_buttons++; - if (_debug) { - char buffer[100]; - sprintf(buffer, "Added button %d of type %d", _num_buttons - 1, button->type); - Serial.println(buffer); - } +void Joystick::AddMuxButton(uint8_t channel, Mux* mux, ButtonType type, bool pullup) { + _addButton(type, new MuxReader(channel, mux, pullup)); +} + +void Joystick::AddMatrixButton(uint8_t row, uint8_t col, ButtonType type, bool pullup) { + _addButton(type, new MatrixReader(row, col, pullup)); } void Joystick::AddEncoder(uint8_t pin1, uint8_t pin2, ButtonType type) { @@ -164,3 +145,34 @@ void Joystick::Write() { void Joystick::_UpdateAxis(uint8_t index) { if (_debug) Serial.println("STUB: Joystick::_UpdateAxis"); } + +void Joystick::_addButton(ButtonType type, Reader* reader) { + switch (type) { + case BUTTON_PASSTHRU: + button = new PassthruButton(_virtual_buttons, reader); + _virtual_buttons++; + break; + case BUTTON_PULSED: + button = new PulsedButton(_virtual_buttons, reader, false, false); + _virtual_buttons++; + break; + case BUTTON_PULSED_DOUBLE_ACTION: + button = new PulsedButton(_virtual_buttons, reader, true, false); + _virtual_buttons++; + break; + case BUTTON_PULSED_DOUBLE_ACTION_SPLIT: + button = new PulsedButton(_virtual_buttons, reader, true, true); + _virtual_buttons += 2; + break; + default: + return; + } + + _buttons[_num_buttons] = button; + _num_buttons++; + if (_debug) { + char buffer[100]; + sprintf(buffer, "Added button %d of type %d", _num_buttons - 1, button->type); + Serial.println(buffer); + } +} diff --git a/Joystick.h b/Joystick.h index d9b2f56..0712375 100644 --- a/Joystick.h +++ b/Joystick.h @@ -39,8 +39,10 @@ class Joystick { // If `pullup` is false, your button should connect the pin to VCC. // Setting `analogOnly` to true indicates your button *must* be read with analog code, such as the A6 and A7 pins // on the Arduino Nano. - void AddButton(uint8_t pin, ButtonType type, bool pullup=true, Mux* mux=NULL, bool analog_only=false); - + void AddButton(uint8_t pin, ButtonType type, bool pullup=true); + void AddMuxButton(uint8_t channel, Mux* mux, ButtonType type, bool pullup=true); + void AddMatrixButton(uint8_t row, uint8_t col, ButtonType type, bool pullup=true); + // Add a rotary encoder. ENCODER button types allow you to treat an encoder as a momentary button or an axis (TODO) void AddEncoder(uint8_t pin1, uint8_t pin2, ButtonType type); @@ -59,6 +61,8 @@ class Joystick { void _UpdateAxis(uint8_t index); + void _addButton(ButtonType type, Reader* reader); + Button* _buttons[JOYSTICK_NUM_BUTTONS]; uint8_t _num_buttons; uint8_t _virtual_buttons; // a single user-defined button can have multiple virtual buttons. diff --git a/Reader.cpp b/Reader.cpp new file mode 100644 index 0000000..d6f1c95 --- /dev/null +++ b/Reader.cpp @@ -0,0 +1,61 @@ +#include "Reader.h" + +#include +#include + + +Reader::Reader(bool inverted) { + this->inverted = inverted; +} + +bool Reader::On() { + bool state = bouncer.rose(); + if (inverted) state = bouncer.fell(); + return state; +} + +uint8_t Reader::getMode(bool pullup) { + if (pullup) return INPUT_PULLUP; + return INPUT; +} + + +DirectReader::DirectReader(uint8_t pin, bool inverted) : Reader(inverted) { + uint8_t mode = getMode(inverted); + this->bouncer.attach(pin) +} + +bool DirectReader::Update() { + return bouncer.update(); +} + + +MuxReader::MuxReader(uint8_t channel, Mux* mux, bool inverted) : Reader(inverted) { + uint8_t mode = getMode(inverted); + this->bouncer.attach(mux->signalPin().pin, mode); + this->channel = channel; +} + +bool MuxReader::Update() { + mux->channel(channel); + return bouncer.update(); +} + + +MatrixReader::MatrixReader(uint8_t row, uint8_t col, bool inverted) : Reader(inverted) { + uint8_t mode = getMode(inverted); + this->bouncer.attach(row, mode); + this->col = col; +} + +bool MatrixReader::Update() { + digitalWrite(col, LOW); + delayMicroseconds(10); + + bool changed = bouncer.update(); + + digitalWrite(col, HIGH); + delayMicroseconds(10); + + return changed; +} diff --git a/Reader.h b/Reader.h new file mode 100644 index 0000000..f8b9349 --- /dev/null +++ b/Reader.h @@ -0,0 +1,59 @@ +// "Readers" represent different methods for reading the state of a pin. +// They also include debouncing logic using the Bounce2 library, when appropriate. + +#ifndef _READER_H_ +#define _READER_H_ + +#include +#include + +// The abstract base class - a very simple interface +class Reader { + public: + Reader(bool inverted); + + virtual bool Update() = 0; // should return true if the state of the pin has changed + bool On(); + + protected: + uint8_t getMode(bool pullup); + + Bounce bouncer; + bool inverted; +}; + + +// A standard reader; the button is connected directly to a switch +class DirectReader { + public: + DirectReader(uint8_t pin, bool inverted = true); + bool Update(); + bool On(); +}; + + +// The button is connected to a Multiplexer channel +class MuxReader { + public: + MuxReader(uint8_t channel, Mux* mux, bool inverted = true); + bool Update(); + bool On(); + + protected: + Mux * mux; +}; + + +// The button is connected via a scan matrix +// "row" should always be the input, with "col" the selecter. +class MatrixReader { + public: + MatrixReader(uint8_t row, uint8_t col, bool inverted = true); + bool Update(); + bool On(); + + protected: + uint8_t col; +}; + +#endif