Configuring STM32 ADC DMA mode sampling correctly is the key to high-speed, efficient data acquisition in embedded systems. Using DMA (Direct Memory Access) with the ADC frees the CPU from polling, enabling simultaneous data collection and processing — essential for oscilloscopes, spectrum analysers, and sensor data loggers commonly built by Indian makers.
Table of Contents
- STM32 ADC Architecture
- Why DMA for ADC
- Configuration in STM32CubeMX
- ADC DMA Code Example
- Multi-Channel Scan Mode
- Achieving Maximum Sample Rates
- Frequently Asked Questions
STM32 ADC Architecture
STM32F4 series has three ADC peripherals (ADC1, ADC2, ADC3), each 12-bit with up to 18 channels. The ADC clock is derived from APB2 (up to 84 MHz on F411), with the ADC running at APB2/2, /4, /6, or /8 — typically 21 MHz for stable operation. At 21 MHz and 3 cycles sample time + 12 conversion cycles, maximum sample rate is approximately 1.75 MSPS per channel.
Why DMA for ADC
Without DMA, reading ADC requires the CPU to poll the EOC (end-of-conversion) flag, copy the value, then restart conversion — consuming significant CPU cycles. With DMA, the ADC automatically transfers each converted value directly to a memory buffer. The CPU is notified only when the buffer is half-full or full, leaving it free for processing and other tasks. This is how STM32 ADC DMA high-speed sampling achieves sustained throughput.
Configuration in STM32CubeMX
Steps to configure ADC1 with DMA in STM32CubeMX:
- Enable ADC1, select channel (e.g., IN0 on PA0)
- Set Continuous Conversion Mode: Enabled
- DMA Settings → Add DMA Request for ADC1
- Set DMA Mode: Circular (for continuous sampling)
- Set Data Width: Half Word (16-bit, stores 12-bit ADC in uint16_t)
- Set memory increment: Enabled
- Generate code and open in STM32CubeIDE
ADC DMA Code Example
/* STM32F4 ADC1 DMA continuous sampling */
#define ADC_BUFFER_SIZE 1024
uint16_t adcBuffer[ADC_BUFFER_SIZE];
volatile uint8_t bufferReady = 0;
volatile uint8_t halfBuffer = 0;
// Start ADC with DMA in main()
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcBuffer, ADC_BUFFER_SIZE);
// DMA half-complete callback (first half of buffer ready)
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
halfBuffer = 0; // Process adcBuffer[0..511]
bufferReady = 1;
}
// DMA complete callback (second half of buffer ready)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
halfBuffer = 1; // Process adcBuffer[512..1023]
bufferReady = 1;
}
// In main loop
while(1) {
if (bufferReady) {
bufferReady = 0;
uint16_t *processBuffer = halfBuffer ?
&adcBuffer[ADC_BUFFER_SIZE/2] : adcBuffer;
// Process 512 samples
for (int i = 0; i < ADC_BUFFER_SIZE/2; i++) {
float voltage = processBuffer[i] * 3.3f / 4096.0f;
// Your processing here
}
}
}
Multi-Channel Scan Mode
To sample multiple channels, enable scan mode in STM32CubeMX. ADC1 scans channels sequentially and stores interleaved results in the DMA buffer.
// With 3 channels (CH0, CH1, CH2), buffer layout is:
// [CH0][CH1][CH2][CH0][CH1][CH2]...
#define NUM_CHANNELS 3
#define SAMPLES_PER_CHANNEL 256
uint16_t adcBuffer[NUM_CHANNELS * SAMPLES_PER_CHANNEL];
// Extract per-channel data
for (int i = 0; i < SAMPLES_PER_CHANNEL; i++) {
ch0_data[i] = adcBuffer[i * NUM_CHANNELS + 0];
ch1_data[i] = adcBuffer[i * NUM_CHANNELS + 1];
ch2_data[i] = adcBuffer[i * NUM_CHANNELS + 2];
}
Achieving Maximum Sample Rates
To maximise STM32 ADC sample rate:
- Set ADC clock prescaler to /2 (fastest: ADC at APB2/2)
- Minimise sampling time (3 ADC cycles minimum, at risk of accuracy)
- Use triple interleaved mode on STM32F4 for up to 6 MSPS combined
- Use SRAM for DMA buffer — avoid flash (read-only) or CCM SRAM (no DMA access on some F4 devices)
- On STM32F7/H7: use DTCM RAM carefully — DMA may not access it directly
Frequently Asked Questions
What is the maximum STM32F4 ADC sample rate with DMA?
Single channel: approximately 1.75 MSPS at minimum sample time. Triple interleaved mode: up to ~6 MSPS. For most sensor data logging, 100 kSPS single channel is more than sufficient.
Can DMA ADC run during FreeRTOS task execution?
Yes. DMA operates independently of the CPU. ADC DMA runs continuously in the background; FreeRTOS tasks are notified via callbacks. Use thread-safe flags or semaphores in callbacks instead of direct data manipulation.
Why is my ADC DMA giving incorrect readings at high speed?
Common causes: insufficient sampling time (increase from 3 to 15+ cycles), ADC source impedance too high (add buffer op-amp), or ADC clock too high (reduce to 21 MHz for stable operation).
Add comment