Search topics...
ADCDMA and Modesfoundational

Why is using DMA with the ADC important, and how do you set it up?

0 upvotes
Practice with AISoon
Study the fundamentals first — ADC topic page

Without DMA, the CPU must read each ADC conversion result by polling the EOC (End of Conversion) flag or responding to an interrupt. At high sample rates — say 1 Msps on an STM32F4 — this means a million interrupts per second. Each interrupt requires context save/restore, flag checking, data copy, and return, consuming roughly 30-50 clock cycles. At 168 MHz, a million 40-cycle ISRs consume 24% of the CPU — and that is before any processing of the data. At lower-cost MCUs with slower clocks, the overhead is proportionally worse and can consume 100% of the CPU, leaving no time for the application.

DMA solves this by transferring ADC results directly from the ADC data register to a RAM buffer without any CPU involvement. The typical setup is: configure the ADC in continuous conversion mode (or timer-triggered mode for a precise, jitter-free sample rate), enable DMA requests on EOC, and configure the DMA channel with source = ADC_DR (peripheral address, no increment), destination = RAM buffer (memory address, auto-increment), transfer width = half-word (16-bit for a 12-bit ADC), and circular mode so the DMA wraps to the buffer start when it reaches the end. For multi-channel scan mode, the DMA stores each channel's result sequentially in the buffer — element 0 is channel 1, element 1 is channel 2, and so on.

Enable the Half-Transfer (HT) and Transfer-Complete (TC) interrupts to implement double-buffering: when HT fires, process the first half of the buffer while DMA fills the second half. When TC fires, process the second half while DMA wraps around and fills the first half. This gives the CPU a full half-buffer's worth of time to process data, and the interrupt rate drops from once-per-sample to twice-per-buffer. With a 256-sample buffer, that is 2 interrupts instead of 256 — a 128x reduction in interrupt overhead.

c
// DMA + ADC circular buffer setup (STM32 HAL pseudocode)
uint16_t adc_buf[256]; // Half-word buffer, 256 samples
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buf, 256);
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc) {
process_samples(adc_buf, 128); // First half ready
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
process_samples(adc_buf + 128, 128); // Second half ready
}

Source: ADC Q&A