-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for input shift registers (e.g. 74HC165) #77
Changes from 2 commits
8e48b9d
de9440b
e2cd3b6
b766f48
bcaf841
ad20d52
ff3e9b3
875db3a
ba498c7
630cb10
09fa9b9
127dacc
fa5aaaf
5befe43
9bedb57
5b4094e
fce5ef9
0ee2b3d
8309bc6
7f560c8
849b2ff
86be5ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// MFInputShifter.h | ||
|
||
#ifndef MFInputShifter_h | ||
#define MFInputShifter_h | ||
|
||
#if ARDUINO >= 100 | ||
#include <Arduino.h> | ||
#else | ||
#include <WProgram.h> | ||
#include <wiring.h> | ||
#endif | ||
|
||
// Maximum number of shifters allowed on an individual chain. While this is currently set to 4 | ||
// there is no technical limit in the code for how many can be chained. It is constrained only | ||
// by available memory (one byte required per chip) and the time it takes to read all the bits in. | ||
#define MAX_CHAINED_INPUT_SHIFTERS 4 | ||
|
||
extern "C" | ||
{ | ||
typedef void (*inputShifterEvent)(byte, uint8_t, const char *); | ||
}; | ||
|
||
enum | ||
{ | ||
inputShifterOnPress, | ||
inputShifterOnRelease, | ||
}; | ||
|
||
///////////////////////////////////////////////////////////////////// | ||
/// \class MFInputShifter MFInputShifter.h <MFInputShifter.h> | ||
class MFInputShifter | ||
{ | ||
public: | ||
MFInputShifter(const char *name = "InputShifter"); | ||
void attach(uint8_t latchPin, uint8_t clockPin, uint8_t dataPin, uint8_t moduleCount); | ||
void detach(); | ||
void clear(); | ||
void update(); | ||
void attachHandler(byte eventId, inputShifterEvent newHandler); | ||
|
||
private: | ||
const char *_name; | ||
uint8_t _latchPin; // SH/~LD (latch) pin | ||
uint8_t _clockPin; // CLK (clock) pin | ||
uint8_t _dataPin; // SDO (data) pin | ||
uint8_t _moduleCount; // Number of 8 bit modules in series. | ||
bool _initialized = false; | ||
uint32_t _last; | ||
uint8_t _lastState[MAX_CHAINED_INPUT_SHIFTERS]; | ||
|
||
void detectChanges(uint8_t lastState, uint8_t currentState, uint8_t module); | ||
void trigger(uint8_t pin, bool state); | ||
void clearLastState(); | ||
inputShifterEvent _handlerList[2]; | ||
}; | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// MFSegments.cpp | ||
// | ||
// Copyright (C) 2021 | ||
#include "MFInputShifter.h" | ||
|
||
MFInputShifter::MFInputShifter(const char *name) | ||
{ | ||
_initialized = false; | ||
clearLastState(); | ||
_last = millis(); | ||
_name = name; | ||
} | ||
|
||
// Registers a new input shifter and configures the clock, data and latch pins as well | ||
// as the number of modules to read from. | ||
void MFInputShifter::attach(uint8_t latchPin, uint8_t clockPin, uint8_t dataPin, uint8_t moduleCount) | ||
{ | ||
_latchPin = latchPin; | ||
_clockPin = clockPin; | ||
_dataPin = dataPin; | ||
_moduleCount = moduleCount; | ||
|
||
pinMode(_latchPin, OUTPUT); | ||
pinMode(_clockPin, OUTPUT); | ||
pinMode(_dataPin, INPUT); | ||
_initialized = true; | ||
} | ||
|
||
// Reads the values from the attached modules, compares them to the previously | ||
// read values, and calls the registered event handler for any inputs that | ||
// changed from the previously read state. | ||
void MFInputShifter::update() | ||
{ | ||
// Don't take any changes within the last 10 milliseconds to cover basic switch debouncing. | ||
// This technically could miss two different switches changing within 10 milliseconds | ||
// but that seems extremely unlikely to happen. | ||
uint32_t now = millis(); | ||
if (now - _last <= 10) | ||
return; | ||
|
||
digitalWrite(_clockPin, HIGH); // Preset clock to retrieve first bit | ||
digitalWrite(_latchPin, HIGH); // Disable input latching and enable shifting | ||
|
||
// Multiple chained modules are handled one at a time. As shiftIn() keeps getting | ||
// called it will pull in the data from each chained module. | ||
for (int i = 0; i < _moduleCount; i++) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a different implementation approach than the one used for output shifters that I think is easier to understand. |
||
{ | ||
uint8_t currentState; | ||
|
||
currentState = shiftIn(_dataPin, _clockPin, MSBFIRST); | ||
|
||
// If an input changed on the current module from the last time it was read | ||
// then hand it off to figure out which bits specifically changed. | ||
if (currentState != _lastState[i]) | ||
{ | ||
detectChanges(_lastState[i], currentState, i); | ||
_lastState[i] = currentState; | ||
} | ||
} | ||
|
||
digitalWrite(_latchPin, LOW); // disable shifting and enable input latching | ||
|
||
_last = now; | ||
} | ||
|
||
// Detects changes between the current state and the previously saved state | ||
// of a byte's worth of input. | ||
void MFInputShifter::detectChanges(uint8_t lastState, uint8_t currentState, uint8_t module) | ||
{ | ||
for (uint8_t i = 0; i < 8; i++) | ||
{ | ||
// If last and current input state for the bit are different | ||
// then the input changed and the handler for the bit needs to fire | ||
if ((lastState & 1) ^ (currentState & 1)) | ||
{ | ||
// When triggering event the pin is the actual pin on the chip offset by 8 bits for each | ||
// module beyond the first that it's on. The state of the trigger is the bit currently | ||
// in position 0 of currentState. | ||
trigger(i + (module * 8), currentState & 1); | ||
} | ||
|
||
lastState = lastState >> 1; | ||
currentState = currentState >> 1; | ||
} | ||
} | ||
|
||
// Triggers the event handler for the associated input shift register pin, | ||
// if a handler is registered. | ||
void MFInputShifter::trigger(uint8_t pin, bool state) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is identical to how buttons work other than the addition of sending the pin that triggered along with the event. |
||
{ | ||
if (state == LOW && _handlerList[inputShifterOnPress] != NULL) | ||
{ | ||
(*_handlerList[inputShifterOnPress])(inputShifterOnPress, pin, _name); | ||
} | ||
else if (_handlerList[inputShifterOnRelease] != NULL) | ||
{ | ||
(*_handlerList[inputShifterOnRelease])(inputShifterOnRelease, pin, _name); | ||
} | ||
} | ||
|
||
// Attaches a new event handler for the specified event. | ||
void MFInputShifter::attachHandler(byte eventId, inputShifterEvent newHandler) | ||
{ | ||
_handlerList[eventId] = newHandler; | ||
} | ||
|
||
void MFInputShifter::detach() | ||
{ | ||
if (!_initialized) | ||
return; | ||
_initialized = false; | ||
} | ||
|
||
// Clears the internal state of the shifter, including all received bits | ||
// and the timestamp for the last time the data was read. | ||
void MFInputShifter::clear() | ||
{ | ||
_last = 0; | ||
clearLastState(); | ||
} | ||
|
||
// Sets the last recorded state of every bit on every shifter to 0. | ||
void MFInputShifter::clearLastState() | ||
{ | ||
for (int i = 0; i < MAX_CHAINED_INPUT_SHIFTERS; i++) | ||
{ | ||
_lastState[i] = 0; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The general shape of the MFInputShifter is modeled on the existing output shifter and button classes.