dma fix priority and debug

This commit is contained in:
2026-04-02 17:25:10 +03:00
parent af3e012aac
commit e7d7c0e1c1
18 changed files with 89260 additions and 142 deletions

386
IrDmaTxStm32.h Normal file
View File

@ -0,0 +1,386 @@
#pragma once
#include "IR_Encoder.h"
#if defined(ARDUINO_ARCH_STM32) && defined(STM32G4xx)
#include <Arduino.h>
#include <HardwareTimer.h>
#if defined(__GNUC__)
#define IR_DMA_TX_HOT __attribute__((always_inline)) inline
#else
#define IR_DMA_TX_HOT inline
#endif
#include "stm32g4xx_hal.h"
/**
* STM32G4: ИК TX через DMA в GPIO BSRR, такт от TIM UPDATE (carrierFrec×2).
*
* Число слотов потоков — параметр шаблона (без макросов), например IrDmaTxStm32<2>.
* По умолчанию: IrDmaTxStm32<> ≡ irproto::kDefaultDmaTxMaxStreams (см. IR_config.h).
*
* Контракт: ref/IR_DMA_TX_backend.md
*/
template<size_t MaxStreams = irproto::kDefaultDmaTxMaxStreams>
class IrDmaTxStm32 {
static_assert(MaxStreams >= 1U, "IrDmaTxStm32: MaxStreams >= 1");
public:
struct StreamCfg {
DMA_Channel_TypeDef* instance = nullptr;
IRQn_Type irq = IRQn_Type(0);
uint32_t dmamuxRequest = 0;
IR_Encoder* enc = nullptr;
uint32_t* dmaWords = nullptr;
uint16_t dmaWordCount = 0;
IR_Encoder::IR_TxGateRun* gateRuns = nullptr;
size_t maxGateRuns = 0;
};
struct Config {
HardwareTimer* timer = nullptr;
uint8_t streamCount = 0;
StreamCfg streams[MaxStreams];
};
bool begin(const Config& cfg) {
cfg_ = cfg;
streamCount_ = cfg.streamCount;
if (cfg_.timer == nullptr || streamCount_ == 0) return false;
if (streamCount_ > MaxStreams) return false;
htim_ = cfg_.timer->getHandle();
if (htim_ == nullptr) return false;
for (uint8_t i = 0; i < streamCount_; i++) {
const StreamCfg& sc = cfg_.streams[i];
if (sc.enc == nullptr || sc.instance == nullptr) return false;
if (sc.dmaWords == nullptr || sc.dmaWordCount < 2U) return false;
if ((sc.dmaWordCount & 1U) != 0U) return false;
if (sc.gateRuns == nullptr || sc.maxGateRuns == 0U) return false;
}
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMAMUX1_CLK_ENABLE();
for (uint8_t i = 0; i < streamCount_; i++) {
const StreamCfg& sc = cfg_.streams[i];
if (!initStream(streams_[i], sc)) {
return false;
}
}
s_instance = this;
activeCount_ = 0;
for (uint8_t i = 0; i < streamCount_; i++) {
HAL_NVIC_EnableIRQ(streams_[i].dmaIrq);
}
return true;
}
static IrDmaTxStm32<MaxStreams>* instance() {
return s_instance;
}
bool busy() const {
if (streamCount_ == 0) return false;
for (uint8_t i = 0; i < streamCount_; i++) {
if (!streams_[i].active) return false;
}
return true;
}
bool start(IR_Encoder* enc, const uint8_t* packet, uint8_t len) {
if (enc == nullptr) return false;
for (uint8_t i = 0; i < streamCount_; i++) {
if (streams_[i].enc == enc) {
return startStream(streams_[i], packet, len);
}
}
return false;
}
void irqForStream(size_t streamIndex) {
if (streamIndex >= streamCount_) return;
HAL_DMA_IRQHandler(&streams_[streamIndex].hdma);
}
DMA_HandleTypeDef* dmaHandle(size_t streamIndex) {
if (streamIndex >= streamCount_) return nullptr;
return &streams_[streamIndex].hdma;
}
private:
struct TxStream {
DMA_HandleTypeDef hdma{};
DMA_Channel_TypeDef* dmaInstance = nullptr;
IRQn_Type dmaIrq = IRQn_Type(0);
uint32_t dmamuxRequest = 0;
IR_Encoder* enc = nullptr;
GPIO_TypeDef* port = nullptr;
uint16_t mask = 0;
uint32_t setWord = 0;
uint32_t resetWord = 0;
uint32_t* dmaBuf = nullptr;
uint16_t bufLen = 0;
uint16_t halfLen = 0;
IR_Encoder::IR_TxGateRun* runs = nullptr;
size_t maxRuns = 0;
size_t runCount = 0;
size_t runIndex = 0;
uint16_t ticksLeftInRun = 0;
bool carrierPhase = false;
uint32_t totalTicks = 0;
volatile uint32_t ticksOutput = 0;
bool active = false;
void resetWave() {
runIndex = 0;
carrierPhase = false;
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);
}
void onHalf() {
ticksOutput += halfLen;
fill(&dmaBuf[0], halfLen);
}
void onComplete() {
ticksOutput += halfLen;
fill(&dmaBuf[halfLen], halfLen);
}
void onError() {}
};
static IrDmaTxStm32<MaxStreams>* s_instance;
Config cfg_{};
TIM_HandleTypeDef* htim_ = nullptr;
TxStream streams_[MaxStreams]{};
uint8_t streamCount_ = 0;
volatile uint8_t activeCount_ = 0;
static uint32_t u32ptr(const volatile void* p) {
return (uint32_t)(uintptr_t)p;
}
void startTimerIfNeeded() {
if (htim_ == nullptr) return;
if (activeCount_ != 1) return;
__HAL_TIM_DISABLE_DMA(htim_, TIM_DMA_UPDATE);
__HAL_TIM_CLEAR_FLAG(htim_, TIM_FLAG_UPDATE);
__HAL_TIM_SET_COUNTER(htim_, 0);
__HAL_TIM_ENABLE_DMA(htim_, TIM_DMA_UPDATE);
HAL_TIM_Base_Start(htim_);
}
void stopTimerIfIdle() {
if (htim_ == nullptr) return;
if (activeCount_ != 0) return;
__HAL_TIM_DISABLE_DMA(htim_, TIM_DMA_UPDATE);
HAL_TIM_Base_Stop(htim_);
}
static TxStream* streamFromDma(DMA_HandleTypeDef* hdma) {
if (s_instance == nullptr || hdma == nullptr) return nullptr;
for (uint8_t i = 0; i < s_instance->streamCount_; i++) {
if (hdma == &s_instance->streams_[i].hdma) {
return &s_instance->streams_[i];
}
}
return nullptr;
}
static void dmaHalfCpltCb(DMA_HandleTypeDef* hdma) {
auto* s = streamFromDma(hdma);
if (s == nullptr || !s->active) return;
s->onHalf();
if (s_instance != nullptr && s->ticksOutput >= s->totalTicks) {
s_instance->stopStream(*s);
}
}
static void dmaCpltCb(DMA_HandleTypeDef* hdma) {
auto* s = streamFromDma(hdma);
if (s == nullptr || !s->active) return;
s->onComplete();
if (s_instance != nullptr && s->ticksOutput >= s->totalTicks) {
s_instance->stopStream(*s);
}
}
static void dmaErrorCb(DMA_HandleTypeDef* hdma) {
auto* s = streamFromDma(hdma);
if (s == nullptr) return;
s->onError();
if (s_instance != nullptr) {
s_instance->stopStream(*s);
}
}
bool initStream(TxStream& s, const StreamCfg& chCfg) {
s.enc = chCfg.enc;
s.dmaInstance = chCfg.instance;
s.dmaIrq = chCfg.irq;
s.dmamuxRequest = chCfg.dmamuxRequest;
s.dmaBuf = chCfg.dmaWords;
s.bufLen = chCfg.dmaWordCount;
s.halfLen = (uint16_t)(chCfg.dmaWordCount / 2U);
s.runs = chCfg.gateRuns;
s.maxRuns = chCfg.maxGateRuns;
s.port = (s.enc != nullptr) ? s.enc->getPort() : nullptr;
s.mask = (s.enc != nullptr) ? s.enc->getPinMask() : 0;
s.setWord = (uint32_t)s.mask;
s.resetWord = ((uint32_t)s.mask) << 16;
s.resetWave();
s.hdma.Instance = s.dmaInstance;
s.hdma.Init.Request = s.dmamuxRequest;
s.hdma.Init.Direction = DMA_MEMORY_TO_PERIPH;
s.hdma.Init.PeriphInc = DMA_PINC_DISABLE;
s.hdma.Init.MemInc = DMA_MINC_ENABLE;
s.hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
s.hdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
s.hdma.Init.Mode = DMA_CIRCULAR;
s.hdma.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_DeInit(&s.hdma);
if (HAL_DMA_Init(&s.hdma) != HAL_OK) {
return false;
}
s.hdma.XferHalfCpltCallback = dmaHalfCpltCb;
s.hdma.XferCpltCallback = dmaCpltCb;
s.hdma.XferErrorCallback = dmaErrorCb;
s.hdma.XferAbortCallback = nullptr;
return true;
}
bool startStream(TxStream& s, const uint8_t* packet, uint8_t len) {
if (s.enc == nullptr || s.port == nullptr || s.mask == 0) return false;
if (s.active) return false;
if (s.dmaBuf == nullptr || s.bufLen < 2 || s.halfLen == 0) return false;
if (s.runs == nullptr || s.maxRuns == 0) return false;
s.resetWave();
s.runCount = IR_Encoder::buildGateRuns(packet, len, s.runs, s.maxRuns);
if (s.runCount == 0) return false;
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;
s.fill(&s.dmaBuf[0], s.bufLen);
s.port->BSRR = s.resetWord;
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) {
return false;
}
s.active = true;
activeCount_++;
startTimerIfNeeded();
return true;
}
void stopStream(TxStream& s) {
if (!s.active) return;
s.active = false;
HAL_DMA_Abort_IT(&s.hdma);
if (s.port != nullptr) {
s.port->BSRR = s.resetWord;
}
if (s.enc != nullptr) {
s.enc->externalFinishSend();
}
if (activeCount_ > 0) activeCount_--;
stopTimerIfIdle();
}
};
template<size_t MaxStreams>
IrDmaTxStm32<MaxStreams>* IrDmaTxStm32<MaxStreams>::s_instance = nullptr;
inline void IrDmaTxStm32_onDmaHandle(DMA_HandleTypeDef* hdma) {
HAL_DMA_IRQHandler(hdma);
}
#elif defined(ARDUINO_ARCH_STM32)
#error "IrDmaTxStm32: добавьте ветку HAL для вашей серии STM32 (сейчас только STM32G4xx)."
#else
template<size_t MaxStreams = irproto::kDefaultDmaTxMaxStreams>
class IrDmaTxStm32 {};
#endif