Skip to content

Commit

Permalink
Merge branch 'master' into pr-fix4395
Browse files Browse the repository at this point in the history
  • Loading branch information
thebentern authored Aug 6, 2024
2 parents 718ce00 + c1870f9 commit 4eb1732
Show file tree
Hide file tree
Showing 18 changed files with 422 additions and 69 deletions.
84 changes: 84 additions & 0 deletions src/GpioLogic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "GpioLogic.h"
#include <assert.h>

void GpioVirtPin::set(bool value)
{
if (value != this->value) {
this->value = value ? PinState::On : PinState::Off;
if (dependentPin)
dependentPin->update();
}
}

GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {}

void GpioTransformer::set(bool value)
{
outPin->set(value);
}

GpioNotTransformer::GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin)
{
assert(!inPin->dependentPin); // We only allow one dependent pin
inPin->dependentPin = this;

// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
// order of operations for global constructors is not defined.
// update();
}

/**
* Update the output pin based on the current state of the input pin.
*/
void GpioNotTransformer::update()
{
auto p = inPin->get();
if (p == GpioVirtPin::PinState::Unset)
return; // Not yet fully initialized

set(!p);
}

GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation)
: GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation)
{
assert(!inPin1->dependentPin); // We only allow one dependent pin
inPin1->dependentPin = this;
assert(!inPin2->dependentPin); // We only allow one dependent pin
inPin2->dependentPin = this;

// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
// order of operations for global constructors is not defined.
// update();
}

void GpioBinaryTransformer::update()
{
auto p1 = inPin1->get(), p2 = inPin2->get();
GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset;

if (p1 == GpioVirtPin::PinState::Unset)
newValue = p2; // Not yet fully initialized
else if (p2 == GpioVirtPin::PinState::Unset)
newValue = p1; // Not yet fully initialized

// If we've already found our value just use it, otherwise need to do the operation
if (newValue == GpioVirtPin::PinState::Unset) {
switch (operation) {
case And:
newValue = (GpioVirtPin::PinState)(p1 && p2);
break;
case Or:
newValue = (GpioVirtPin::PinState)(p1 || p2);
break;
case Xor:
newValue = (GpioVirtPin::PinState)(p1 != p2);
break;
default:
assert(false);
}
}
set(newValue);
}

GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {}
144 changes: 144 additions & 0 deletions src/GpioLogic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#pragma once

#include "configuration.h"

/**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not
require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared power enable)
then using these classes might be able to let you cleanly turn on that enable when either dependent device is needed.
Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM
requirements.
*/

/**
* A logical GPIO pin (not necessary raw hardware).
*/
class GpioPin
{
public:
virtual void set(bool value) = 0;
};

/**
* A physical GPIO hw pin.
*/
class GpioHwPin : public GpioPin
{
uint32_t num;

public:
explicit GpioHwPin(uint32_t num) : num(num) {}

void set(bool value) { digitalWrite(num, value); }
};

class GpioTransformer;
class GpioNotTransformer;
class GpioBinaryTransformer;

/**
* A virtual GPIO pin.
*/
class GpioVirtPin : public GpioPin
{
friend class GpioBinaryTransformer;
friend class GpioNotTransformer;

public:
enum PinState { On = true, Off = false, Unset = 2 };

void set(bool value);
PinState get() const { return value; }

private:
PinState value = PinState::Unset;
GpioTransformer *dependentPin = NULL;
};

#include <assert.h>

/**
* A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to change.
* notably: the set method is not public (because it always is calculated by a subclass)
*/
class GpioTransformer
{
public:
/**
* Update the output pin based on the current state of the input pin.
*/
virtual void update() = 0;

protected:
GpioTransformer(GpioPin *outPin);

void set(bool value);

private:
GpioPin *outPin;
};

/**
* A transformer that performs a unary NOT operation from an input.
*/
class GpioNotTransformer : public GpioTransformer
{
public:
GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin);

protected:
friend class GpioVirtPin;

/**
* Update the output pin based on the current state of the input pin.
*/
void update();

private:
GpioVirtPin *inPin;
};

/**
* A transformer that combines multiple virtual pins to drive an output pin
*/
class GpioBinaryTransformer : public GpioTransformer
{

public:
enum Operation { And, Or, Xor };

GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation);

protected:
friend class GpioVirtPin;

/**
* Update the output pin based on the current state of the input pins.
*/
void update();

private:
GpioVirtPin *inPin1;
GpioVirtPin *inPin2;
Operation operation;
};

/**
* Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that.
*/
class GpioSplitter : public GpioPin
{

public:
GpioSplitter(GpioPin *outPin1, GpioPin *outPin2);

void set(bool value)
{
outPin1->set(value);
outPin2->set(value);
}

private:
GpioPin *outPin1;
GpioPin *outPin2;
};
66 changes: 66 additions & 0 deletions src/Led.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "Led.h"
#include "PowerMon.h"
#include "main.h"
#include "power.h"

GpioVirtPin ledForceOn, ledBlink;

#if defined(LED_PIN)
// Most boards have a GPIO for LED control
static GpioHwPin ledRawHwPin(LED_PIN);
#else
static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
#endif

#if LED_STATE_ON == 0
static GpioVirtPin ledHwPin;
static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin);
#else
static GpioPin &ledHwPin = ledRawHwPin;
#endif

#if defined(HAS_PMU)
/**
* A GPIO controlled by the PMU
*/
class GpioPmuPin : public GpioPin
{
public:
void set(bool value)
{
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
}
} ledPmuHwPin;

// In some cases we need to drive a PMU LED and a normal LED
static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin);
#else
static GpioPin &ledFinalPin = ledHwPin;
#endif

#ifdef USE_POWERMON
/**
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
*/
class MonitoredLedPin : public GpioPin
{
public:
void set(bool value)
{
if (powerMon) {
if (value)
powerMon->setState(meshtastic_PowerMon_State_LED_On);
else
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
}
ledFinalPin.set(value);
}
} monitoredLedPin;
#else
static GpioPin &monitoredLedPin = ledFinalPin;
#endif

static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or);
7 changes: 7 additions & 0 deletions src/Led.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "GpioLogic.h"
#include "configuration.h"

/**
* ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main)
*/
extern GpioVirtPin ledForceOn, ledBlink;
3 changes: 0 additions & 3 deletions src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor;
#endif

#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
XPowersLibInterface *PMU = NULL;
#else

Expand Down
Loading

0 comments on commit 4eb1732

Please sign in to comment.