Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 15 additions & 4 deletions include/v2/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
*/
#pragma once

#include <tempo/bus/event.h>

#include <cstdint>

#include <tempo/bus/event.h>

#include "v2/pocketpd.h"

namespace pocketpd {
Expand Down Expand Up @@ -65,7 +65,7 @@ namespace pocketpd {
};

/**
* @brief Latest sensor reading from INA226.
* @brief Load-side reading from INA226.
*/
struct LoadReading {
uint32_t timestamp_ms = 0;
Expand All @@ -74,10 +74,21 @@ namespace pocketpd {
};

/**
* @brief Published by SensorTask. Carries one bus reading.
* @brief Supply-side reading Sourced from the V_SENSE ADC divider on HW1.3+ or the AP33772
* VOLTAGE register on earlier board.
*/
struct SupplyReading {
uint32_t timestamp_ms = 0;
uint32_t mv = 0;
bool valid = false;
};

/**
* @brief Published by SensorTask
*/
struct SensorEvent {
LoadReading load;
SupplyReading supply;
};

/**
Expand Down
40 changes: 40 additions & 0 deletions include/v2/hal/adc_supply_voltage_source.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @file adc_supply_voltage_source.h
* @brief ADC-backed `SupplyVoltageSource` for HW1.3+ (V_SENSE divider into ADC3).
*/
#pragma once

#include <Arduino.h>

#include <cstdint>

#include "v2/hal/supply_voltage_source.h"

namespace pocketpd {

class AdcSupplyVoltageSource : public SupplyVoltageSource {
private:
uint8_t m_pin;

// RP2040 ADC: 12-bit, 3.3 V reference. V_SENSE divider = 2/13 of VBUS.
static constexpr uint32_t ADC_REF_MV = 3300;
static constexpr uint16_t ADC_MAX = 4095;
static constexpr uint32_t DIVIDER_NUM = 13;
static constexpr uint32_t DIVIDER_DEN = 2;

public:
explicit AdcSupplyVoltageSource(uint8_t pin) : m_pin(pin) {}

void begin() override {
analogReadResolution(12);
}

SupplyVoltageReading read() override {
const uint32_t raw = analogRead(m_pin);
const uint32_t adc_mv = (raw * ADC_REF_MV) / ADC_MAX;
const uint32_t vbus_mv = (adc_mv * DIVIDER_NUM) / DIVIDER_DEN;
return SupplyVoltageReading{vbus_mv, true};
}
};

} // namespace pocketpd
3 changes: 3 additions & 0 deletions include/v2/hal/ap33772_pd_sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ namespace pocketpd {
bool set_pps_pdo(int index, int voltage_mv, int current_ma) override {
return m_driver.set_pps_pdo(index, voltage_mv, current_ma);
}
int read_vbus_mv() override {
return m_driver.read_voltage();
}
bool reset() const {
return m_driver.reset();
}
Expand Down
31 changes: 31 additions & 0 deletions include/v2/hal/ap33772_supply_voltage_source.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @file ap33772_supply_voltage_source.h
* @brief `SupplyVoltageSource` backed by the AP33772 VOLTAGE register, for
* boards without the V_SENSE ADC divider (HW < 1.3).
*/
#pragma once

#include "v2/hal/pd_sink_controller.h"
#include "v2/hal/supply_voltage_source.h"

namespace pocketpd {

class Ap33772SupplyVoltageSource : public SupplyVoltageSource {
private:
PdSinkController& m_sink;

public:
explicit Ap33772SupplyVoltageSource(PdSinkController& sink) : m_sink(sink) {}

void begin() override {}

SupplyVoltageReading read() override {
const int mv = m_sink.read_vbus_mv();
if (mv < 0) {
return SupplyVoltageReading{0, false};
}
return SupplyVoltageReading{static_cast<uint32_t>(mv), true};
}
};

} // namespace pocketpd
8 changes: 8 additions & 0 deletions include/v2/hal/pd_sink_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ namespace pocketpd {

/** @brief Request a PPS APDO with explicit voltage and current. */
[[nodiscard]] virtual bool set_pps_pdo(int index, int voltage_mv, int current_ma) = 0;

// —— Live readings

/**
* @brief Read VBUS voltage from the sink in mV. Returns -1 on I2C error.
* Used by Ap33772VSenseSource on HW < 1.3 (no ADC voltage divider).
*/
virtual int read_vbus_mv() = 0;
};

} // namespace pocketpd
32 changes: 32 additions & 0 deletions include/v2/hal/supply_voltage_source.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @file supply_voltage_source.h
* @brief Abstract source for the supply-side voltage.
*
* HW1.3+ reads through the V_SENSE ADC divider. Earlier boards query the
* AP33772 VOLTAGE register through `PdSinkController`.
*/
#pragma once

#include <cstdint>

namespace pocketpd {

/**
* @brief One supply-voltage sample. `valid` false on hardware read error.
*/
struct SupplyVoltageReading {
uint32_t mv = 0;
bool valid = false;
};

class SupplyVoltageSource {
public:
virtual ~SupplyVoltageSource() = default;

virtual void begin() = 0;

/** @brief Sample the supply voltage. */
virtual SupplyVoltageReading read() = 0;
};

} // namespace pocketpd
6 changes: 3 additions & 3 deletions include/v2/stages/normal/normal_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace pocketpd {
bool locked = false;
uint8_t arrow_frame = 0;
LoadReading load_reading{};
SupplyReading supply_reading{};

// —— PPS branch (valid when has_profile && is_pps)

Expand Down Expand Up @@ -79,9 +80,8 @@ namespace pocketpd {
std::array<char, 32> buf{};

d.set_font(tempo::Font::XL);
draw_measured(
d, "V", V_MEASURED_Y, vm.load_reading.vbus_mv, buf, vm.readout_visible
);
const uint32_t v_mv = vm.output_enabled ? vm.load_reading.vbus_mv : vm.supply_reading.mv;
draw_measured(d, "V", V_MEASURED_Y, v_mv, buf, vm.readout_visible);
draw_measured(
d, "A", A_MEASURED_Y, vm.load_reading.current_ma, buf, vm.readout_visible
);
Expand Down
10 changes: 10 additions & 0 deletions include/v2/stages/normal_stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ namespace pocketpd {
OutputGate& m_output_gate;

LoadReading m_load_reading{};
SupplyReading m_supply_reading{};
bool m_load_init = false;
bool m_supply_init = false;

int8_t m_active_pdo_index = -1;
int8_t m_last_active_index = -1;
Expand All @@ -47,6 +49,7 @@ namespace pocketpd {
bool m_blink_visible = true;

static constexpr uint32_t SENSOR_EMA_DEN = 4;
static constexpr uint32_t SUPPLY_EMA_DEN = 8;
static constexpr uint32_t READOUT_BLINK_ON_MS = 1200;
static constexpr uint32_t READOUT_BLINK_OFF_MS = 400;
static constexpr uint32_t READOUT_BLINK_CYCLE_MS =
Expand Down Expand Up @@ -190,6 +193,12 @@ namespace pocketpd {
? Filter::ema(m_load_reading, evt.load, SENSOR_EMA_DEN)
: evt.load;
m_load_init = true;
if (evt.supply.valid) {
m_supply_reading = m_supply_init
? Filter::ema(m_supply_reading, evt.supply, SUPPLY_EMA_DEN)
: evt.supply;
m_supply_init = true;
}
},
[](const auto&) {},
};
Expand Down Expand Up @@ -244,6 +253,7 @@ namespace pocketpd {
.locked = m_locked,
.arrow_frame = m_arrow_frame,
.load_reading = m_load_reading,
.supply_reading = m_supply_reading,
};

if (!vm.has_profile) {
Expand Down
22 changes: 14 additions & 8 deletions include/v2/tasks/sensor_task.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @file sensor_task.h
* @brief Reads PowerMonitor at 33 ms while NormalStage is active and publishes
* a `SensorEvent` carrying the snapshot. NormalStage consumes the event.
* @brief Polls PowerMonitor and SupplyVoltageSource at 40 ms while NormalStage,
* EnergyStage, or ProfilePickerStage is active. Publishes a fused `SensorEvent`.
*/
#pragma once

Expand All @@ -10,30 +10,36 @@
#include "v2/app.h"
#include "v2/events.h"
#include "v2/hal/power_monitor.h"
#include "v2/hal/supply_voltage_source.h"
#include "v2/pocketpd.h"

namespace pocketpd {

class SensorTask : public App::StageScopedTask, public App::UsePublisher<SensorTask> {
private:
PowerMonitor& m_monitor;
SupplyVoltageSource& m_supply;

public:
static constexpr uint32_t PERIOD_MS = 33;
static constexpr uint32_t PERIOD_MS = 40;

explicit SensorTask(PowerMonitor& monitor)
SensorTask(PowerMonitor& monitor, SupplyVoltageSource& supply)
: App::StageScopedTask(
PERIOD_MS,
App::StageMask::of<NormalStage, EnergyStage, ProfilePickerStage>()
),
m_monitor(monitor) {}
m_monitor(monitor), m_supply(supply) {}

void on_tick(uint32_t now_ms) override {
const auto reading = m_monitor.read();
if (!reading.valid) {
const auto load = m_monitor.read();
const auto supply = m_supply.read();
if (!load.valid && !supply.valid) {
return;
}
publish(SensorEvent{LoadReading{now_ms, reading.mv, reading.ma}});
publish(SensorEvent{
LoadReading{now_ms, load.mv, load.ma},
SupplyReading{now_ms, supply.mv, supply.valid},
});
}
};

Expand Down
10 changes: 10 additions & 0 deletions include/v2/util/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ namespace pocketpd {
.current_ma = ema(prev.current_ma, sample.current_ma, den),
};
}

/** @brief Per-field EMA across a `SupplyReading`. Caller filters invalid samples. */
static SupplyReading
ema(const SupplyReading& prev, const SupplyReading& sample, uint32_t den) {
return {
.timestamp_ms = sample.timestamp_ms,
.mv = ema(prev.mv, sample.mv, den),
.valid = sample.valid,
};
}
};

} // namespace pocketpd
2 changes: 2 additions & 0 deletions lib/PocketPDPinOut/PocketPDPinOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
#define pin_SDA 4
#define pin_SCL 5

#define pin_VSENSE 29 // ADC3 — V_SENSE divider (2/13 of VBUS)

11 changes: 10 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "v2/app.h"
#include "v2/hal/ap33772_pd_sink.h"
#include "v2/hal/ap33772_supply_voltage_source.h"
#include "v2/hal/adc_supply_voltage_source.h"
#include "v2/hal/arduino_clock.h"
#include "v2/hal/arduino_output_gate.h"
#include "v2/hal/arduino_stream_reader.h"
Expand Down Expand Up @@ -40,6 +42,12 @@ namespace pocketpd {
INA226 ina226_driver{INA226_I2C_ADDR};
Ina226PowerMonitor power_monitor{ina226_driver};

#if HW_VERSION_MAJOR == 1 && HW_VERSION_MINOR >= 3
AdcSupplyVoltageSource supply_voltage_source{pin_VSENSE};
#else
Ap33772SupplyVoltageSource supply_voltage_source{pd_sink};
#endif

U8g2Display u8g2_display;

ArduinoOutputGate output_gate{pin_output_Enable};
Expand All @@ -61,7 +69,7 @@ namespace pocketpd {

ButtonTask button_task(encoder_button, l_button, r_button);
EncoderTask encoder_task(encoder);
SensorTask sensor_task{power_monitor};
SensorTask sensor_task{power_monitor, supply_voltage_source};
EnergyTask energy_task{output_gate};
CommandTask command_task{arduino_stream_reader, arduino_stream_writer};

Expand All @@ -78,6 +86,7 @@ void setup() {

ina226_driver.begin();
power_monitor.begin();
supply_voltage_source.begin();
u8g2_display.begin();
output_gate.begin();
encoder.begin();
Expand Down
1 change: 1 addition & 0 deletions test/mocks/MockPdSink.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace pocketpd {
MOCK_METHOD(int, pdo_max_current_ma, (int index), (const, override));
MOCK_METHOD(bool, set_pdo, (int index), (override));
MOCK_METHOD(bool, set_pps_pdo, (int index, int voltage_mv, int current_ma), (override));
MOCK_METHOD(int, read_vbus_mv, (), (override));
};

} // namespace pocketpd
Loading
Loading