mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-22 12:59:36 +00:00
Compare commits
9 Commits
5b9f73ec7c
...
STM32
| Author | SHA1 | Date | |
|---|---|---|---|
| 5759658d42 | |||
| 1b408b0de6 | |||
| baac9fbf46 | |||
| 5b220dadd8 | |||
| b375aa169e | |||
| 883c0b00cf | |||
| d103d2a3ae | |||
| 86956bcf99 | |||
| b1d7016147 |
@ -440,6 +440,19 @@ void IR_DecoderRaw::abortFrame(uint32_t tUs)
|
|||||||
isReciveRaw = false;
|
isReciveRaw = false;
|
||||||
msgTypeReceive = 0;
|
msgTypeReceive = 0;
|
||||||
firstRX();
|
firstRX();
|
||||||
|
releasePreambleGuard(tUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// После обрыва кадра «длинная тишина» (IR_timeout × 2 ≈ 30 мс) перед новым кандидатом преамбулы не требуется.
|
||||||
|
// prevRise — последний ДЕКОДИРОВАННЫЙ фронт; после abort он свежий, а фронты, отброшенные гвардом, его не двигают,
|
||||||
|
// поэтому валидный кадр, начавшийся через <30 мс после обрыва мусора, проглатывался целиком без счётчика
|
||||||
|
// (стенд 09.09: КУ теряла пинг машинки после обрывков чужого заднего и всплеска её дальномера за 24 мс до пинга).
|
||||||
|
// Ложных захватов это не добавляет: хвост оборванного кадра (период фронтов 962 мкс, синхробиты ~1100) не проходит
|
||||||
|
// грубый фильтр периода преамбулы (2116…3270 мкс), а настоящая преамбула перезапускает кандидата по паузе > IR_timeout.
|
||||||
|
// После чистого конца кадра гвард остаётся: там он отсекает хвост синхробитов.
|
||||||
|
void IR_DecoderRaw::releasePreambleGuard(uint32_t tUs)
|
||||||
|
{
|
||||||
|
prevRise = tUs - IR_timeout * 2U - 1U; // «тишина уже была»: (front.time - prevRise) > longSilence для следующего фронта
|
||||||
}
|
}
|
||||||
|
|
||||||
void IR_DecoderRaw::expirePreambleCandidate()
|
void IR_DecoderRaw::expirePreambleCandidate()
|
||||||
|
|||||||
@ -186,6 +186,7 @@ private:
|
|||||||
RxEndInfo rxEnd;
|
RxEndInfo rxEnd;
|
||||||
void noteRxEnd(RxEndReason reason, uint32_t tUs); // терминал: фиксирует тип/длину/расчётный конец
|
void noteRxEnd(RxEndReason reason, uint32_t tUs); // терминал: фиксирует тип/длину/расчётный конец
|
||||||
void abortFrame(uint32_t tUs); // немедленный сброс битого кадра (sync/длина/overflow)
|
void abortFrame(uint32_t tUs); // немедленный сброс битого кадра (sync/длина/overflow)
|
||||||
|
void releasePreambleGuard(uint32_t tUs); // после abort: новый кандидат преамбулы без ожидания длинной тишины
|
||||||
void expirePreambleCandidate(); // кандидат без фронтов дольше таймаута → Idle
|
void expirePreambleCandidate(); // кандидат без фронтов дольше таймаута → Idle
|
||||||
uint8_t preambleGoodPeriods = 0;
|
uint8_t preambleGoodPeriods = 0;
|
||||||
uint16_t preambleMeanPeriod = 0;
|
uint16_t preambleMeanPeriod = 0;
|
||||||
|
|||||||
@ -352,6 +352,29 @@ bool IR_Encoder::txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool
|
|||||||
return txAdvanceAfterOutput(st, sendBufferLocal);
|
return txAdvanceAfterOutput(st, sendBufferLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обход кадра по границам ранов: между границами автомат выдаёт st.toggleCounter+1 тактов
|
||||||
|
// уровня st.state (txAdvanceAfterOutput считает toggleCounter до нуля, затем txAdvanceBoundary
|
||||||
|
// открывает следующий ран). Даёт ту же последовательность тактов, что потиковый обход, но за
|
||||||
|
// число шагов = число ранов (пинг: ~230 вместо ~8700 тактов — на 12 МГц это ~30 мс перед стартом DMA).
|
||||||
|
template <typename Emit>
|
||||||
|
bool IR_Encoder::txWalkRuns(TxFsmState &st, const uint8_t *sendBufferLocal, Emit emit)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const bool gate = st.state;
|
||||||
|
const uint32_t lenTicks = (uint32_t)st.toggleCounter + 1U;
|
||||||
|
if (!emit(gate, lenTicks))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
st.toggleCounter = 0;
|
||||||
|
if (!txAdvanceBoundary(st, sendBufferLocal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IR_Encoder::loadTxFsmFromMembers(TxFsmState &st) const
|
void IR_Encoder::loadTxFsmFromMembers(TxFsmState &st) const
|
||||||
{
|
{
|
||||||
st.sendLen = sendLen;
|
st.sendLen = sendLen;
|
||||||
@ -476,28 +499,27 @@ size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRu
|
|||||||
st.currentBitSequence = bitHigh;
|
st.currentBitSequence = bitHigh;
|
||||||
|
|
||||||
size_t runCount = 0;
|
size_t runCount = 0;
|
||||||
bool isActive = true;
|
const bool ok = txWalkRuns(st, sendBufferLocal, [&](bool gate, uint32_t lenTicks) -> bool {
|
||||||
while (isActive)
|
|
||||||
{
|
|
||||||
bool gate = false;
|
|
||||||
isActive = txEmitTick(st, sendBufferLocal, gate);
|
|
||||||
|
|
||||||
if (runCount > 0 && outRuns[runCount - 1].gate == gate)
|
if (runCount > 0 && outRuns[runCount - 1].gate == gate)
|
||||||
{
|
{
|
||||||
outRuns[runCount - 1].lenTicks = (uint16_t)(outRuns[runCount - 1].lenTicks + 1U);
|
const uint32_t merged = (uint32_t)outRuns[runCount - 1].lenTicks + lenTicks;
|
||||||
}
|
if (merged > 65535U)
|
||||||
else
|
|
||||||
{
|
|
||||||
if (runCount >= maxRuns)
|
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
outRuns[runCount].gate = gate;
|
outRuns[runCount - 1].lenTicks = (uint16_t)merged;
|
||||||
outRuns[runCount].lenTicks = 1U;
|
return true;
|
||||||
runCount++;
|
|
||||||
}
|
}
|
||||||
}
|
if (runCount >= maxRuns || lenTicks > 65535U)
|
||||||
return runCount;
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outRuns[runCount].gate = gate;
|
||||||
|
outRuns[runCount].lenTicks = (uint16_t)lenTicks;
|
||||||
|
runCount++;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return ok ? runCount : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns, uint16_t multiply)
|
size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns, uint16_t multiply)
|
||||||
@ -564,40 +586,29 @@ size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_
|
|||||||
bool currentGate = false;
|
bool currentGate = false;
|
||||||
uint32_t currentLogicalLen = 0;
|
uint32_t currentLogicalLen = 0;
|
||||||
bool havePendingRun = false;
|
bool havePendingRun = false;
|
||||||
bool isActive = true;
|
const bool ok = txWalkRuns(st, sendBufferLocal, [&](bool gate, uint32_t lenTicks) -> bool {
|
||||||
while (isActive)
|
if (havePendingRun && currentGate == gate)
|
||||||
{
|
|
||||||
bool gate = false;
|
|
||||||
isActive = txEmitTick(st, sendBufferLocal, gate);
|
|
||||||
|
|
||||||
if (!havePendingRun)
|
|
||||||
{
|
{
|
||||||
currentGate = gate;
|
currentLogicalLen += lenTicks;
|
||||||
currentLogicalLen = 1U;
|
return true;
|
||||||
havePendingRun = true;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount))
|
||||||
if (currentGate == gate)
|
|
||||||
{
|
{
|
||||||
currentLogicalLen++;
|
return false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appendPhysicalRun(currentGate, currentLogicalLen, runCount))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentGate = gate;
|
currentGate = gate;
|
||||||
currentLogicalLen = 1U;
|
currentLogicalLen = lenTicks;
|
||||||
|
havePendingRun = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount))
|
if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return runCount;
|
return runCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -214,6 +214,8 @@ private:
|
|||||||
static bool txAdvanceBoundary(TxFsmState &st, const uint8_t *sendBufferLocal);
|
static bool txAdvanceBoundary(TxFsmState &st, const uint8_t *sendBufferLocal);
|
||||||
static bool txAdvanceAfterOutput(TxFsmState &st, const uint8_t *sendBufferLocal);
|
static bool txAdvanceAfterOutput(TxFsmState &st, const uint8_t *sendBufferLocal);
|
||||||
static bool txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool &gateOut);
|
static bool txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool &gateOut);
|
||||||
|
template <typename Emit>
|
||||||
|
static bool txWalkRuns(TxFsmState &st, const uint8_t *sendBufferLocal, Emit emit);
|
||||||
void loadTxFsmFromMembers(TxFsmState &st) const;
|
void loadTxFsmFromMembers(TxFsmState &st) const;
|
||||||
void storeTxFsmToMembers(const TxFsmState &st);
|
void storeTxFsmToMembers(const TxFsmState &st);
|
||||||
bool shouldUseBufferedIsr() const;
|
bool shouldUseBufferedIsr() const;
|
||||||
|
|||||||
@ -231,9 +231,11 @@ typedef uint16_t crc_t;
|
|||||||
#ifndef IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT
|
#ifndef IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT
|
||||||
#define IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT 340U
|
#define IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT 340U
|
||||||
#endif
|
#endif
|
||||||
/** Таймаут окна кандидата преамбулы: IR_timeout * mult. */
|
/** Таймаут окна кандидата преамбулы: IR_timeout * mult. Кандидат без фронтов дольше таймаута байта
|
||||||
|
преамбулой быть не может; при 3× линия считалась занятой (rxLineActive) ещё 45 мс после последнего
|
||||||
|
паразитного фронта (напр. засветка своим дальномером) и откладывала передачу. */
|
||||||
#ifndef IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT
|
#ifndef IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT
|
||||||
#define IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT 3U
|
#define IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT 1U
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define preambPulse 3
|
#define preambPulse 3
|
||||||
|
|||||||
@ -65,13 +65,63 @@ public:
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Заполнение по ранам, а не по словам: тот же поток слов, что даёт nextWord() (состояние
|
||||||
|
// runIndex_/ticksLeftInRun_/slotInPeriod_ переносится через границы порций), но пауза (gate=0)
|
||||||
|
// пишется одним циклом записи, а несущая — копией готового шаблона периода. На 12 МГц это
|
||||||
|
// ~1 мс на 4096 слов вместо ~16 (пословный автомат) — и в предзаполнении перед стартом DMA,
|
||||||
|
// и в ISR-дозаполнении половин буфера во время передачи.
|
||||||
IR_TX_BSRR_WAVE_HOT void fill(uint32_t* dst, uint16_t count) {
|
IR_TX_BSRR_WAVE_HOT void fill(uint32_t* dst, uint16_t count) {
|
||||||
if (dst == nullptr || count == 0) {
|
if (dst == nullptr || count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
do {
|
while (count != 0) {
|
||||||
*dst++ = nextWord();
|
if (runIndex_ >= runCount) {
|
||||||
} while (--count != 0);
|
do { *dst++ = resetWord; } while (--count != 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bool gate = runs[runIndex_].gate;
|
||||||
|
uint16_t n = ticksLeftInRun_; // слов до конца текущего рана
|
||||||
|
if (n == 0) n = 1; // ран нулевой длины: nextWord() выдаёт одно слово и переходит дальше
|
||||||
|
if (n > count) n = count;
|
||||||
|
if (!gate) {
|
||||||
|
slotInPeriod_ = 0;
|
||||||
|
uint16_t k = n;
|
||||||
|
do { *dst++ = resetWord; } while (--k != 0);
|
||||||
|
} else {
|
||||||
|
uint16_t k = n;
|
||||||
|
// добить текущий период до слота 0 (если ран начался посреди периода на границе порции)
|
||||||
|
while (k != 0 && slotInPeriod_ != 0) {
|
||||||
|
*dst++ = (slotInPeriod_ < powerN_) ? setWord : resetWord;
|
||||||
|
if (++slotInPeriod_ >= multiply_) slotInPeriod_ = 0;
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
// целые периоды: powerN_ слов setWord, остальные resetWord
|
||||||
|
while (k >= multiply_) {
|
||||||
|
uint16_t i = 0;
|
||||||
|
for (; i < powerN_; ++i) *dst++ = setWord;
|
||||||
|
for (; i < multiply_; ++i) *dst++ = resetWord;
|
||||||
|
k = (uint16_t)(k - multiply_);
|
||||||
|
}
|
||||||
|
// хвост неполного периода
|
||||||
|
while (k != 0) {
|
||||||
|
*dst++ = (slotInPeriod_ < powerN_) ? setWord : resetWord;
|
||||||
|
if (++slotInPeriod_ >= multiply_) slotInPeriod_ = 0;
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count = (uint16_t)(count - n);
|
||||||
|
if (ticksLeftInRun_ > n) {
|
||||||
|
ticksLeftInRun_ = (uint16_t)(ticksLeftInRun_ - n);
|
||||||
|
} else {
|
||||||
|
ticksLeftInRun_ = 0;
|
||||||
|
}
|
||||||
|
if (ticksLeftInRun_ == 0) {
|
||||||
|
runIndex_++;
|
||||||
|
if (runIndex_ < runCount) {
|
||||||
|
ticksLeftInRun_ = runs[runIndex_].lenTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user