1. Тема дня

DMA, circular buffers, half-transfer/complete callbacks, UART RX DMA, ADC DMA, cache coherency. Главная мысль: DMA - это отдельный участник системы, который читает/пишет память параллельно CPU.

2. Зачем это нужно в проектах

DMA нужен для стабильного UART/EC25/RS-485 RX, ADC scan, HIL stream, timer+DMA для PWM/CCR, неблокирующего логирования.

3. Теория

Обычно:

text
периферия -> interrupt -> CPU читает -> CPU кладёт в массив

С DMA:

text
периферия -> DMA controller -> память

DMA работает параллельно CPU, поэтому надо решить: кто владеет буфером, какую область можно читать, не перезаписал ли DMA старые данные, нет ли cache coherency. Режимы:

text
Normal mode:
  перенёс заданный объём и остановился.
Circular mode:
  дошёл до конца buffer и начал сначала.

Half-transfer/transfer-complete делят circular buffer на две половины: пока DMA пишет одну, CPU обрабатывает другую. UART RX DMA position:

c
#define UART_RX_DMA_SIZE 256
static uint8_t uart_rx_dma_buf[UART_RX_DMA_SIZE];
static volatile size_t uart_rx_old_pos = 0;
static void uart_rx_check_new_data(void)
{
    size_t new_pos = UART_RX_DMA_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx);
    if (new_pos == uart_rx_old_pos) return;
    if (new_pos > uart_rx_old_pos) {
        parser_feed(&uart_rx_dma_buf[uart_rx_old_pos], new_pos - uart_rx_old_pos);
    } else {
        parser_feed(&uart_rx_dma_buf[uart_rx_old_pos], UART_RX_DMA_SIZE - uart_rx_old_pos);
        parser_feed(&uart_rx_dma_buf[0], new_pos);
    }
    uart_rx_old_pos = new_pos;
}

ADC DMA scan обычно идёт интерливингом: CH0, CH1, CH2, CH3, CH0… Cache coherency:

text
DMA RX -> CPU должен invalidate cache перед чтением.
DMA TX -> CPU должен clean cache перед стартом DMA.
Buffers -> DMA-capable и cache-line aligned.

ESP32-S3/P4: выделяй DMA buffer из памяти с MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED.

4. Типичные ошибки

  1. Думать, что DMA чинит блокирующий parser.
  2. Читать весь DMA buffer без позиции DMA.
  3. Не использовать HT/TC/IDLE.
  4. Использовать buffer из неподходящей памяти.
  5. Не учитывать cache coherency.
  6. Делать тяжёлую обработку в DMA callback.
  7. Не вести DMA diagnostics.

5. Практическое задание

Создай DMA_BUFFER_POLICY.md:

markdown
# DMA buffer policy
1. DMA buffer has one owner: low-level driver.
2. Application never modifies DMA buffer directly.
3. Callback does not parse full protocol or print logs.
4. Callback only marks block ready and wakes task.
5. CPU must not read memory currently written by DMA.
6. DMA-capable memory and alignment are mandatory.
7. Cache maintenance is mandatory on cache-enabled MCUs.
8. Every DMA stream has diagnostics.

Диагностика:

c
typedef struct {
    uint32_t ht_count;
    uint32_t tc_count;
    uint32_t idle_count;
    uint32_t error_count;
    uint32_t overrun_count;
    uint32_t dropped_bytes;
    uint32_t max_lag_bytes;
    int64_t max_processing_time_us;
} dma_stream_diag_t;

6. Что почитать или попробовать дальше

STM32 HAL DMA, ST AN4839 cache coherency, ESP-IDF heap capabilities, ESP-IDF memory synchronization.

DMA RX записал RAM, но CPU читает старую cache line. Что требуется для cacheable RX buffer?

Задание

В circular DMA buffer из 256 байт old_pos=20, new_pos=20. Можно ли заключить, что новых байтов нет? Назовите два возможных случая и способ обнаружения потери.

Критерии самопроверки: Укажите оба случая, дополнительный учёт прогресса и реакцию на overrun.

Показать ответ автора

Позиции равны и при отсутствии движения, и после полного оборота 256 байт (либо нескольких оборотов). Одна позиция этого не различает. Нужны HT/TC/sequence или счётчик оборотов и оценка service gap; если consumer не успел, зафиксируйте overrun/desync, а не передавайте повреждённый stream как валидный.