Multi-Channel ADC Data Acquisition Using DMA on STM32

Configure the ADC to sample across 5 channels and enable DMA in circular mode.

Buffer Definition and Initialization

#define CHANNEL_COUNT         5
#define SAMPLES_PER_CHANNEL   100

static uint16_t raw_adc_buffer[CHAMPLES_PER_CHANNEL * CHANNEL_COUNT];
/**
 * @brief Initialize ADC calibration and start DMA transfer
 */
void ADC_Startup(void)
{
    HAL_ADCEx_Calibration_Start(&hadc1);
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)raw_adc_buffer, 
                      SAMPLES_PER_CHANNEL * CHANNEL_COUNT);
}

Data Storage Pattern

When HAL_ADC_Start_DMA transfers data in u32 units to a u16 buffer, consecutive samples are paired:

u32 Index Upper 16-bit Lower 16-bit
0 Channel 0 Channel 1
1 Channel 2 Channel 3
2 Channel 4 Channel 0
3 Channel 1 Channel 2
4 Channel 3 Channel 4

For each channel, samples appear at indices 0, 5, 10, 15, etc. within the buffer. This arrangement allows straightforward access for averaging and filtering operations.

Data Processing

static volatile uint8_t data_ready_flag = 0;

static uint16_t channel_readings[CHANNEL_COUNT] = {0};

/**
 * @brief Extract and filter ADC values for each channel
 */
static void Process_ADC_Data(void)
{
    if (data_ready_flag == 1)
    {
        data_ready_flag = 0;

        for (uint8_t ch = 0; ch < CHANNEL_COUNT; ch++)
        {
            uint32_t sum = 0;
            uint16_t max_val = 0;
            uint16_t min_val = 0xFFFF;

            for (uint16_t idx = 0; idx < SAMPLES_PER_CHANNEL; idx++)
            {
                uint16_t sample = raw_adc_buffer[CHANNEL_COUNT * idx + ch];
                sum += sample;

                if (sample > max_val)
                    max_val = sample;
                if (sample < min_val)
                    min_val = sample;
            }

            channel_readings[ch] = (sum - max_val - min_val) / (SAMPLES_PER_CHANNEL - 2);
        }
    }
}

/**
 * @brief DMA transfer complete callback
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    data_ready_flag = 1;
}

The filtering algorithm removes maximum and minimum values before calculating the average, which effectively elimniates outliers from the dataset. Call Process_ADC_Data() in the main loop to retrieve filtered ADC values from channel_readings[0] through channel_readings[4].

Usage Example

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();

    ADC_Startup();

    while (1)
    {
        Process_ADC_Data();

        // Access filtered values
        for (uint8_t i = 0; i < CHANNEL_COUNT; i++)
        {
            // Use channel_readings[i]
        }

        HAL_Delay(10);
    }
}

Tags: STM32 HAL ADC DMA embedded

Posted on Tue, 23 Jun 2026 17:54:34 +0000 by Magestic