# Контракт бэкенда DMA-TX ИК (`IrDmaTxStm32`) См. также: [IR_TX_MODES.md](IR_TX_MODES.md) — общая схема выбора `legacy ISR`, `buffered ISR` и `external backend`. Платформа: **STM32G4**, Arduino STM32. Передача: **DMA memory → GPIO BSRR**, запрос от **TIM UPDATE** (частота `carrierFrec×2` из `IR_Encoder::beginClockOnly`). ### Число потоков (шаблон) Класс: **`IrDmaTxStm32`**. Число слотов в `Config::streams[]` и внутреннем массиве задаётся **в коде**, без `-D` и без макроса до инклюда: ```cpp constexpr size_t kStreams = 2; static IrDmaTxStm32 dma; IrDmaTxStm32::Config cfg; // IRQ: IrDmaTxStm32::instance() ``` По умолчанию: **`IrDmaTxStm32<>`** эквивалентно **`IrDmaTxStm32`** (`IR_config.h`, обычно 4). Реализация в заголовке (отдельного `.cpp` нет). ## Роль библиотеки - Разбор пакета в RLE-пробеги: `IR_Encoder::buildGateRuns`. - Генерация слов для BSRR (несущая/тишина по тикам), предзаполнение буфера и дозаполнение по прерываниям half/complete. - Настройка канала DMA, DMAMUX, кольцевой режим, старт/стоп DMA и таймера, колбэки HAL. - В `begin()` только `HAL_NVIC_EnableIRQ` для каналов DMA (без `SetPriority`). ## Роль прошивки (клиента) ### Буферы и размеры На **каждый** поток в `StreamCfg` клиент передаёт: | Поле | Смысл | |------|--------| | `dmaWords` | Указатель на массив `uint32_t` — слова для записи в BSRR. | | `dmaWordCount` | Число **слов** (32-bit), **чётное**, ≥ 2. Половина — один «полубуфер» для HT/TC IRQ. | | `gateRuns` | Массив `IR_Encoder::IR_TxGateRun` для выхода `buildGateRuns`. | | `maxGateRuns` | Длина этого массива. Должен быть достаточен для самого длинного кадра. | Память и выравнивание — ответственность клиента; типичные порядки: 4096 слов DMA, 1024 ранов (как в машинке). ### Таймер и DMA - `HardwareTimer` / тот же TIM, что и `beginClockOnly`, без конкурирующего `attachInterrupt` на UPDATE. - `instance`, `irq`, `dmamuxRequest` (например `DMA_REQUEST_TIM17_UP`) — из схемы платы; оба потока на одном TIM обычно используют **один** `TIMx_UP` в DMAMUX. ### Приоритеты NVIC Не задаются в библиотеке. После `begin()` клиент выставляет preempt/sub для `DMA1_ChannelN` (и согласует с приёмом EXTI и др.), например общей функцией вроде `Car_applyInterruptPriorities()`. ### Прерывания DMA Библиотека **не** объявляет `DMA1_ChannelN_IRQHandler`. В одном `.cpp` прошивки — единственное определение на канал, внутри: `IrDmaTxStm32::instance()->irqForStream(i)` (тот же **N**, что у объекта бэкенда) или `IrDmaTxStm32_onDmaHandle(hdma)`. ## Контракт `IR_Encoder::setExternalTxBackend` Подключение: `setExternalTxBackend(startFn, busyFn, ctx)`. - **`startFn(ctx, enc, packet, len)`** — должен вызвать `IrDmaTxStm32::start(enc, packet, len)` (или обёртку). Возвращает успех старта DMA. - **`busyFn(ctx)`** — пока возвращает «занято», новая отправка не стартует. У `IrDmaTxStm32::busy()`: **true**, если **все** настроенные потоки в передаче (для двух передатчиков — оба активны); иначе можно запустить второй канал. ## Сбой `begin()` При ошибке `HAL_DMA_Init` и т.п. `begin()` возвращает `false`, `instance()` не используется для IRQ до успешного `begin()`.