Skip to content
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

Merged
merged 22 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions _Boards/Atmel/Board_Mega/MFBoards.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#ifndef MF_SHIFTER_SUPPORT
#define MF_SHIFTER_SUPPORT 1
#endif
#ifndef MF_INPUT_SHIFTER_SUPPORT
#define MF_INPUT_SHIFTER_SUPPORT 1
#endif

#define MODULE_MAX_PINS 69
#define MAX_OUTPUTS 40
Expand All @@ -30,6 +33,7 @@
#define MAX_MFLCD_I2C 2
#define MAX_ANALOG_INPUTS 5
#define MAX_SHIFTERS 10
#define MAX_INPUT_SHIFTERS 10

#define STEPS 64
#define STEPPER_SPEED 400 // 300 already worked, 467, too?
Expand Down
4 changes: 4 additions & 0 deletions _Boards/Atmel/Board_ProMicro/MFBoards.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#ifndef MF_SHIFTER_SUPPORT
#define MF_SHIFTER_SUPPORT 1
#endif
#ifndef MF_INPUT_SHIFTER_SUPPORT
#define MF_INPUT_SHIFTER_SUPPORT 1
#endif

#define MODULE_MAX_PINS 21
#define MAX_OUTPUTS 10
Expand All @@ -30,6 +33,7 @@
#define MAX_MFLCD_I2C 2
#define MAX_ANALOG_INPUTS 2
#define MAX_SHIFTERS 4
#define MAX_INPUT_SHIFTERS 4

#define STEPS 64
#define STEPPER_SPEED 400 // 300 already worked, 467, too?
Expand Down
4 changes: 4 additions & 0 deletions _Boards/Atmel/Board_Uno/MFBoards.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#ifndef MF_SHIFTER_SUPPORT
#define MF_SHIFTER_SUPPORT 1
#endif
#ifndef MF_INPUT_SHIFTER_SUPPORT
#define MF_INPUT_SHIFTER_SUPPORT 1
#endif

#define MODULE_MAX_PINS 13
#define MAX_OUTPUTS 8
Expand All @@ -30,6 +33,7 @@
#define MAX_MFLCD_I2C 2
#define MAX_ANALOG_INPUTS 0
#define MAX_SHIFTERS 0
#define MAX_INPUT_SHIFTERS 4

#define STEPS 64
#define STEPPER_SPEED 400 // 300 already worked, 467, too?
Expand Down
56 changes: 56 additions & 0 deletions include/MFInputShifter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// MFInputShifter.h
Copy link
Contributor Author

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.


#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
14 changes: 9 additions & 5 deletions include/mobiflight.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <MFEncoder.h>
#include <MFAnalog.h>

#define MF_BUTTON_DEBOUNCE_MS 10 // time between updating the buttons
#define MF_BUTTON_DEBOUNCE_MS 10 // time between updating the buttons

enum
{
Expand All @@ -19,7 +19,8 @@ enum
kTypeEncoder, // 8
kTypeStepper, // 9 (new stepper type with auto zero support if btnPin is > 0)
kShiftRegister, // 10 Shift register support (example: 74HC595, TLC592X)
kTypeAnalogInput // 11 Analog Device with 1 pin
kTypeAnalogInput, // 11 Analog Device with 1 pin
kTypeInputShifter // 12 Input shift register support (example: 74HC165)
};

// This is the list of recognized commands. These can be commands that can either be sent or received.
Expand Down Expand Up @@ -57,7 +58,8 @@ enum
kSetLcdDisplayI2C, // 25
kSetModuleBrightness, // 26
kSetShiftRegisterPins, // 27
kAnalogChange // 28
kAnalogChange, // 28
kInputShifterChange // 29
};

void attachCommandCallbacks();
Expand Down Expand Up @@ -124,6 +126,8 @@ void handlerOnAnalogChange(int value, uint8_t pin, const char *name);
void OnInitShiftRegister();
void OnSetShiftRegisterPins();
void AddShifter(uint8_t latchPin, uint8_t clockPin, uint8_t dataPin, uint8_t modules, char const *name);


void AddInputShifter(uint8_t latchPin, uint8_t clockPin, uint8_t dataPin, uint8_t modules, char const *name);
void ClearInputShifters();
void readInputShifters();
void handlerInputShifterOnChange(uint8_t eventId, uint8_t pin, const char *name);
#endif
129 changes: 129 additions & 0 deletions src/MF_Modules/MFInputShifter.cpp
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++)
Copy link
Contributor Author

@neilenns neilenns Oct 29, 2021

Choose a reason for hiding this comment

The 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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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;
}
}
Loading