Multi-channel ADC sampling using interrupts with STM32 and HAL library

ampheoampheo
2 min read

Here's an example of multi-channel ADC sampling using interrupts with STM32 and HAL library. We’ll sample:


Objective:

  • Use STM32 ADC in scan mode with 2 channels

  • Trigger ADC conversions periodically using a timer interrupt

  • Use interrupt-based ADC to process both values

  • Convert temperature and voltage readings


STM32CubeMX Configuration:

1. Enable ADC1

  • Resolution: 12-bit

  • Scan Conversion Mode: Enabled

  • Continuous Mode: Disabled

  • Discontinuous Mode: Disabled

  • External Trigger: Software Start

  • Number of conversions: 2

  • Enable Channels:

    • Channel 0 (PA0 for LM35)

    • Channel 1 (PA1 for voltage divider)

2. Enable Timer (e.g., TIM3)

  • Configure for update interrupt every 100 ms

  • Enable global interrupt

3. Enable ADC Interrupt in NVIC


User Code (HAL, No DMA)

Global Variables:

c

volatile uint16_t adc_raw[2];  // Stores both channel results
volatile uint8_t adc_index = 0;
volatile uint8_t adc_ready = 0;

Timer Interrupt: Start ADC Sequence

c

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3)
    {
        adc_index = 0;
        HAL_ADC_Start_IT(&hadc1);
    }
}

ADC Interrupt Handler:

Read one result at a time, manually restart for the next channel.

c

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        adc_raw[adc_index++] = HAL_ADC_GetValue(hadc);

        if (adc_index < 2)
        {
            HAL_ADC_Start_IT(&hadc1); // Start next channel
        }
        else
        {
            adc_ready = 1; // Both channels sampled
        }
    }
}

Process in Main Loop:

c

float convert_temp(uint16_t raw)
{
    float voltage = (raw / 4095.0f) * 3.3f;
    return voltage * 100.0f; // LM35: 10 mV/°C
}

float convert_voltage(uint16_t raw)
{
    float voltage = (raw / 4095.0f) * 3.3f;
    return voltage * 2.0f; // Adjust based on resistor divider
}

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

    HAL_TIM_Base_Start_IT(&htim3);

    while (1)
    {
        if (adc_ready)
        {
            adc_ready = 0;

            float temp = convert_temp(adc_raw[0]);
            float vbat = convert_voltage(adc_raw[1]);

            printf("Temperature: %.2f °C | Battery: %.2f V\r\n", temp, vbat);
        }

        // Optionally sleep here
        // __WFI();
    }
}

Summary

ChannelPurposePin
0LM35 TemperaturePA0
1Battery Voltage SensePA1
  • Timer triggers ADC conversion every 100 ms

  • ADC samples 2 channels using interrupts

  • Main loop processes results and prints them

0
Subscribe to my newsletter

Read articles from ampheo directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

ampheo
ampheo