mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
add brightness controll
This commit is contained in:
@ -207,7 +207,7 @@ HardwareTimer IR_Timer(TIM3);
|
||||
|
||||
void setup()
|
||||
{
|
||||
IR_Timer.setOverflow(carrierFrec * 2, HERTZ_FORMAT);
|
||||
IR_Timer.setOverflow((uint32_t)carrierFrec * (uint32_t)IR_Encoder::carrierMultiply(), HERTZ_FORMAT);
|
||||
IR_Timer.attachInterrupt(1, EncoderISR);
|
||||
NVIC_SetPriority(IRQn_Type::TIM3_IRQn, 0);
|
||||
IR_Timer.resume();
|
||||
|
||||
265
IR_Encoder.cpp
265
IR_Encoder.cpp
@ -38,12 +38,120 @@ IR_Encoder::IR_Encoder(uint8_t pin, uint16_t addr, IR_DecoderRaw *decPair, bool
|
||||
|
||||
pinMode(pin, OUTPUT);
|
||||
}
|
||||
powerNumerator_ = 1;
|
||||
};
|
||||
|
||||
HardwareTimer* IR_Encoder::IR_Timer = nullptr;
|
||||
IR_Encoder::ExternalTxStartFn IR_Encoder::externalTxStartFn = nullptr;
|
||||
IR_Encoder::ExternalTxBusyFn IR_Encoder::externalTxBusyFn = nullptr;
|
||||
void *IR_Encoder::externalTxCtx = nullptr;
|
||||
bool IR_Encoder::txIsrLegacyMode_ = false;
|
||||
uint16_t IR_Encoder::s_carrierMultiply = 2;
|
||||
|
||||
void IR_Encoder::setCarrierMultiply(uint16_t multiply)
|
||||
{
|
||||
if (multiply < 2)
|
||||
{
|
||||
multiply = 2;
|
||||
}
|
||||
s_carrierMultiply = multiply;
|
||||
}
|
||||
|
||||
uint16_t IR_Encoder::carrierMultiply()
|
||||
{
|
||||
return s_carrierMultiply;
|
||||
}
|
||||
|
||||
void IR_Encoder::retuneCarrierClock()
|
||||
{
|
||||
if (IR_Timer == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
IR_Timer->pause();
|
||||
IR_Timer->setOverflow((uint32_t)carrierFrec * (uint32_t)s_carrierMultiply, HERTZ_FORMAT);
|
||||
IR_Timer->pause();
|
||||
}
|
||||
|
||||
uint16_t IR_Encoder::maxPowerNumerator()
|
||||
{
|
||||
return static_cast<uint16_t>(s_carrierMultiply / 2U);
|
||||
}
|
||||
|
||||
void IR_Encoder::setPowerNumerator(uint16_t n)
|
||||
{
|
||||
const uint16_t cap = maxPowerNumerator();
|
||||
powerNumerator_ = (n > cap) ? cap : n;
|
||||
}
|
||||
|
||||
void IR_Encoder::setPowerPercent(uint8_t p)
|
||||
{
|
||||
if (p > 100U)
|
||||
{
|
||||
p = 100U;
|
||||
}
|
||||
const uint16_t cap = maxPowerNumerator();
|
||||
const uint32_t n = ((uint32_t)p * (uint32_t)cap + 50U) / 100U;
|
||||
powerNumerator_ = static_cast<uint16_t>(n);
|
||||
}
|
||||
|
||||
uint16_t IR_Encoder::powerNumerator() const
|
||||
{
|
||||
return powerNumerator_;
|
||||
}
|
||||
|
||||
bool IR_Encoder::scaleGateRunsToPhysical(IR_TxGateRun* runs, size_t* ioCount, size_t maxRuns, uint16_t multiply)
|
||||
{
|
||||
if (runs == nullptr || ioCount == nullptr || maxRuns == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (multiply < 2)
|
||||
{
|
||||
multiply = 2;
|
||||
}
|
||||
const size_t nIn = *ioCount;
|
||||
if (nIn > irproto::kIsrTxMaxGateRuns)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
IrTxGateRun copy[irproto::kIsrTxMaxGateRuns];
|
||||
memcpy(copy, runs, nIn * sizeof(IrTxGateRun));
|
||||
size_t w = 0;
|
||||
for (size_t r = 0; r < nIn; r++)
|
||||
{
|
||||
uint32_t phys = (uint32_t)copy[r].lenTicks * (uint32_t)multiply / 2U;
|
||||
if (copy[r].lenTicks > 0 && phys == 0)
|
||||
{
|
||||
phys = 1;
|
||||
}
|
||||
const bool g = copy[r].gate;
|
||||
while (phys > 0)
|
||||
{
|
||||
if (w >= maxRuns)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const uint32_t chunk = phys > 65535U ? 65535U : phys;
|
||||
runs[w].lenTicks = static_cast<uint16_t>(chunk);
|
||||
runs[w].gate = g;
|
||||
w++;
|
||||
phys -= chunk;
|
||||
}
|
||||
}
|
||||
*ioCount = w;
|
||||
return true;
|
||||
}
|
||||
|
||||
void IR_Encoder::setTxIsrLegacyMode(bool legacy)
|
||||
{
|
||||
txIsrLegacyMode_ = legacy;
|
||||
}
|
||||
|
||||
bool IR_Encoder::txIsrLegacyMode()
|
||||
{
|
||||
return txIsrLegacyMode_;
|
||||
}
|
||||
|
||||
bool IR_Encoder::txAdvanceBoundary(TxFsmState &st, const uint8_t *sendBufferLocal)
|
||||
{
|
||||
@ -194,7 +302,7 @@ void IR_Encoder::begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, ui
|
||||
IR_Timer = timer;
|
||||
if(IR_Timer == nullptr) return;
|
||||
IR_Timer->pause();
|
||||
IR_Timer->setOverflow(carrierFrec * 2, HERTZ_FORMAT);
|
||||
IR_Timer->setOverflow((uint32_t)carrierFrec * (uint32_t)s_carrierMultiply, HERTZ_FORMAT);
|
||||
IR_Timer->attachInterrupt(channel, (isrCallback == nullptr ? IR_Encoder::isr : isrCallback));
|
||||
NVIC_SetPriority(IRQn, priority);
|
||||
IR_Timer->pause();
|
||||
@ -206,7 +314,7 @@ void IR_Encoder::beginClockOnly(HardwareTimer *timer)
|
||||
if (IR_Timer == nullptr)
|
||||
return;
|
||||
IR_Timer->pause();
|
||||
IR_Timer->setOverflow(carrierFrec * 2, HERTZ_FORMAT);
|
||||
IR_Timer->setOverflow((uint32_t)carrierFrec * (uint32_t)s_carrierMultiply, HERTZ_FORMAT);
|
||||
IR_Timer->pause();
|
||||
}
|
||||
|
||||
@ -607,23 +715,86 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (port == nullptr || mask == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ptr != sendBuffer)
|
||||
{
|
||||
memcpy(sendBuffer, ptr, len);
|
||||
}
|
||||
sendLen = len;
|
||||
toggleCounter = preambToggle; // Первая генерация для первого signal
|
||||
|
||||
dataBitCounter = bitPerByte - 1;
|
||||
dataByteCounter = 0;
|
||||
if (txIsrLegacyMode_)
|
||||
{
|
||||
toggleCounter = preambToggle;
|
||||
dataBitCounter = bitPerByte - 1;
|
||||
dataByteCounter = 0;
|
||||
preambFrontCounter = preambPulse * 2 - 1;
|
||||
dataSequenceCounter = bitPerByte * 2;
|
||||
syncSequenceCounter = syncBits * 2;
|
||||
signal = preamb;
|
||||
state = HIGH;
|
||||
currentBitSequence = bitHigh;
|
||||
txMultiplySnap_ = carrierMultiply();
|
||||
{
|
||||
const uint16_t cap = maxPowerNumerator();
|
||||
txPowerSnap_ = (powerNumerator_ > cap) ? cap : powerNumerator_;
|
||||
}
|
||||
legacyPhysPerLogical_ = static_cast<uint16_t>(txMultiplySnap_ / 2U);
|
||||
if (legacyPhysPerLogical_ == 0)
|
||||
{
|
||||
legacyPhysPerLogical_ = 1;
|
||||
}
|
||||
legacyPhysCounter_ = 0;
|
||||
legacySlotInPeriod_ = 0;
|
||||
isSending = true;
|
||||
refreshBlindDecoderMuteState();
|
||||
IR_Encoder::carrierResume();
|
||||
return;
|
||||
}
|
||||
|
||||
preambFrontCounter = preambPulse * 2 - 1; // -1 за счёт генерации уже на этапе сразу после инициализации
|
||||
dataSequenceCounter = bitPerByte * 2;
|
||||
syncSequenceCounter = syncBits * 2;
|
||||
size_t nRuns = buildGateRuns(sendBuffer, len, txGateRuns_, irproto::kIsrTxMaxGateRuns);
|
||||
if (nRuns == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!scaleGateRunsToPhysical(txGateRuns_, &nRuns, irproto::kIsrTxMaxGateRuns, carrierMultiply()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t total = 0;
|
||||
for (size_t i = 0; i < nRuns; i++)
|
||||
{
|
||||
total += txGateRuns_[i].lenTicks;
|
||||
}
|
||||
txBsrrTotalTicks_ = total;
|
||||
|
||||
const uint32_t setW = (uint32_t)mask;
|
||||
const uint32_t resetW = ((uint32_t)mask) << 16U;
|
||||
txMultiplySnap_ = carrierMultiply();
|
||||
{
|
||||
const uint16_t cap = maxPowerNumerator();
|
||||
txPowerSnap_ = (powerNumerator_ > cap) ? cap : powerNumerator_;
|
||||
}
|
||||
txBsrrWave_.configure(setW, resetW, txGateRuns_, nRuns, txMultiplySnap_, txPowerSnap_);
|
||||
|
||||
txBsrrHalfLen_ = (uint16_t)(irproto::kIsrTxBsrrWordCount / 2U);
|
||||
txBsrrWave_.fill(txBsrrWords_, irproto::kIsrTxBsrrWordCount);
|
||||
|
||||
txBsrrReadIdx_ = 0;
|
||||
txBsrrTicksSent_ = 0;
|
||||
|
||||
signal = preamb;
|
||||
isSending = true;
|
||||
state = HIGH;
|
||||
currentBitSequence = bitHigh;
|
||||
refreshBlindDecoderMuteState();
|
||||
if (port != nullptr)
|
||||
{
|
||||
port->BSRR = resetW;
|
||||
}
|
||||
IR_Encoder::carrierResume();
|
||||
// interrupts();
|
||||
}
|
||||
|
||||
void IR_Encoder::isr()
|
||||
@ -641,21 +812,71 @@ void IR_Encoder::_isr()
|
||||
if (!isSending)
|
||||
return;
|
||||
|
||||
ir_out_virtual = !ir_out_virtual && state;
|
||||
if (port == nullptr)
|
||||
return;
|
||||
|
||||
port->ODR &= ~(mask);
|
||||
port->ODR |= mask & (ir_out_virtual ? (uint16_t)0xFFFF : (uint16_t)0x0000);
|
||||
|
||||
TxFsmState st{};
|
||||
loadTxFsmFromMembers(st);
|
||||
const bool active = txAdvanceAfterOutput(st, sendBuffer);
|
||||
storeTxFsmToMembers(st);
|
||||
|
||||
if (!active)
|
||||
if (txIsrLegacyMode_)
|
||||
{
|
||||
const uint32_t setW = (uint32_t)mask;
|
||||
const uint32_t resetW = ((uint32_t)mask) << 16U;
|
||||
if (!state)
|
||||
{
|
||||
port->BSRR = resetW;
|
||||
legacySlotInPeriod_ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
port->BSRR = (legacySlotInPeriod_ < txPowerSnap_) ? setW : resetW;
|
||||
legacySlotInPeriod_++;
|
||||
if (legacySlotInPeriod_ >= txMultiplySnap_)
|
||||
{
|
||||
legacySlotInPeriod_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
legacyPhysCounter_++;
|
||||
if (legacyPhysCounter_ < legacyPhysPerLogical_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
legacyPhysCounter_ = 0;
|
||||
|
||||
TxFsmState st{};
|
||||
loadTxFsmFromMembers(st);
|
||||
const bool active = txAdvanceAfterOutput(st, sendBuffer);
|
||||
storeTxFsmToMembers(st);
|
||||
|
||||
if (!active)
|
||||
{
|
||||
port->BSRR = resetW;
|
||||
isSending = false;
|
||||
refreshBlindDecoderMuteState();
|
||||
carrierStopPending = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
port->BSRR = txBsrrWords_[txBsrrReadIdx_];
|
||||
txBsrrReadIdx_++;
|
||||
txBsrrTicksSent_++;
|
||||
|
||||
if (txBsrrTicksSent_ >= txBsrrTotalTicks_)
|
||||
{
|
||||
port->BSRR = ((uint32_t)mask) << 16U;
|
||||
isSending = false;
|
||||
refreshBlindDecoderMuteState();
|
||||
carrierStopPending = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (txBsrrReadIdx_ == txBsrrHalfLen_)
|
||||
{
|
||||
txBsrrWave_.fill(&txBsrrWords_[0], txBsrrHalfLen_);
|
||||
}
|
||||
else if (txBsrrReadIdx_ >= irproto::kIsrTxBsrrWordCount)
|
||||
{
|
||||
txBsrrReadIdx_ = 0;
|
||||
txBsrrWave_.fill(&txBsrrWords_[txBsrrHalfLen_], txBsrrHalfLen_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
59
IR_Encoder.h
59
IR_Encoder.h
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "IR_config.h"
|
||||
#include "IrTxBsrrWave.h"
|
||||
|
||||
// TODO: Отложенная передача после завершения приема
|
||||
|
||||
@ -22,10 +23,7 @@ class IR_Encoder : public IR_FOX
|
||||
public:
|
||||
static HardwareTimer* IR_Timer;
|
||||
|
||||
struct IR_TxGateRun {
|
||||
uint16_t lenTicks; // number of timer ticks at carrierFrec*2
|
||||
bool gate; // true: carrier enabled (output toggles), false: silent (output forced low)
|
||||
};
|
||||
using IR_TxGateRun = IrTxGateRun;
|
||||
|
||||
using ExternalTxBusyFn = bool (*)(void *ctx);
|
||||
using ExternalTxStartFn = bool (*)(void *ctx, IR_Encoder *enc, const uint8_t *packet, uint8_t len);
|
||||
@ -40,12 +38,42 @@ public:
|
||||
IR_Encoder(uint8_t pin, uint16_t addr = 0, IR_DecoderRaw *decPair = nullptr, bool autoHandle = true);
|
||||
static void isr();
|
||||
static void begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, uint8_t priority, void(*isrCallback)() = nullptr);
|
||||
/** Configure timer frequency for TX clock (carrierFrec*2) without attaching ISR. */
|
||||
/**
|
||||
* Глобальный знаменатель: частота таймера TX = carrierFrec × multiply (слотов на период несущей).
|
||||
* По умолчанию multiply=2 (как бывшие carrierFrec×2). Задавать до begin/beginClockOnly либо после
|
||||
* изменения вызвать retuneCarrierClock() (не менять multiply во время активной передачи).
|
||||
*/
|
||||
static void setCarrierMultiply(uint16_t multiply);
|
||||
static uint16_t carrierMultiply();
|
||||
/** Повторно применить carrierFrec×multiply к IR_Timer (pause + setOverflow), ISR не перенавешивает. */
|
||||
static void retuneCarrierClock();
|
||||
|
||||
/** Максимальный числитель мощности: ⌊multiply/2⌋ (100% в setPowerPercent). */
|
||||
static uint16_t maxPowerNumerator();
|
||||
|
||||
/** Числитель N: при открытой огибающей N из multiply тиков HIGH за период несущей. Clamped к maxPowerNumerator(). */
|
||||
void setPowerNumerator(uint16_t n);
|
||||
uint16_t powerNumerator() const;
|
||||
/** p∈[0,100] → ближайший допустимый числитель; 100% даёт N = maxPowerNumerator(). */
|
||||
void setPowerPercent(uint8_t p);
|
||||
|
||||
/** После buildGateRuns: lenTicks в тактах 2×Fc → физические тики (carrierFrec×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. */
|
||||
static void beginClockOnly(HardwareTimer *timer);
|
||||
static HardwareTimer* get_IR_Timer();
|
||||
/** Call from main loop/tick: if ISR requested carrier stop, pause timer here (not in ISR). */
|
||||
static void tick();
|
||||
|
||||
/**
|
||||
* Режим внутреннего TX без DMA: false — BSRR + кольцо (buildGateRuns + scaleGateRunsToPhysical);
|
||||
* true — FSM «налету» + скважность несущей как у буферного пути (подшаги multiply/2 на шаг FSM).
|
||||
* Выставить до begin/rawSend (глобально на все IR_Encoder). Игнорируется при externalTxStartFn.
|
||||
*/
|
||||
static void setTxIsrLegacyMode(bool legacy);
|
||||
static bool txIsrLegacyMode();
|
||||
|
||||
/** Optional: register external TX backend (e.g. DMA driver). */
|
||||
static void setExternalTxBackend(ExternalTxStartFn startFn, ExternalTxBusyFn busyFn, void *ctx);
|
||||
|
||||
@ -99,6 +127,8 @@ public:
|
||||
void _isr();
|
||||
private:
|
||||
static volatile bool carrierStopPending;
|
||||
static bool txIsrLegacyMode_;
|
||||
static uint16_t s_carrierMultiply;
|
||||
static void carrierResume();
|
||||
static void carrierPauseIfIdle();
|
||||
|
||||
@ -146,6 +176,25 @@ private:
|
||||
void loadTxFsmFromMembers(TxFsmState &st) const;
|
||||
void storeTxFsmToMembers(const TxFsmState &st);
|
||||
|
||||
IrTxBsrrWave txBsrrWave_{};
|
||||
IR_TxGateRun txGateRuns_[irproto::kIsrTxMaxGateRuns]{};
|
||||
uint32_t txBsrrWords_[irproto::kIsrTxBsrrWordCount]{};
|
||||
uint16_t txBsrrReadIdx_ = 0;
|
||||
uint16_t txBsrrHalfLen_ = 0;
|
||||
uint32_t txBsrrTotalTicks_ = 0;
|
||||
uint32_t txBsrrTicksSent_ = 0;
|
||||
|
||||
/** Снимок на старт TX (буферный и legacy путь). */
|
||||
uint16_t txPowerSnap_ = 1;
|
||||
uint16_t txMultiplySnap_ = 2;
|
||||
|
||||
/** Legacy: физических тиков на один логический шаг FSM = multiply/2. */
|
||||
uint16_t legacyPhysPerLogical_ = 1;
|
||||
uint16_t legacyPhysCounter_ = 0;
|
||||
uint16_t legacySlotInPeriod_ = 0;
|
||||
|
||||
volatile uint16_t powerNumerator_ = 1;
|
||||
|
||||
IR_DecoderRaw *decPair = nullptr;
|
||||
IR_DecoderRaw *singleBlindDecoder = nullptr;
|
||||
IR_DecoderRaw **blindDecoders = nullptr;
|
||||
|
||||
@ -6,6 +6,11 @@
|
||||
/** Число потоков DMA-TX задаётся шаблоном: IrDmaTxStm32<2>, см. IrDmaTxStm32.h и irproto::kDefaultDmaTxMaxStreams. */
|
||||
namespace irproto {
|
||||
constexpr size_t kDefaultDmaTxMaxStreams = 4U;
|
||||
/** Кольцевой буфер BSRR-слов для ISR-TX (как у DMA: два полублока). Чётное число. */
|
||||
constexpr uint16_t kIsrTxBsrrWordCount = 256U;
|
||||
/** Максимум RLE-сегментов для buildGateRuns при ISR-TX. */
|
||||
constexpr size_t kIsrTxMaxGateRuns = 512U;
|
||||
static_assert((kIsrTxBsrrWordCount & 1U) == 0U, "kIsrTxBsrrWordCount must be even");
|
||||
}
|
||||
|
||||
// Пошаговый разбор кадра на Serial (по умолчанию выключено). Пульсы IRDEBUG на пинах не меняют.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR_Encoder.h"
|
||||
#include "IrTxBsrrWave.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_STM32) && defined(STM32G4xx)
|
||||
|
||||
@ -16,7 +17,7 @@
|
||||
#include "stm32g4xx_hal.h"
|
||||
|
||||
/**
|
||||
* STM32G4: ИК TX через DMA в GPIO BSRR, такт от TIM UPDATE (carrierFrec×2).
|
||||
* STM32G4: ИК TX через DMA в GPIO BSRR, такт от TIM UPDATE (carrierFrec × IR_Encoder::carrierMultiply()).
|
||||
*
|
||||
* Число слотов потоков — параметр шаблона (без макросов), например IrDmaTxStm32<2>.
|
||||
* По умолчанию: IrDmaTxStm32<> ≡ irproto::kDefaultDmaTxMaxStreams (см. IR_config.h).
|
||||
@ -37,7 +38,7 @@ public:
|
||||
uint32_t* dmaWords = nullptr;
|
||||
uint16_t dmaWordCount = 0;
|
||||
|
||||
IR_Encoder::IR_TxGateRun* gateRuns = nullptr;
|
||||
IrTxGateRun* gateRuns = nullptr;
|
||||
size_t maxGateRuns = 0;
|
||||
};
|
||||
|
||||
@ -134,13 +135,11 @@ private:
|
||||
uint16_t bufLen = 0;
|
||||
uint16_t halfLen = 0;
|
||||
|
||||
IR_Encoder::IR_TxGateRun* runs = nullptr;
|
||||
IrTxGateRun* runs = nullptr;
|
||||
size_t maxRuns = 0;
|
||||
|
||||
size_t runCount = 0;
|
||||
size_t runIndex = 0;
|
||||
uint16_t ticksLeftInRun = 0;
|
||||
bool carrierPhase = false;
|
||||
IrTxBsrrWave wave{};
|
||||
|
||||
uint32_t totalTicks = 0;
|
||||
volatile uint32_t ticksOutput = 0;
|
||||
@ -148,45 +147,14 @@ private:
|
||||
bool active = false;
|
||||
|
||||
void resetWave() {
|
||||
runIndex = 0;
|
||||
carrierPhase = false;
|
||||
wave.configure(setWord, resetWord, nullptr, 0, 2, 1);
|
||||
ticksOutput = 0;
|
||||
totalTicks = 0;
|
||||
runCount = 0;
|
||||
ticksLeftInRun = 0;
|
||||
}
|
||||
|
||||
IR_DMA_TX_HOT uint32_t nextWord() {
|
||||
if (runIndex >= runCount) {
|
||||
return resetWord;
|
||||
}
|
||||
const bool gate = runs[runIndex].gate;
|
||||
if (!gate) {
|
||||
carrierPhase = false;
|
||||
} else {
|
||||
carrierPhase = !carrierPhase;
|
||||
}
|
||||
const uint32_t out = (gate && carrierPhase) ? setWord : resetWord;
|
||||
|
||||
if (ticksLeftInRun > 0) {
|
||||
ticksLeftInRun--;
|
||||
}
|
||||
if (ticksLeftInRun == 0) {
|
||||
runIndex++;
|
||||
if (runIndex < runCount) {
|
||||
ticksLeftInRun = runs[runIndex].lenTicks;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
IR_DMA_TX_HOT void fill(uint32_t* dst, uint16_t count) {
|
||||
if (dst == nullptr || count == 0) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
*dst++ = nextWord();
|
||||
} while (--count != 0);
|
||||
wave.fill(dst, count);
|
||||
}
|
||||
|
||||
void onHalf() {
|
||||
@ -325,13 +293,25 @@ private:
|
||||
s.runCount = IR_Encoder::buildGateRuns(packet, len, s.runs, s.maxRuns);
|
||||
if (s.runCount == 0) return false;
|
||||
|
||||
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;
|
||||
for (size_t i = 0; i < s.runCount; i++) total += s.runs[i].lenTicks;
|
||||
s.totalTicks = total;
|
||||
|
||||
s.runIndex = 0;
|
||||
s.ticksLeftInRun = s.runs[0].lenTicks;
|
||||
s.carrierPhase = false;
|
||||
const uint16_t mult = IR_Encoder::carrierMultiply();
|
||||
uint16_t pwr = mult / 2U;
|
||||
if (s.enc != nullptr) {
|
||||
const uint16_t want = s.enc->powerNumerator();
|
||||
const uint16_t cap = IR_Encoder::maxPowerNumerator();
|
||||
pwr = (want > cap) ? cap : want;
|
||||
}
|
||||
|
||||
s.wave.configure(s.setWord, s.resetWord, s.runs, s.runCount, mult, pwr);
|
||||
|
||||
s.fill(&s.dmaBuf[0], s.bufLen);
|
||||
|
||||
|
||||
87
IrTxBsrrWave.h
Normal file
87
IrTxBsrrWave.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "IrTxGateTypes.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define IR_TX_BSRR_WAVE_HOT __attribute__((always_inline)) inline
|
||||
#else
|
||||
#define IR_TX_BSRR_WAVE_HOT inline
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Генерация потока 32-бит слов для GPIO BSRR из RLE-сегментов IrTxGateRun.
|
||||
* За один период несущей — multiply физических тиков; при gate — powerN из них HIGH (N ≤ multiply/2).
|
||||
*/
|
||||
class IrTxBsrrWave {
|
||||
public:
|
||||
void configure(uint32_t setW, uint32_t resetW, IrTxGateRun* r, size_t n, uint16_t multiply, uint16_t powerN) {
|
||||
setWord = setW;
|
||||
resetWord = resetW;
|
||||
runs = r;
|
||||
runCount = n;
|
||||
multiply_ = multiply < 2 ? 2 : multiply;
|
||||
const uint16_t cap = static_cast<uint16_t>(multiply_ / 2U);
|
||||
powerN_ = (powerN > cap) ? cap : powerN;
|
||||
resetWave();
|
||||
}
|
||||
|
||||
void resetWave() {
|
||||
runIndex_ = 0;
|
||||
slotInPeriod_ = 0;
|
||||
ticksLeftInRun_ = 0;
|
||||
if (runCount > 0U && runs != nullptr) {
|
||||
ticksLeftInRun_ = runs[0].lenTicks;
|
||||
}
|
||||
}
|
||||
|
||||
IR_TX_BSRR_WAVE_HOT uint32_t nextWord() {
|
||||
if (runIndex_ >= runCount) {
|
||||
return resetWord;
|
||||
}
|
||||
const bool gate = runs[runIndex_].gate;
|
||||
uint32_t out;
|
||||
if (!gate) {
|
||||
slotInPeriod_ = 0;
|
||||
out = resetWord;
|
||||
} else {
|
||||
out = (slotInPeriod_ < powerN_) ? setWord : resetWord;
|
||||
slotInPeriod_++;
|
||||
if (slotInPeriod_ >= multiply_) {
|
||||
slotInPeriod_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ticksLeftInRun_ > 0) {
|
||||
ticksLeftInRun_--;
|
||||
}
|
||||
if (ticksLeftInRun_ == 0) {
|
||||
runIndex_++;
|
||||
if (runIndex_ < runCount) {
|
||||
ticksLeftInRun_ = runs[runIndex_].lenTicks;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
IR_TX_BSRR_WAVE_HOT void fill(uint32_t* dst, uint16_t count) {
|
||||
if (dst == nullptr || count == 0) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
*dst++ = nextWord();
|
||||
} while (--count != 0);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t setWord = 0;
|
||||
uint32_t resetWord = 0;
|
||||
IrTxGateRun* runs = nullptr;
|
||||
size_t runCount = 0;
|
||||
uint16_t multiply_ = 2;
|
||||
uint16_t powerN_ = 1;
|
||||
size_t runIndex_ = 0;
|
||||
uint16_t ticksLeftInRun_ = 0;
|
||||
uint16_t slotInPeriod_ = 0;
|
||||
};
|
||||
13
IrTxGateTypes.h
Normal file
13
IrTxGateTypes.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Один RLE-сегмент огибающей несущей.
|
||||
* В buildGateRuns: lenTicks в тактах логической шкалы 2×carrierFrec (как раньше).
|
||||
* После IR_Encoder::scaleGateRunsToPhysical — в физических тиках carrierFrec×multiply.
|
||||
*/
|
||||
struct IrTxGateRun {
|
||||
uint16_t lenTicks;
|
||||
bool gate;
|
||||
};
|
||||
@ -91,6 +91,7 @@ void setup() {
|
||||
rebuildIrPayload();
|
||||
|
||||
#if LONGDATA_USE_DMA
|
||||
// IR_Encoder::setCarrierMultiply(N); // до beginClockOnly; после смены — retuneCarrierClock()
|
||||
IR_Encoder::beginClockOnly(&irTimer);
|
||||
|
||||
IrDmaTxStm32<kIrDmaStreams>::Config cfg;
|
||||
|
||||
Reference in New Issue
Block a user