Доработки по тесту драйвера
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user