#pragma once #include #include #include #include "Config.h" #include "Core.h" #if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) #define OPTICAL_USE_MCPWM_CAPTURE 1 #include #else #define OPTICAL_USE_MCPWM_CAPTURE 0 #endif enum class CaptureSource : uint8_t { RX, TX, WINDOW_END }; struct CaptureEvent { uint64_t tick; bool rising; CaptureSource source; }; class PulseReceiver { public: bool begin(); bool start(uint32_t expectedHz, float expectedDutyPct, bool activeLightOn); bool startDriver(uint32_t frequencyHz, uint32_t pulseNs, bool activeTxLightOn); void stop(); void resetStream(); size_t readPeriods(PulsePeriod *periods, size_t capacity, TickType_t waitTicks = 0); size_t readEvents(CaptureEvent *events, size_t capacity, TickType_t waitTicks = 0); uint32_t takeDroppedItems(); uint32_t tickHz() const; uint32_t pulseTickHz() const { return tickHz(); } uint32_t plannedTickHz(uint32_t expectedHz, float expectedDutyPct) const; uint32_t plannedPulseTickHz(uint32_t expectedHz, float expectedDutyPct) const { return plannedTickHz(expectedHz, expectedDutyPct); } uint16_t receiveChunkSymbols() const { return 1; } bool highRateBackend() const { return OPTICAL_USE_MCPWM_CAPTURE; } private: struct Edge { uint32_t tick; uint8_t rising; uint8_t source; }; static constexpr uint16_t DRIVER_RING_CAPACITY = 512; static_assert((DRIVER_RING_CAPACITY & (DRIVER_RING_CAPACITY - 1U)) == 0, "driver capture ring must be a power of two"); struct TimedEdge { uint64_t tick; bool rising; }; bool startCapture(bool withTx); bool consumeEdge(const Edge &edge, PulsePeriod &period); bool nextOrderedEdge(Edge &edge, TickType_t waitTicks); TimedEdge extendEdge(const Edge &edge); #if OPTICAL_USE_MCPWM_CAPTURE bool configureDriverTxCapture(bool risingEdge); static bool IRAM_ATTR onCapture(mcpwm_cap_channel_handle_t, const mcpwm_capture_event_data_t *, void *); mcpwm_cap_timer_handle_t captureTimer_ = nullptr; mcpwm_cap_channel_handle_t risingChannel_ = nullptr; mcpwm_cap_channel_handle_t fallingChannel_ = nullptr; mcpwm_cap_channel_handle_t txChannel_ = nullptr; uint32_t captureResolutionHz_ = 0; #else static void IRAM_ATTR onGpio(void *ctx); uint32_t cpuTickHz_ = 0; #endif QueueHandle_t queue_ = nullptr; Edge driverRing_[DRIVER_RING_CAPACITY] = {}; volatile uint16_t driverRingWrite_ = 0; volatile uint16_t driverRingRead_ = 0; portMUX_TYPE driverRingMux_ = portMUX_INITIALIZER_UNLOCKED; Edge lastDriverEdge_ = {}; bool haveLastDriverEdge_ = false; uint32_t driverPulseTicks_ = 0; uint32_t driverReleaseSlackTicks_ = 0; Edge reorderEdge_ = {}; bool haveReorderEdge_ = false; volatile uint32_t droppedItems_ = 0; volatile bool running_ = false; volatile bool txCaptureEnabled_ = false; uint32_t expectedHz_ = 0; float expectedDutyPct_ = 50.0f; bool polarityKnown_ = false, activeStartRising_ = false; TimedEdge polarityEdges_[3] = {}; uint8_t polarityEdgeCount_ = 0; bool waitingForActiveEnd_ = true; uint64_t activeStart_ = 0, activeEnd_ = 0; bool haveRawTick_ = false; uint32_t lastRawTick_ = 0; uint64_t tickEpoch_ = 0; };