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


Here's an example of multi-channel ADC sampling using interrupts with STM32 and HAL library. We’ll sample:
Channel 1: LM35 temperature sensor (e.g., on
PA0
)Channel 2: Battery voltage monitor (e.g., via a resistor divider on
PA1
)
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
Channel | Purpose | Pin |
0 | LM35 Temperature | PA0 |
1 | Battery Voltage Sense | PA1 |
Timer triggers ADC conversion every 100 ms
ADC samples 2 channels using interrupts
Main loop processes results and prints them
Subscribe to my newsletter
Read articles from ampheo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
