Skip to content

Commit

Permalink
for meshtastic#4154 and meshtastic#4136 add concept of dependent gpio…
Browse files Browse the repository at this point in the history
…s...

Which is currently only tested with the LED but eventually
will be used for shared GPIO/screen power rail enable
and LED forcing (which is a sanity check in the power stress
testing)
  • Loading branch information
geeksville committed Jun 30, 2024
1 parent 469ae0f commit db68c88
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 25 deletions.
80 changes: 80 additions & 0 deletions src/GpioLogic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#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();
}
}

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

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

GpioNotPin::GpioNotPin(GpioVirtPin *inPin, GpioPin *outPin) : GpioLogicPin(outPin), inPin(inPin)
{
assert(!inPin->dependentPin); // We only allow one dependent pin
inPin->dependentPin = this;
update();
}
void GpioNotPin::set(bool value)
{
outPin->set(value);
}

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

set(!p);
}

GpioBinaryLogicPin::GpioBinaryLogicPin(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation)
: GpioLogicPin(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;
update();
}

void GpioBinaryLogicPin::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);
}
125 changes: 125 additions & 0 deletions src/GpioLogic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#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 GpioLogicPin;
class GpioNotPin;
class GpioBinaryLogicPin;

/**
* A virtual GPIO pin.
*/
class GpioVirtPin : public GpioPin
{
friend class GpioBinaryLogicPin;
friend class GpioNotPin;

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

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

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

#include <assert.h>

/**
* A logical GPIO pin baseclass, notably: the set method is not public (because it always is calculated by a subclass)
*/
class GpioLogicPin
{
public:
/**
* Update the output pin based on the current state of the input pin.
*/
virtual void update() = 0;

protected:
GpioLogicPin(GpioPin *outPin);

void set(bool value);

private:
GpioPin *outPin;
};

/**
* A logical GPIO pin that performs a unary NOT operation on a virtual pin.
*/
class GpioNotPin : public GpioLogicPin
{
public:
GpioNotPin(GpioVirtPin *inPin, GpioPin *outPin);
void set(bool value);

protected:
friend class GpioVirtPin;

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

private:
GpioVirtPin *inPin;
GpioPin *outPin;
};

/**
* A logical GPIO pin that combines multiple virtual pins to drive a real physical pin
*/
class GpioBinaryLogicPin : public GpioLogicPin
{

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

GpioBinaryLogicPin(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;
};
37 changes: 37 additions & 0 deletions src/Led.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "Led.h"

GpioVirtPin ledForceOn, ledBlink;

#if defined(LED_PIN)

// Most boards have a GPIO for LED control
GpioHwPin ledRawHwPin(LED_PIN);

#elif 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(ledOn ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
}
} ledRawHwPin;

#else
GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
#endif

#if LED_INVERTED
GpioPin ledHwPin = GpioNotPin(&ledRawHwPin);
#else
GpioPin &ledHwPin = ledRawHwPin;
#endif

static GpioBinaryLogicPin ledForcer(&ledForceOn, &ledBlink, &ledHwPin, GpioBinaryLogicPin::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;
7 changes: 4 additions & 3 deletions src/PowerFSM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
#include "PowerFSM.h"
#include "Default.h"
#include "Led.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "configuration.h"
Expand Down Expand Up @@ -87,14 +88,14 @@ static void lsIdle()
// Briefly come out of sleep long enough to blink the led once every few seconds
uint32_t sleepTime = SLEEP_TIME;

setLed(false); // Never leave led on while in light sleep
ledBlink.set(false); // Never leave led on while in light sleep
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);

switch (wakeCause2) {
case ESP_SLEEP_WAKEUP_TIMER:
// Normal case: timer expired, we should just go back to sleep ASAP

setLed(true); // briefly turn on led
ledBlink.set(true); // briefly turn on led
wakeCause2 = doLightSleep(100); // leave led on for 1ms

secsSlept += sleepTime;
Expand Down Expand Up @@ -129,7 +130,7 @@ static void lsIdle()
}
} else {
// Time to stop sleeping!
setLed(false);
ledBlink.set(false);
LOG_INFO("Reached ls_secs, servicing loop()\n");
powerFSM.trigger(EVENT_WAKE_TIMER);
}
Expand Down
3 changes: 2 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "power.h"
// #include "debug.h"
#include "FSCommon.h"
#include "Led.h"
#include "RTC.h"
#include "SPILock.h"
#include "concurrency/OSThread.h"
Expand Down Expand Up @@ -191,7 +192,7 @@ static int32_t ledBlinker()
static bool ledOn;
ledOn ^= 1;

setLed(ledOn);
ledBlink.set(ledOn);

// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000);
Expand Down
6 changes: 3 additions & 3 deletions src/mesh/http/ContentHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
#if !MESHTASTIC_EXCLUDE_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "Led.h"
#include "mqtt/JSON.h"
#include "power.h"
#include "sleep.h"
#include <FSCommon.h>
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
Expand Down Expand Up @@ -798,9 +798,9 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
if (blink_target == "LED") {
uint8_t count = 10;
while (count > 0) {
setLed(true);
ledBlink.set(true);
delay(50);
setLed(false);
ledBlink.set(false);
delay(50);
count = count - 1;
}
Expand Down
20 changes: 3 additions & 17 deletions src/sleep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#endif

#include "ButtonThread.h"
#include "Led.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
Expand Down Expand Up @@ -83,21 +84,6 @@ void setCPUFast(bool on)
#endif
}

void setLed(bool ledOn)
{
#ifdef LED_PIN
// toggle the led so we can get some rough sense of how often loop is pausing
digitalWrite(LED_PIN, ledOn ^ LED_INVERTED);
#endif

#ifdef HAS_PMU
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(ledOn ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
#endif
}

// Perform power on init that we do on each wake from deep sleep
void initDeepSleep()
{
Expand Down Expand Up @@ -240,7 +226,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
if (gps)
gps->setGPSPower(false, false, 0);
#endif
setLed(false);
ledBlink.set(false);

#ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
Expand Down Expand Up @@ -500,4 +486,4 @@ void enableLoraInterrupt()
}
#endif
}
#endif
#endif
1 change: 0 additions & 1 deletion src/sleep.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ extern XPowersLibInterface *PMU;
void initDeepSleep();

void setCPUFast(bool on);
void setLed(bool ledOn);

/** return true if sleep is allowed right now */
bool doPreflightSleep();
Expand Down

0 comments on commit db68c88

Please sign in to comment.