Доработки по тесту драйвера

This commit is contained in:
2026-08-14 14:39:39 +03:00
parent 037bb37e62
commit 035792aedd
10 changed files with 307 additions and 140 deletions

View File

@@ -75,6 +75,7 @@ bool PulseReceiver::begin() {
bool PulseReceiver::start(uint32_t expectedHz, float expectedDutyPct,
bool activeLightOn) {
(void)activeLightOn;
if (!plannedTickHz(expectedHz, expectedDutyPct)) return false;
expectedHz_ = expectedHz;
expectedDutyPct_ = expectedDutyPct;
@@ -83,11 +84,7 @@ bool PulseReceiver::start(uint32_t expectedHz, float expectedDutyPct,
if (!cpuTickHz_) return false;
#endif
resetStream();
const bool rawHighMeansLightOn = RX_LIGHT_ON_GPIO_LEVEL == HIGH;
activeStartRising_ = rawHighMeansLightOn == activeLightOn;
Log::printf("CAPTURE", "RX active optical level=%s, raw active starts on %s",
activeLightOn ? "H/light-on" : "L/light-off",
activeStartRising_ ? "RISING" : "FALLING");
Log::event("CAPTURE", "RX optical polarity will be detected automatically");
return startCapture(false);
}
@@ -209,6 +206,8 @@ void PulseReceiver::resetStream() {
droppedItems_ = 0;
polarityKnown_ = false;
activeStartRising_ = false;
polarityEdgeCount_ = 0;
memset(polarityEdges_, 0, sizeof(polarityEdges_));
waitingForActiveEnd_ = true;
activeStart_ = activeEnd_ = 0;
haveRawTick_ = false;
@@ -244,12 +243,42 @@ bool PulseReceiver::consumeEdge(const Edge &rawEdge, PulsePeriod &out) {
return true;
}
// HH/HL/LH/LL defines the active optical state explicitly. Synchronize on
// its physical starting edge instead of guessing polarity from pulse width.
if (edge.rising != activeStartRising_) return false;
// Optical mode does not use the DRIVER level setting. Compare the first two
// alternating intervals with the configured active duration and select the
// level that is actually present at RX. Three edges are enough to determine
// polarity and, when the first interval is active, produce the first period.
polarityEdges_[polarityEdgeCount_++] = edge;
if (polarityEdgeCount_ < 3U) return false;
const uint64_t firstInterval =
polarityEdges_[1].tick - polarityEdges_[0].tick;
const uint64_t secondInterval =
polarityEdges_[2].tick - polarityEdges_[1].tick;
const uint64_t expectedPeriod = tickHz() / expectedHz_;
const uint64_t expectedActive = static_cast<uint64_t>(
expectedPeriod * expectedDutyPct_ / 100.0f + 0.5f);
const uint64_t firstError = firstInterval > expectedActive ?
firstInterval - expectedActive : expectedActive - firstInterval;
const uint64_t secondError = secondInterval > expectedActive ?
secondInterval - expectedActive : expectedActive - secondInterval;
const bool firstIntervalIsActive = firstError <= secondError;
activeStartRising_ = firstIntervalIsActive ? polarityEdges_[0].rising :
polarityEdges_[1].rising;
polarityKnown_ = true;
activeStart_ = edge.tick;
waitingForActiveEnd_ = true;
polarityEdgeCount_ = 0;
if (firstIntervalIsActive) {
out = {polarityEdges_[0].tick,
static_cast<uint32_t>(polarityEdges_[2].tick - polarityEdges_[0].tick),
static_cast<uint32_t>(firstInterval), tickHz()};
activeStart_ = polarityEdges_[2].tick;
waitingForActiveEnd_ = true;
return true;
}
activeStart_ = polarityEdges_[1].tick;
activeEnd_ = polarityEdges_[2].tick;
waitingForActiveEnd_ = false;
return false;
}
@@ -268,10 +297,11 @@ bool IRAM_ATTR PulseReceiver::onCapture(mcpwm_cap_channel_handle_t channel,
static_cast<uint8_t>(channel == self->txChannel_ ? CaptureSource::TX :
CaptureSource::RX)};
if (self->txCaptureEnabled_) {
// Three capture channels are independent ISR producers. Serialize their
// reservation/publication of a ring slot; treating this as an SPSC ring
// loses or duplicates RX events when TX and RX interrupts overlap.
portENTER_CRITICAL_ISR(&self->driverRingMux_);
// All channels in one MCPWM group are dispatched serially by the same
// group ISR. Keep this callback shorter than the minimum interval between
// equal RX edges (about 2.1 us at W=2 us): a spinlock and several atomic
// RMW operations here can leave the channel pending until its capture
// register is overwritten by the next edge.
if (self->haveLastDriverEdge_ &&
self->lastDriverEdge_.tick == edge.tick &&
self->lastDriverEdge_.rising == edge.rising &&
@@ -279,22 +309,20 @@ bool IRAM_ATTR PulseReceiver::onCapture(mcpwm_cap_channel_handle_t channel,
// The same channel callback can be delivered twice while several MCPWM
// capture status bits are pending. Two physical edges cannot have the
// same source, direction and 12.5 ns hardware timestamp.
portEXIT_CRITICAL_ISR(&self->driverRingMux_);
return false;
}
const uint16_t write = __atomic_load_n(
&self->driverRingWrite_, __ATOMIC_RELAXED);
const uint16_t write = self->driverRingWrite_;
const uint16_t next = static_cast<uint16_t>(
(write + 1U) % DRIVER_RING_CAPACITY);
if (next == __atomic_load_n(&self->driverRingRead_, __ATOMIC_ACQUIRE)) {
__atomic_fetch_add(&self->droppedItems_, 1U, __ATOMIC_RELAXED);
(write + 1U) & (DRIVER_RING_CAPACITY - 1U));
if (next == self->driverRingRead_) {
++self->droppedItems_;
} else {
self->driverRing_[write] = edge;
self->lastDriverEdge_ = edge;
self->haveLastDriverEdge_ = true;
__atomic_store_n(&self->driverRingWrite_, next, __ATOMIC_RELEASE);
asm volatile("memw" ::: "memory");
self->driverRingWrite_ = next;
}
portEXIT_CRITICAL_ISR(&self->driverRingMux_);
return false;
}
BaseType_t wake = pdFALSE;
@@ -350,7 +378,7 @@ size_t PulseReceiver::readPeriods(PulsePeriod *periods, size_t capacity,
size_t PulseReceiver::readEvents(CaptureEvent *events, size_t capacity,
TickType_t waitTicks) {
if (!events || capacity < 2U || !txCaptureEnabled_ || !driverPulseTicks_ ||
if (!events || capacity < 3U || !txCaptureEnabled_ || !driverPulseTicks_ ||
!driverReleaseSlackTicks_) return 0;
constexpr size_t MAX_BATCH = 64;
@@ -367,30 +395,42 @@ size_t PulseReceiver::readEvents(CaptureEvent *events, size_t capacity,
// can the missing start interrupt be reconstructed and sorted before RX.
const uint16_t write = __atomic_load_n(&driverRingWrite_, __ATOMIC_ACQUIRE);
uint16_t scan = read;
bool haveTxEnd = false;
uint32_t lastTxEnd = 0;
bool haveLatestTxEnd = false;
bool haveReleaseTxEnd = false;
uint32_t latestTxEnd = 0;
uint32_t releaseTxEnd = 0;
size_t projectedCount = 0;
while (scan != write) {
const Edge &edge = driverRing_[scan];
const size_t needed = edge.source == static_cast<uint8_t>(CaptureSource::TX)
? 2U : 1U;
if (projectedCount + needed > limit) break;
// Reserve one output slot for WINDOW_END.
if (projectedCount + needed + 1U > limit) break;
projectedCount += needed;
if (edge.source == static_cast<uint8_t>(CaptureSource::TX)) {
lastTxEnd = edge.tick;
haveTxEnd = true;
if (haveLatestTxEnd) {
releaseTxEnd = latestTxEnd;
haveReleaseTxEnd = true;
}
latestTxEnd = edge.tick;
haveLatestTxEnd = true;
}
scan = static_cast<uint16_t>((scan + 1U) % DRIVER_RING_CAPACITY);
}
if (!haveTxEnd) {
// Keep the newest TX period in the ring. Arrival of the following TX end
// proves that the previous end's ACK/fault window has completely elapsed.
if (!haveReleaseTxEnd) {
if (waitTicks) vTaskDelay(waitTicks);
return 0;
}
const uint32_t releaseThrough = lastTxEnd + driverReleaseSlackTicks_;
const uint32_t releaseThrough = releaseTxEnd + driverReleaseSlackTicks_;
size_t count = 0;
while (read != write) {
const Edge edge = driverRing_[read];
if (edge.source == static_cast<uint8_t>(CaptureSource::TX) &&
static_cast<int32_t>(edge.tick - releaseTxEnd) > 0)
break;
if (edge.source == static_cast<uint8_t>(CaptureSource::RX) &&
static_cast<int32_t>(edge.tick - releaseThrough) > 0)
break;
@@ -426,5 +466,9 @@ size_t PulseReceiver::readEvents(CaptureEvent *events, size_t capacity,
events[i] = {timed.tick, timed.rising,
static_cast<CaptureSource>(ordered[i].source)};
}
const Edge marker = {releaseThrough, 0,
static_cast<uint8_t>(CaptureSource::WINDOW_END)};
const TimedEdge timedMarker = extendEdge(marker);
events[count++] = {timedMarker.tick, false, CaptureSource::WINDOW_END};
return count;
}