mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
tx error tracking and inline glitch filter
This commit is contained in:
205
IR_Encoder.cpp
205
IR_Encoder.cpp
@ -66,6 +66,41 @@ void *IR_Encoder::externalTxCtx = nullptr;
|
|||||||
bool IR_Encoder::txIsrLegacyMode_ = true;
|
bool IR_Encoder::txIsrLegacyMode_ = true;
|
||||||
uint16_t IR_Encoder::s_carrierMultiply = 2;
|
uint16_t IR_Encoder::s_carrierMultiply = 2;
|
||||||
|
|
||||||
|
const char* irSendStatusToString(IR_SendStatus status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case IR_SendStatus::Success:
|
||||||
|
return "Success";
|
||||||
|
case IR_SendStatus::PayloadTooLarge:
|
||||||
|
return "PayloadTooLarge";
|
||||||
|
case IR_SendStatus::EncoderBusy:
|
||||||
|
return "EncoderBusy";
|
||||||
|
case IR_SendStatus::BufferTooLarge:
|
||||||
|
return "BufferTooLarge";
|
||||||
|
case IR_SendStatus::ExternalBackendBusy:
|
||||||
|
return "ExternalBackendBusy";
|
||||||
|
case IR_SendStatus::ExternalStartFailed:
|
||||||
|
return "ExternalStartFailed";
|
||||||
|
case IR_SendStatus::ExternalNoStream:
|
||||||
|
return "ExternalNoStream";
|
||||||
|
case IR_SendStatus::ExternalInvalidConfig:
|
||||||
|
return "ExternalInvalidConfig";
|
||||||
|
case IR_SendStatus::BuildGateRunsFailed:
|
||||||
|
return "BuildGateRunsFailed";
|
||||||
|
case IR_SendStatus::ScaleGateRunsFailed:
|
||||||
|
return "ScaleGateRunsFailed";
|
||||||
|
case IR_SendStatus::DmaStartFailed:
|
||||||
|
return "DmaStartFailed";
|
||||||
|
case IR_SendStatus::EncoderPinUnavailable:
|
||||||
|
return "EncoderPinUnavailable";
|
||||||
|
case IR_SendStatus::BufferedStorageInvalid:
|
||||||
|
return "BufferedStorageInvalid";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IR_Encoder::setCarrierMultiply(uint16_t multiply)
|
void IR_Encoder::setCarrierMultiply(uint16_t multiply)
|
||||||
{
|
{
|
||||||
if (multiply < 2)
|
if (multiply < 2)
|
||||||
@ -465,6 +500,107 @@ size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRu
|
|||||||
return runCount;
|
return runCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns, uint16_t multiply)
|
||||||
|
{
|
||||||
|
if (packet == nullptr || outRuns == nullptr || maxRuns == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (len == 0 || len > dataByteSizeMax)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (multiply < 2)
|
||||||
|
{
|
||||||
|
multiply = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy into fixed-size buffer to match original encoder behavior (safe reads past sendLen).
|
||||||
|
uint8_t sendBufferLocal[dataByteSizeMax] = {0};
|
||||||
|
memcpy(sendBufferLocal, packet, len);
|
||||||
|
|
||||||
|
TxFsmState st{};
|
||||||
|
st.sendLen = len;
|
||||||
|
st.toggleCounter = preambToggle;
|
||||||
|
st.dataBitCounter = bitPerByte - 1;
|
||||||
|
st.dataByteCounter = 0;
|
||||||
|
st.preambFrontCounter = preambPulse * 2 - 1;
|
||||||
|
st.dataSequenceCounter = bitPerByte * 2;
|
||||||
|
st.syncSequenceCounter = syncBits * 2;
|
||||||
|
st.syncLastBit = false;
|
||||||
|
st.signal = preamb;
|
||||||
|
st.state = HIGH;
|
||||||
|
st.currentBitSequence = bitHigh;
|
||||||
|
|
||||||
|
auto appendPhysicalRun = [&](bool gate, uint32_t logicalLen, size_t& runCount) -> bool {
|
||||||
|
if (logicalLen == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t phys = (logicalLen * (uint32_t)multiply) / 2U;
|
||||||
|
if (logicalLen > 0 && phys == 0)
|
||||||
|
{
|
||||||
|
phys = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (phys > 0)
|
||||||
|
{
|
||||||
|
if (runCount >= maxRuns)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t chunk = phys > 65535U ? 65535U : phys;
|
||||||
|
outRuns[runCount].gate = gate;
|
||||||
|
outRuns[runCount].lenTicks = static_cast<uint16_t>(chunk);
|
||||||
|
runCount++;
|
||||||
|
phys -= chunk;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t runCount = 0;
|
||||||
|
bool currentGate = false;
|
||||||
|
uint32_t currentLogicalLen = 0;
|
||||||
|
bool havePendingRun = false;
|
||||||
|
bool isActive = true;
|
||||||
|
while (isActive)
|
||||||
|
{
|
||||||
|
bool gate = false;
|
||||||
|
isActive = txEmitTick(st, sendBufferLocal, gate);
|
||||||
|
|
||||||
|
if (!havePendingRun)
|
||||||
|
{
|
||||||
|
currentGate = gate;
|
||||||
|
currentLogicalLen = 1U;
|
||||||
|
havePendingRun = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentGate == gate)
|
||||||
|
{
|
||||||
|
currentLogicalLen++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appendPhysicalRun(currentGate, currentLogicalLen, runCount))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGate = gate;
|
||||||
|
currentLogicalLen = 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return runCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void IR_Encoder::enable()
|
void IR_Encoder::enable()
|
||||||
{
|
{
|
||||||
@ -555,7 +691,7 @@ IR_SendResult IR_Encoder::sendDataFULL(uint16_t addrFrom, uint16_t addrTo, uint8
|
|||||||
if (len > bytePerPack)
|
if (len > bytePerPack)
|
||||||
{
|
{
|
||||||
Serial.println("IR Pack to big");
|
Serial.println("IR Pack to big");
|
||||||
return IR_SendResult(false, 0);
|
return IR_SendResult(false, 0, IR_SendStatus::PayloadTooLarge);
|
||||||
}
|
}
|
||||||
constexpr uint8_t dataStart = msgBytes + addrBytes + addrBytes;
|
constexpr uint8_t dataStart = msgBytes + addrBytes + addrBytes;
|
||||||
memset(sendBuffer, 0x00, dataByteSizeMax);
|
memset(sendBuffer, 0x00, dataByteSizeMax);
|
||||||
@ -605,11 +741,15 @@ IR_SendResult IR_Encoder::sendDataFULL(uint16_t addrFrom, uint16_t addrTo, uint8
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// отправка
|
// отправка
|
||||||
rawSend(sendBuffer, packSize);
|
const IR_SendStatus status = rawSend(sendBuffer, packSize);
|
||||||
|
if (status != IR_SendStatus::Success)
|
||||||
|
{
|
||||||
|
return IR_SendResult(false, 0, status);
|
||||||
|
}
|
||||||
|
|
||||||
// Возвращаем результат отправки
|
// Возвращаем результат отправки
|
||||||
uint32_t sendTime = calculateSendTime(packSize);
|
uint32_t sendTime = calculateSendTime(packSize);
|
||||||
return IR_SendResult(true, sendTime);
|
return IR_SendResult(true, sendTime, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -633,11 +773,15 @@ IR_SendResult IR_Encoder::sendAccept(uint16_t addrTo, uint8_t customByte)
|
|||||||
sendBuffer[4] = crc8(sendBuffer, 0, 4, poly1) & 0xFF;
|
sendBuffer[4] = crc8(sendBuffer, 0, 4, poly1) & 0xFF;
|
||||||
sendBuffer[5] = crc8(sendBuffer, 0, 5, poly2) & 0xFF;
|
sendBuffer[5] = crc8(sendBuffer, 0, 5, poly2) & 0xFF;
|
||||||
|
|
||||||
rawSend(sendBuffer, packsize);
|
const IR_SendStatus status = rawSend(sendBuffer, packsize);
|
||||||
|
if (status != IR_SendStatus::Success)
|
||||||
|
{
|
||||||
|
return IR_SendResult(false, 0, status);
|
||||||
|
}
|
||||||
|
|
||||||
// Возвращаем результат отправки
|
// Возвращаем результат отправки
|
||||||
uint32_t sendTime = calculateSendTime(packsize);
|
uint32_t sendTime = calculateSendTime(packsize);
|
||||||
return IR_SendResult(true, sendTime);
|
return IR_SendResult(true, sendTime, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
IR_SendResult IR_Encoder::sendRequest(uint16_t addrTo)
|
IR_SendResult IR_Encoder::sendRequest(uint16_t addrTo)
|
||||||
@ -659,11 +803,15 @@ IR_SendResult IR_Encoder::sendRequest(uint16_t addrTo)
|
|||||||
sendBuffer[5] = crc8(sendBuffer, 0, 5, poly1) & 0xFF;
|
sendBuffer[5] = crc8(sendBuffer, 0, 5, poly1) & 0xFF;
|
||||||
sendBuffer[6] = crc8(sendBuffer, 0, 6, poly2) & 0xFF;
|
sendBuffer[6] = crc8(sendBuffer, 0, 6, poly2) & 0xFF;
|
||||||
|
|
||||||
rawSend(sendBuffer, packsize);
|
const IR_SendStatus status = rawSend(sendBuffer, packsize);
|
||||||
|
if (status != IR_SendStatus::Success)
|
||||||
|
{
|
||||||
|
return IR_SendResult(false, 0, status);
|
||||||
|
}
|
||||||
|
|
||||||
// Возвращаем результат отправки
|
// Возвращаем результат отправки
|
||||||
uint32_t sendTime = calculateSendTime(packsize);
|
uint32_t sendTime = calculateSendTime(packsize);
|
||||||
return IR_SendResult(true, sendTime);
|
return IR_SendResult(true, sendTime, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
IR_SendResult IR_Encoder::sendBack(uint8_t data)
|
IR_SendResult IR_Encoder::sendBack(uint8_t data)
|
||||||
@ -685,7 +833,7 @@ IR_SendResult IR_Encoder::_sendBack(bool isAdressed, uint16_t addrTo, uint8_t *d
|
|||||||
{
|
{
|
||||||
if (len > bytePerPack)
|
if (len > bytePerPack)
|
||||||
{
|
{
|
||||||
return IR_SendResult(false, 0);
|
return IR_SendResult(false, 0, IR_SendStatus::PayloadTooLarge);
|
||||||
}
|
}
|
||||||
memset(sendBuffer, 0x00, dataByteSizeMax);
|
memset(sendBuffer, 0x00, dataByteSizeMax);
|
||||||
uint8_t dataStart = msgBytes + addrBytes + (isAdressed ? addrBytes : 0);
|
uint8_t dataStart = msgBytes + addrBytes + (isAdressed ? addrBytes : 0);
|
||||||
@ -716,11 +864,15 @@ IR_SendResult IR_Encoder::_sendBack(bool isAdressed, uint16_t addrTo, uint8_t *d
|
|||||||
sendBuffer[packSize - crcBytes + 1] = crc8(sendBuffer, 0, packSize - crcBytes + 1, poly2) & 0xFF;
|
sendBuffer[packSize - crcBytes + 1] = crc8(sendBuffer, 0, packSize - crcBytes + 1, poly2) & 0xFF;
|
||||||
|
|
||||||
// отправка
|
// отправка
|
||||||
rawSend(sendBuffer, packSize);
|
const IR_SendStatus status = rawSend(sendBuffer, packSize);
|
||||||
|
if (status != IR_SendStatus::Success)
|
||||||
|
{
|
||||||
|
return IR_SendResult(false, 0, status);
|
||||||
|
}
|
||||||
|
|
||||||
// Возвращаем результат отправки
|
// Возвращаем результат отправки
|
||||||
uint32_t sendTime = calculateSendTime(packSize);
|
uint32_t sendTime = calculateSendTime(packSize);
|
||||||
return IR_SendResult(true, sendTime);
|
return IR_SendResult(true, sendTime, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IR_Encoder::registerWithBlindDecoders()
|
void IR_Encoder::registerWithBlindDecoders()
|
||||||
@ -747,18 +899,18 @@ void IR_Encoder::refreshBlindDecoderMuteState()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
IR_SendStatus IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
||||||
{
|
{
|
||||||
if (isSending)
|
if (isSending)
|
||||||
{
|
{
|
||||||
// TODO: Обработка повторной отправки
|
// TODO: Обработка повторной отправки
|
||||||
return;
|
return IR_SendStatus::EncoderBusy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверка на переполнение буфера
|
// Проверка на переполнение буфера
|
||||||
if (len > dataByteSizeMax)
|
if (len > dataByteSizeMax)
|
||||||
{
|
{
|
||||||
return;
|
return IR_SendStatus::BufferTooLarge;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial.print("IR tx hex: ");
|
// Serial.print("IR tx hex: ");
|
||||||
@ -773,7 +925,7 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
{
|
{
|
||||||
if (externalTxBusyFn != nullptr && externalTxBusyFn(externalTxCtx))
|
if (externalTxBusyFn != nullptr && externalTxBusyFn(externalTxCtx))
|
||||||
{
|
{
|
||||||
return;
|
return IR_SendStatus::ExternalBackendBusy;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendLen = len;
|
sendLen = len;
|
||||||
@ -782,18 +934,18 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
isSending = true;
|
isSending = true;
|
||||||
refreshBlindDecoderMuteState();
|
refreshBlindDecoderMuteState();
|
||||||
|
|
||||||
const bool ok = externalTxStartFn(externalTxCtx, this, ptr, len);
|
const IR_SendStatus status = externalTxStartFn(externalTxCtx, this, ptr, len);
|
||||||
if (!ok)
|
if (status != IR_SendStatus::Success)
|
||||||
{
|
{
|
||||||
isSending = false;
|
isSending = false;
|
||||||
refreshBlindDecoderMuteState();
|
refreshBlindDecoderMuteState();
|
||||||
}
|
}
|
||||||
return;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port == nullptr || mask == 0)
|
if (port == nullptr || mask == 0)
|
||||||
{
|
{
|
||||||
return;
|
return IR_SendStatus::EncoderPinUnavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr != sendBuffer)
|
if (ptr != sendBuffer)
|
||||||
@ -832,7 +984,7 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
isSending = true;
|
isSending = true;
|
||||||
refreshBlindDecoderMuteState();
|
refreshBlindDecoderMuteState();
|
||||||
IR_Encoder::carrierResume();
|
IR_Encoder::carrierResume();
|
||||||
return;
|
return IR_SendStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrTxIsrBufferedStorageBase* buf = txActiveBufferedCtx_;
|
IrTxIsrBufferedStorageBase* buf = txActiveBufferedCtx_;
|
||||||
@ -840,23 +992,18 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
{
|
{
|
||||||
txUseBufferedIsr_ = false;
|
txUseBufferedIsr_ = false;
|
||||||
txActiveBufferedCtx_ = nullptr;
|
txActiveBufferedCtx_ = nullptr;
|
||||||
return;
|
return IR_SendStatus::BufferedStorageInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf->resetRuntimeState();
|
buf->resetRuntimeState();
|
||||||
|
|
||||||
size_t nRuns = buildGateRuns(sendBuffer, len, buf->gateRuns, buf->maxGateRuns);
|
txMultiplySnap_ = carrierMultiply();
|
||||||
|
size_t nRuns = buildPhysicalGateRuns(sendBuffer, len, buf->gateRuns, buf->maxGateRuns, txMultiplySnap_);
|
||||||
if (nRuns == 0U)
|
if (nRuns == 0U)
|
||||||
{
|
{
|
||||||
txUseBufferedIsr_ = false;
|
txUseBufferedIsr_ = false;
|
||||||
txActiveBufferedCtx_ = nullptr;
|
txActiveBufferedCtx_ = nullptr;
|
||||||
return;
|
return IR_SendStatus::BuildGateRunsFailed;
|
||||||
}
|
|
||||||
if (!scaleGateRunsToPhysical(buf->gateRuns, &nRuns, buf->maxGateRuns, carrierMultiply()))
|
|
||||||
{
|
|
||||||
txUseBufferedIsr_ = false;
|
|
||||||
txActiveBufferedCtx_ = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t total = 0;
|
uint32_t total = 0;
|
||||||
@ -868,7 +1015,6 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
|
|
||||||
const uint32_t setW = (uint32_t)mask;
|
const uint32_t setW = (uint32_t)mask;
|
||||||
const uint32_t resetW = ((uint32_t)mask) << 16U;
|
const uint32_t resetW = ((uint32_t)mask) << 16U;
|
||||||
txMultiplySnap_ = carrierMultiply();
|
|
||||||
{
|
{
|
||||||
const uint16_t cap = maxPowerNumerator();
|
const uint16_t cap = maxPowerNumerator();
|
||||||
txPowerSnap_ = (powerNumerator_ > cap) ? cap : powerNumerator_;
|
txPowerSnap_ = (powerNumerator_ > cap) ? cap : powerNumerator_;
|
||||||
@ -883,6 +1029,7 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
port->BSRR = resetW;
|
port->BSRR = resetW;
|
||||||
}
|
}
|
||||||
IR_Encoder::carrierResume();
|
IR_Encoder::carrierResume();
|
||||||
|
return IR_SendStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IR_Encoder::isr()
|
void IR_Encoder::isr()
|
||||||
|
|||||||
41
IR_Encoder.h
41
IR_Encoder.h
@ -4,13 +4,34 @@
|
|||||||
|
|
||||||
// TODO: Отложенная передача после завершения приема
|
// TODO: Отложенная передача после завершения приема
|
||||||
|
|
||||||
|
enum class IR_SendStatus : uint8_t {
|
||||||
|
Success = 0,
|
||||||
|
PayloadTooLarge,
|
||||||
|
EncoderBusy,
|
||||||
|
BufferTooLarge,
|
||||||
|
ExternalBackendBusy,
|
||||||
|
ExternalStartFailed,
|
||||||
|
ExternalNoStream,
|
||||||
|
ExternalInvalidConfig,
|
||||||
|
BuildGateRunsFailed,
|
||||||
|
ScaleGateRunsFailed,
|
||||||
|
DmaStartFailed,
|
||||||
|
EncoderPinUnavailable,
|
||||||
|
BufferedStorageInvalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* irSendStatusToString(IR_SendStatus status);
|
||||||
|
|
||||||
// Структура для возврата результата отправки
|
// Структура для возврата результата отправки
|
||||||
struct IR_SendResult {
|
struct IR_SendResult {
|
||||||
bool success; // Флаг успешности отправки
|
bool success; // Флаг успешности отправки
|
||||||
uint32_t sendTimeMs; // Время отправки пакета в миллисекундах
|
uint32_t sendTimeMs; // Время отправки пакета в миллисекундах
|
||||||
|
IR_SendStatus status; // Детализированный статус старта передачи
|
||||||
|
|
||||||
IR_SendResult(bool success = false, uint32_t sendTimeMs = 0)
|
IR_SendResult(bool success = false,
|
||||||
: success(success), sendTimeMs(sendTimeMs) {}
|
uint32_t sendTimeMs = 0,
|
||||||
|
IR_SendStatus status = IR_SendStatus::ExternalStartFailed)
|
||||||
|
: success(success), sendTimeMs(sendTimeMs), status(status) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class IR_DecoderRaw;
|
class IR_DecoderRaw;
|
||||||
@ -31,7 +52,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
using ExternalTxBusyFn = bool (*)(void *ctx);
|
using ExternalTxBusyFn = bool (*)(void *ctx);
|
||||||
using ExternalTxStartFn = bool (*)(void *ctx, IR_Encoder *enc, const uint8_t *packet, uint8_t len);
|
using ExternalTxStartFn = IR_SendStatus (*)(void *ctx, IR_Encoder *enc, const uint8_t *packet, uint8_t len);
|
||||||
private:
|
private:
|
||||||
// uint16_t id; /// @brief Адрес передатчика
|
// uint16_t id; /// @brief Адрес передатчика
|
||||||
public:
|
public:
|
||||||
@ -62,7 +83,7 @@ public:
|
|||||||
/** p∈[0,100] → ближайший допустимый числитель; 100% даёт N = maxPowerNumerator(). */
|
/** p∈[0,100] → ближайший допустимый числитель; 100% даёт N = maxPowerNumerator(). */
|
||||||
void setPowerPercent(uint8_t p);
|
void setPowerPercent(uint8_t p);
|
||||||
|
|
||||||
/** После buildGateRuns: lenTicks в тактах 2×Fc → физические тики (carrierFrec×multiply). Может разбить сегменты. */
|
/** Legacy helper: lenTicks в тактах 2×Fc → физические тики (carrierFrec×multiply). Может разбить сегменты. */
|
||||||
static bool scaleGateRunsToPhysical(IR_TxGateRun* runs, size_t* ioCount, size_t maxRuns, uint16_t multiply);
|
static bool scaleGateRunsToPhysical(IR_TxGateRun* runs, size_t* ioCount, size_t maxRuns, uint16_t multiply);
|
||||||
|
|
||||||
/** Configure timer frequency for TX clock (carrierFrec × multiply) without attaching ISR. */
|
/** Configure timer frequency for TX clock (carrierFrec × multiply) without attaching ISR. */
|
||||||
@ -72,7 +93,7 @@ public:
|
|||||||
static void tick();
|
static void tick();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Режим внутреннего TX без DMA: false — BSRR + кольцо (buildGateRuns + scaleGateRunsToPhysical);
|
* Режим внутреннего TX без DMA: false — BSRR + кольцо (direct physical gate-runs builder);
|
||||||
* true — FSM «налету» + скважность несущей как у буферного пути (подшаги multiply/2 на шаг FSM).
|
* true — FSM «налету» + скважность несущей как у буферного пути (подшаги multiply/2 на шаг FSM).
|
||||||
* По умолчанию включён legacy=true для обратной совместимости. Вызов меняет default и обновляет
|
* По умолчанию включён legacy=true для обратной совместимости. Вызов меняет default и обновляет
|
||||||
* все зарегистрированные encoder-объекты. Buffered ISR реально используется только если у encoder
|
* все зарегистрированные encoder-объекты. Buffered ISR реально используется только если у encoder
|
||||||
@ -94,8 +115,10 @@ public:
|
|||||||
/** Called by external TX backend on actual end of transmission. */
|
/** Called by external TX backend on actual end of transmission. */
|
||||||
void externalFinishSend();
|
void externalFinishSend();
|
||||||
|
|
||||||
/** Build RLE runs of carrier gate for a packet (no HW access). */
|
/** Build RLE runs of carrier gate for a packet in logical 2×Fc ticks (no HW access). */
|
||||||
static size_t buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns);
|
static size_t buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns);
|
||||||
|
/** Build RLE runs directly in physical carrierFrec×multiply ticks (DMA/buffered ISR path). */
|
||||||
|
static size_t buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns, uint16_t multiply);
|
||||||
|
|
||||||
void enable();
|
void enable();
|
||||||
void disable();
|
void disable();
|
||||||
@ -108,7 +131,7 @@ public:
|
|||||||
"IR_Encoder::setBlindDecoders: array size exceeds IR_PAIR_MUTE_MAX_ENCODERS");
|
"IR_Encoder::setBlindDecoders: array size exceeds IR_PAIR_MUTE_MAX_ENCODERS");
|
||||||
setBlindDecoders(decoders, static_cast<uint8_t>(N));
|
setBlindDecoders(decoders, static_cast<uint8_t>(N));
|
||||||
}
|
}
|
||||||
void rawSend(uint8_t *ptr, uint8_t len);
|
IR_SendStatus rawSend(uint8_t *ptr, uint8_t len);
|
||||||
|
|
||||||
IR_SendResult sendData(uint16_t addrTo, uint8_t dataByte, bool needAccept = false);
|
IR_SendResult sendData(uint16_t addrTo, uint8_t dataByte, bool needAccept = false);
|
||||||
IR_SendResult sendData(uint16_t addrTo, uint8_t *data = nullptr, uint8_t len = 0, bool needAccept = false);
|
IR_SendResult sendData(uint16_t addrTo, uint8_t *data = nullptr, uint8_t len = 0, bool needAccept = false);
|
||||||
|
|||||||
@ -106,14 +106,14 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start(IR_Encoder* enc, const uint8_t* packet, uint8_t len) {
|
IR_SendStatus start(IR_Encoder* enc, const uint8_t* packet, uint8_t len) {
|
||||||
if (enc == nullptr) return false;
|
if (enc == nullptr) return IR_SendStatus::ExternalNoStream;
|
||||||
for (uint8_t i = 0; i < streamCount_; i++) {
|
for (uint8_t i = 0; i < streamCount_; i++) {
|
||||||
if (streams_[i].enc == enc) {
|
if (streams_[i].enc == enc) {
|
||||||
return startStream(streams_[i], packet, len);
|
return startStream(streams_[i], packet, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return IR_SendStatus::ExternalNoStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
void irqForStream(size_t streamIndex) {
|
void irqForStream(size_t streamIndex) {
|
||||||
@ -290,28 +290,22 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool startStream(TxStream& s, const uint8_t* packet, uint8_t len) {
|
IR_SendStatus startStream(TxStream& s, const uint8_t* packet, uint8_t len) {
|
||||||
if (s.enc == nullptr || s.port == nullptr || s.mask == 0) return false;
|
if (s.enc == nullptr || s.port == nullptr || s.mask == 0) return IR_SendStatus::ExternalInvalidConfig;
|
||||||
if (s.active) return false;
|
if (s.active) return IR_SendStatus::EncoderBusy;
|
||||||
if (s.dmaBuf == nullptr || s.bufLen < 2 || s.halfLen == 0) return false;
|
if (s.dmaBuf == nullptr || s.bufLen < 2 || s.halfLen == 0) return IR_SendStatus::ExternalInvalidConfig;
|
||||||
if (s.runs == nullptr || s.maxRuns == 0) return false;
|
if (s.runs == nullptr || s.maxRuns == 0) return IR_SendStatus::ExternalInvalidConfig;
|
||||||
|
|
||||||
s.resetWave();
|
s.resetWave();
|
||||||
|
|
||||||
s.runCount = IR_Encoder::buildGateRuns(packet, len, s.runs, s.maxRuns);
|
const uint16_t mult = IR_Encoder::carrierMultiply();
|
||||||
if (s.runCount == 0) return false;
|
s.runCount = IR_Encoder::buildPhysicalGateRuns(packet, len, s.runs, s.maxRuns, mult);
|
||||||
|
if (s.runCount == 0) return IR_SendStatus::BuildGateRunsFailed;
|
||||||
size_t rc = s.runCount;
|
|
||||||
if (!IR_Encoder::scaleGateRunsToPhysical(s.runs, &rc, s.maxRuns, IR_Encoder::carrierMultiply())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
s.runCount = rc;
|
|
||||||
|
|
||||||
uint32_t total = 0;
|
uint32_t total = 0;
|
||||||
for (size_t i = 0; i < s.runCount; i++) total += s.runs[i].lenTicks;
|
for (size_t i = 0; i < s.runCount; i++) total += s.runs[i].lenTicks;
|
||||||
s.totalTicks = total;
|
s.totalTicks = total;
|
||||||
|
|
||||||
const uint16_t mult = IR_Encoder::carrierMultiply();
|
|
||||||
uint16_t pwr = mult / 2U;
|
uint16_t pwr = mult / 2U;
|
||||||
if (s.enc != nullptr) {
|
if (s.enc != nullptr) {
|
||||||
const uint16_t want = s.enc->powerNumerator();
|
const uint16_t want = s.enc->powerNumerator();
|
||||||
@ -327,13 +321,13 @@ private:
|
|||||||
|
|
||||||
const uint32_t dst = u32ptr(&s.port->BSRR);
|
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) {
|
if (HAL_DMA_Start_IT(&s.hdma, (uint32_t)(uintptr_t)s.dmaBuf, dst, s.bufLen) != HAL_OK) {
|
||||||
return false;
|
return IR_SendStatus::DmaStartFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.active = true;
|
s.active = true;
|
||||||
activeCount_++;
|
activeCount_++;
|
||||||
startTimerIfNeeded();
|
startTimerIfNeeded();
|
||||||
return true;
|
return IR_SendStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopStream(TxStream& s) {
|
void stopStream(TxStream& s) {
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Один RLE-сегмент огибающей несущей.
|
* Один RLE-сегмент огибающей несущей.
|
||||||
* В buildGateRuns: lenTicks в тактах логической шкалы 2×carrierFrec (как раньше).
|
* В legacy buildGateRuns: lenTicks в тактах логической шкалы 2×carrierFrec.
|
||||||
* После IR_Encoder::scaleGateRunsToPhysical — в физических тиках carrierFrec×multiply.
|
* В современном DMA/buffered ISR пути buildPhysicalGateRuns строит lenTicks сразу в физических тиках carrierFrec×multiply.
|
||||||
*/
|
*/
|
||||||
struct IrTxGateRun {
|
struct IrTxGateRun {
|
||||||
uint16_t lenTicks;
|
uint16_t lenTicks;
|
||||||
|
|||||||
Reference in New Issue
Block a user