Factor pin-reading logic into a new set of classes.
This commit is contained in:
parent
a4f00a0a23
commit
8ebc1a5523
55
Button.cpp
55
Button.cpp
|
@ -5,60 +5,28 @@
|
||||||
|
|
||||||
using namespace admux;
|
using namespace admux;
|
||||||
|
|
||||||
Button::Button(uint8_t vbutton) {
|
Button::Button(uint8_t vbutton, Reader* reader) {
|
||||||
this->vbutton = vbutton;
|
this->vbutton = vbutton;
|
||||||
|
this->reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::ReleaseButtons(Joystick* js) {
|
void Button::ReleaseButtons(Joystick* js) {
|
||||||
js->ReleaseButton(vbutton);
|
js->ReleaseButton(vbutton);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make analog_only work... how to handle that with debouncer?
|
PassthruButton::PassthruButton(uint8_t vbutton, Reader* reader) : Button(vbutton, reader) {
|
||||||
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) {
|
|
||||||
this->type = BUTTON_PASSTHRU;
|
this->type = BUTTON_PASSTHRU;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PassthruButton::Update(Joystick* js) {
|
bool PassthruButton::Update(Joystick* js) {
|
||||||
if (!BouncerUpdate()) return false;
|
if (!reader->Update()) return false;
|
||||||
if (On()) js->PressButton(vbutton);
|
if (reader->On()) js->PressButton(vbutton);
|
||||||
else js->ReleaseButton(vbutton);
|
else js->ReleaseButton(vbutton);
|
||||||
return true;
|
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->type = BUTTON_LATCHED_MOMENTARY;
|
||||||
this->pressed = false;
|
this->pressed = false;
|
||||||
}
|
}
|
||||||
|
@ -131,12 +99,6 @@ bool PulsedButton::Update(Joystick* js) {
|
||||||
return true;
|
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) {
|
EncoderButton::EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton) : Button(vbutton) {
|
||||||
this->type = ENCODER_PULSED_SPLIT;
|
this->type = ENCODER_PULSED_SPLIT;
|
||||||
|
@ -173,8 +135,3 @@ bool EncoderButton::Update(Joystick* js) {
|
||||||
last_value = new_value;
|
last_value = new_value;
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncoderButton::ReleaseButtons(Joystick* js) {
|
|
||||||
Button::ReleaseButtons(js);
|
|
||||||
js->ReleaseButton(vbutton2);
|
|
||||||
}
|
|
||||||
|
|
38
Button.h
38
Button.h
|
@ -2,8 +2,7 @@
|
||||||
#define _BUTTON_H_
|
#define _BUTTON_H_
|
||||||
|
|
||||||
#include <Encoder.h>
|
#include <Encoder.h>
|
||||||
#include <Mux.h>
|
#include "Reader.h"
|
||||||
#include <Bounce2.h>
|
|
||||||
|
|
||||||
using namespace admux;
|
using namespace admux;
|
||||||
|
|
||||||
|
@ -24,53 +23,35 @@ enum ButtonType {
|
||||||
// * an update method.
|
// * an update method.
|
||||||
class Button {
|
class Button {
|
||||||
public:
|
public:
|
||||||
Button(uint8_t vbutton);
|
Button(uint8_t vbutton, Reader* reader);
|
||||||
virtual bool Update(Joystick* js) = 0;
|
virtual bool Update(Joystick* js) = 0;
|
||||||
virtual void ReleaseButtons(Joystick* js);
|
virtual void ReleaseButtons(Joystick* js);
|
||||||
ButtonType type;
|
ButtonType type;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t vbutton;
|
uint8_t vbutton;
|
||||||
|
Reader *reader;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An abstract class for a momentary/pushbutton or toggle switch. Special properties:
|
class PassthruButton : public Button {
|
||||||
// * 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 {
|
|
||||||
public:
|
public:
|
||||||
SwitchButton(uint8_t pin, uint8_t vbutton, bool pullup, Mux* mux, bool analog_only);
|
PassthruButton(uint8_t vbutton, Reader* reader);
|
||||||
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);
|
|
||||||
bool Update(Joystick* js);
|
bool Update(Joystick* js);
|
||||||
};
|
};
|
||||||
|
|
||||||
class LatchedButton : public SwitchButton {
|
class LatchedButton : public Button {
|
||||||
public:
|
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);
|
bool Update(Joystick* js);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool pressed;
|
bool pressed;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PulsedButton : public SwitchButton {
|
class PulsedButton : public Button {
|
||||||
public:
|
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);
|
bool Update(Joystick* js);
|
||||||
void ReleaseButtons(Joystick* js);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool double_action;
|
bool double_action;
|
||||||
|
@ -84,7 +65,6 @@ class EncoderButton : public Button {
|
||||||
public:
|
public:
|
||||||
EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton);
|
EncoderButton(uint8_t pin1, uint8_t pin2, uint8_t vbutton);
|
||||||
bool Update(Joystick* js);
|
bool Update(Joystick* js);
|
||||||
void ReleaseButtons(Joystick* js);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Encoder* encoder;
|
Encoder* encoder;
|
||||||
|
|
70
Joystick.cpp
70
Joystick.cpp
|
@ -1,5 +1,6 @@
|
||||||
#include "Joystick.h"
|
#include "Joystick.h"
|
||||||
#include "Button.h"
|
#include "Button.h"
|
||||||
|
#include "Reader.h"
|
||||||
#include <Mux.h>
|
#include <Mux.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -39,36 +40,16 @@ void Joystick::Init() {
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Joystick::AddButton(uint8_t pin, ButtonType type, bool pullup, Mux* mux, bool analog_only) {
|
void Joystick::AddButton(uint8_t pin, ButtonType type, bool pullup) {
|
||||||
Button *button;
|
_addButton(type, new DirectReader(pin, pullup));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buttons[_num_buttons] = button;
|
void Joystick::AddMuxButton(uint8_t channel, Mux* mux, ButtonType type, bool pullup) {
|
||||||
_num_buttons++;
|
_addButton(type, new MuxReader(channel, mux, pullup));
|
||||||
if (_debug) {
|
}
|
||||||
char buffer[100];
|
|
||||||
sprintf(buffer, "Added button %d of type %d", _num_buttons - 1, button->type);
|
void Joystick::AddMatrixButton(uint8_t row, uint8_t col, ButtonType type, bool pullup) {
|
||||||
Serial.println(buffer);
|
_addButton(type, new MatrixReader(row, col, pullup));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Joystick::AddEncoder(uint8_t pin1, uint8_t pin2, ButtonType type) {
|
void Joystick::AddEncoder(uint8_t pin1, uint8_t pin2, ButtonType type) {
|
||||||
|
@ -164,3 +145,34 @@ void Joystick::Write() {
|
||||||
void Joystick::_UpdateAxis(uint8_t index) {
|
void Joystick::_UpdateAxis(uint8_t index) {
|
||||||
if (_debug) Serial.println("STUB: Joystick::_UpdateAxis");
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -39,7 +39,9 @@ class Joystick {
|
||||||
// If `pullup` is false, your button should connect the pin to VCC.
|
// 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
|
// Setting `analogOnly` to true indicates your button *must* be read with analog code, such as the A6 and A7 pins
|
||||||
// on the Arduino Nano.
|
// 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)
|
// 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);
|
void AddEncoder(uint8_t pin1, uint8_t pin2, ButtonType type);
|
||||||
|
@ -59,6 +61,8 @@ class Joystick {
|
||||||
|
|
||||||
void _UpdateAxis(uint8_t index);
|
void _UpdateAxis(uint8_t index);
|
||||||
|
|
||||||
|
void _addButton(ButtonType type, Reader* reader);
|
||||||
|
|
||||||
Button* _buttons[JOYSTICK_NUM_BUTTONS];
|
Button* _buttons[JOYSTICK_NUM_BUTTONS];
|
||||||
uint8_t _num_buttons;
|
uint8_t _num_buttons;
|
||||||
uint8_t _virtual_buttons; // a single user-defined button can have multiple virtual buttons.
|
uint8_t _virtual_buttons; // a single user-defined button can have multiple virtual buttons.
|
||||||
|
|
61
Reader.cpp
Normal file
61
Reader.cpp
Normal 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
59
Reader.h
Normal 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
|
Loading…
Reference in New Issue
Block a user