archive: freeze IR-protocol WIP before stepwise integration

This commit is contained in:
2026-08-28 13:27:46 +03:00
parent 96ffb91b97
commit 57db9c35b8
18 changed files with 1848 additions and 391 deletions

View File

@ -120,7 +120,23 @@ public:
if (enc == nullptr) return IR_SendStatus::ExternalNoStream;
for (uint8_t i = 0; i < streamCount_; i++) {
if (streams_[i].enc == enc) {
return startStream(streams_[i], packet, len);
const IR_TxSnapshot snapshot = enc->txSnapshot();
const IR_TxPlan plan = enc->planTransmission(packet, len);
return startStream(streams_[i], packet, len, plan, snapshot.operationId);
}
}
return IR_SendStatus::ExternalNoStream;
}
IR_SendStatus startTracked(IR_Encoder* enc,
const uint8_t* packet,
uint8_t len,
const IR_TxPlan& plan,
uint32_t operationId) {
if (enc == nullptr) return IR_SendStatus::ExternalNoStream;
for (uint8_t i = 0; i < streamCount_; i++) {
if (streams_[i].enc == enc) {
return startStream(streams_[i], packet, len, plan, operationId);
}
}
return IR_SendStatus::ExternalNoStream;
@ -192,6 +208,7 @@ private:
uint32_t totalTicks = 0;
volatile uint32_t ticksOutput = 0;
uint32_t operationId = 0;
// Fix D (watchdog): прогресс ticksOutput против стенных часов (контекст потока).
uint32_t lastTicks = 0;
@ -204,20 +221,23 @@ private:
ticksOutput = 0;
totalTicks = 0;
runCount = 0;
operationId = 0;
}
IR_DMA_TX_HOT void fill(uint32_t* dst, uint16_t count) {
wave.fill(dst, count);
}
void onHalf() {
void advanceHalf() {
ticksOutput += halfLen;
}
void refillFirstHalf() {
fill(&dmaBuf[0], halfLen);
__DSB(); // Fix #8: refill первой половины виден DMA до следующего прохода кольца
}
void onComplete() {
ticksOutput += halfLen;
void refillSecondHalf() {
fill(&dmaBuf[halfLen], halfLen);
__DSB(); // Fix #8: refill второй половины виден DMA до следующего прохода кольца
}
@ -262,7 +282,7 @@ private:
void forceStop(TxStream& s) {
HAL_NVIC_DisableIRQ(s.dmaIrq);
if (s.active) {
stopStream(s);
stopStream(s, IR_SendStatus::DmaStalled);
recoveries_++;
}
HAL_NVIC_EnableIRQ(s.dmaIrq);
@ -281,18 +301,22 @@ private:
static void dmaHalfCpltCb(DMA_HandleTypeDef* hdma) {
auto* s = streamFromDma(hdma);
if (s == nullptr || !s->active) return;
s->onHalf();
s->advanceHalf();
if (s_instance != nullptr && s->ticksOutput >= s->totalTicks) {
s_instance->stopStream(*s);
s_instance->stopStream(*s, IR_SendStatus::Success);
} else {
s->refillFirstHalf();
}
}
static void dmaCpltCb(DMA_HandleTypeDef* hdma) {
auto* s = streamFromDma(hdma);
if (s == nullptr || !s->active) return;
s->onComplete();
s->advanceHalf();
if (s_instance != nullptr && s->ticksOutput >= s->totalTicks) {
s_instance->stopStream(*s);
s_instance->stopStream(*s, IR_SendStatus::Success);
} else {
s->refillSecondHalf();
}
}
@ -302,7 +326,7 @@ private:
s->onError();
if (s_instance != nullptr) {
s_instance->errors_++; // Fix #5: наблюдаемость аварийных завершений по Transfer-Error
s_instance->stopStream(*s);
s_instance->stopStream(*s, IR_SendStatus::DmaTransferError);
}
}
@ -348,21 +372,29 @@ private:
return true;
}
IR_SendStatus startStream(TxStream& s, const uint8_t* packet, uint8_t len) {
IR_SendStatus startStream(TxStream& s,
const uint8_t* packet,
uint8_t len,
const IR_TxPlan& expectedPlan,
uint32_t operationId) {
if (s.enc == nullptr || s.port == nullptr || s.mask == 0) return IR_SendStatus::ExternalInvalidConfig;
if (s.active) return IR_SendStatus::EncoderBusy;
if (!expectedPlan.valid() || operationId == 0U) return IR_SendStatus::ExternalInvalidConfig;
if (s.dmaBuf == nullptr || s.bufLen < 2 || s.halfLen == 0) return IR_SendStatus::ExternalInvalidConfig;
if (s.runs == nullptr || s.maxRuns == 0) return IR_SendStatus::ExternalInvalidConfig;
s.resetWave();
const uint16_t mult = IR_Encoder::carrierMultiply();
s.runCount = IR_Encoder::buildPhysicalGateRuns(packet, len, s.runs, s.maxRuns, mult);
if (s.runCount == 0) return IR_SendStatus::BuildGateRunsFailed;
uint32_t total = 0;
for (size_t i = 0; i < s.runCount; i++) total += s.runs[i].lenTicks;
s.totalTicks = total;
const uint16_t mult = expectedPlan.carrierMultiply;
const IR_TxPlan built = IR_Encoder::buildPhysicalTransmission(
packet, len, s.runs, s.maxRuns, mult);
if (!built.valid()) return built.status;
if (built.physicalTicks != expectedPlan.physicalTicks ||
built.gateRunCount != expectedPlan.gateRunCount)
return IR_SendStatus::PlanMismatch;
s.runCount = static_cast<size_t>(built.gateRunCount);
s.totalTicks = built.physicalTicks;
s.operationId = operationId;
uint16_t pwr = mult / 2U;
if (s.enc != nullptr) {
@ -388,6 +420,7 @@ private:
const uint32_t dst = u32ptr(&s.port->BSRR);
if (HAL_DMA_Start_IT(&s.hdma, (uint32_t)(uintptr_t)s.dmaBuf, dst, s.bufLen) != HAL_OK) {
s.active = false;
s.operationId = 0U;
return IR_SendStatus::DmaStartFailed;
}
@ -395,10 +428,12 @@ private:
return IR_SendStatus::Success;
}
void stopStream(TxStream& s) {
void stopStream(TxStream& s, IR_SendStatus terminalStatus) {
if (!s.active) return;
const uint32_t operationId = s.operationId;
s.active = false;
s.operationId = 0U;
HAL_DMA_Abort_IT(&s.hdma);
if (s.port != nullptr) {
@ -406,7 +441,7 @@ private:
}
if (s.enc != nullptr) {
s.enc->externalFinishSend();
s.enc->externalFinishSend(operationId, terminalStatus);
}
// Fix C: TIM НЕ останавливаем — он free-running, без разделяемого счётчика.
}