Factor pin-reading logic into a new set of classes.

This commit is contained in:
Anna Rose 2021-11-22 19:12:44 +00:00
parent a4f00a0a23
commit 8ebc1a5523
6 changed files with 182 additions and 109 deletions

View File

@ -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);
}

View File

@ -2,8 +2,7 @@
#define _BUTTON_H_
#include <Encoder.h>
#include <Mux.h>
#include <Bounce2.h>
#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;

View File

@ -1,5 +1,6 @@
#include "Joystick.h"
#include "Button.h"
#include "Reader.h"
#include <Mux.h>
#include <Arduino.h>
#include <stdio.h>
@ -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);
}
}

View File

@ -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.

61
Reader.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "Reader.h"
#include <Mux.h>
#include <Bounce2.h>
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;
}

59
Reader.h Normal file
View File

@ -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 <Bounce2.h>
#include <Mux.h>
// 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