Для электронщиков и радиолюбителей

Практический материал для ADC и DMA на Raspberry Pi Pico

Практический материал для ADC и DMA на Raspberry Pi Pico

В продолжение темы «Скрытые возможности ADC и DMA: быстрые измерения на Raspberry Pi Pico» приведем минимальную, но правильную схему ADC + DMA для Raspberry Pi Pico (RP2040 / Pico 2 аналогично), с пояснением почему именно так.


Архитектура решения

Идея простая и «осциллографическая»:
ADC работает в свободном режиме с заданной частотой → кладёт выборки в FIFODMA автоматически копирует их в массив в SRAM → CPU получает готовый буфер и занимается обработкой (FFT, фильтрация, USB, сохранение).

CPU не участвует в процессе измерений — это ключ.


Аппаратная схема (минимальная, но корректная)

Вход ADC (например, GPIO26 / ADC0)

      ┌─────[ 1k–10k ]─────┐
      │                    │
SIGNAL ──┤                    ├── GPIO26 (ADC0)
      │                    │
      └─────[ 100 nF ]─────┘
                 |
                GND

Почему так:

  • Резистор ограничивает ток и снижает влияние входной ёмкости ADC
  • Конденсатор образует RC-фильтр (антиалиасинг, подавление шума)
  • Значения подбираются под частоту дискретизации

Вход 0–3.3 В, без защиты — если сигнал «грязный», добавь диоды.


Настройка ADC (C SDK)

Инициализация ADC

adc_init();
adc_gpio_init(26);          // GPIO26 → ADC0
adc_select_input(0);        // канал ADC0

Настройка FIFO

adc_fifo_setup(
    true,    // enable FIFO
    true,    // enable DMA request
    1,       // DREQ when at least 1 sample
    false,   // no ERR bit
    false    // 12-bit samples (no shift)
);

Важно: FIFO + DREQ — это ключ для DMA.


Частота дискретизации

adc_set_clkdiv(96.0f);

Формула:

Fs = 48 MHz / clkdiv

Примеры:

  • clkdiv = 96500 kS/s
  • clkdiv = 1000 → 48 kS/s (аудио)

Настройка DMA

Конфигурация канала DMA

#define SAMPLE_COUNT 4096
uint16_t samples[SAMPLE_COUNT];

int dma_chan = dma_claim_unused_channel(true);
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);

channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
channel_config_set_read_increment(&cfg, false);
channel_config_set_write_increment(&cfg, true);
channel_config_set_dreq(&cfg, DREQ_ADC);

Пояснение:

  • DMA_SIZE_16 → ADC выдаёт 12 бит в 16-битном слове
  • read_increment = false → читаем всегда ADC FIFO
  • write_increment = true → пишем в массив
  • DREQ_ADC → синхронизация по готовности ADC

Запуск DMA

dma_channel_configure(
    dma_chan,
    &cfg,
    samples,          // куда писать
    &adc_hw->fifo,    // откуда читать
    SAMPLE_COUNT,
    false
);

Запуск измерений

adc_run(true);
dma_start_channel_mask(1u << dma_chan);

Ожидание завершения:

dma_channel_wait_for_finish_blocking(dma_chan);
adc_run(false);

Теперь samples[] содержит ровно SAMPLE_COUNT измерений с идеально равномерным интервалом.


Что делать дальше с данными

Примеры:

Аудио

sample_voltage = samples[i] * 3.3f / 4096.0f;

FFT

  • одно ядро: сбор
  • второе ядро: FFT (kissFFT, CMSIS-DSP)

Осциллограф

  • DMA → ring buffer
  • USB CDC → PC
  • отрисовка в Python / Qt

Реальные цифры (RP2040 @ default clock)

ПараметрЗначение
Максимальная Fs~480–500 kS/s
Джиттер≈ 0
Загрузка CPU≈ 0%
Пропускиотсутствуют

Частые ошибки

— читать ADC в цикле
— включать IRQ вместе с DMA
— использовать float во время сбора
— забывать антиалиасинг
— пытаться читать несколько каналов «синхронно»


Итоги

Это каноническая схема использования ADC в Pico:

  • ADC → FIFO → DMA → RAM
  • CPU и ядра не мешают измерениям
  • тайминги стабильны, результат предсказуем

По архитектуре это настоящий измерительный pipeline, а не «хоббийный опрос АЦП».