# STM32F4: How to get data from multiple channels sampled from a single ADC?

So I have searched everywhere for a solution to this problem but I can find anything. I have three channels connected to ADC1 of my STM32F446ZE-nucleo and I want to sample the channels at a rate of 200 Hz each. I set up TIM2 and linked it as the interrupt for the ADC conversion, and this works fine for 1 channel. But I am struggling to get it to work for multiple channels since I don't know how to get the data from each specific channel within the HAL_ADC_ConvCpltCallback.

My setup looks like this:

/* ADC1 init function */
{

/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
{
_Error_Handler(__FILE__, __LINE__);
}

/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Rank = 1;
{
_Error_Handler(__FILE__, __LINE__);
}

/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Rank = 2;
{
_Error_Handler(__FILE__, __LINE__);
}

/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Rank = 3;
{
_Error_Handler(__FILE__, __LINE__);
}

}

/* TIM2 init function */
static void MX_TIM2_Init(void)
{

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;

htim2.Instance = TIM2;
htim2.Init.Prescaler = 41999;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}

}


I start my ADC and timer after receiving data on USART3 like this:

// start timer and ADC interrupt
HAL_TIM_Base_Start(&htim2);


This is where I am struggling to get the right data:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{

// get values

// populate buffer
transmitBuf[0] = WE_ADC_VAL >> 8; // upper 8 bits
transmitBuf[1] = WE_ADC_VAL & 0x00FF; // lower 8 bits

// send by interrupt mode
HAL_UART_Transmit_IT(&huart3, transmitBuf, 2);
}


I thought that hadc->NbrOfCurrentConversionRank should have info on which channel is being sampled, but it is constantly 0 (I thought it would go from 1 to 2 to 3). How would I go about to get the individual channel's data at each conversion complete interrupt? Any help would be appreciated :)

• Ask a specific question, you'll get better answers. – Voltage Spike Jul 29 '18 at 0:22

This should be a comment to ask you if you want my solution, which includes 3 ADCs x 3 Channels = 9 signals, by utilizing DMA peripheral and no HAL libraries. Since my rep is < 50, I cannot make that comment, so I will provide the code for the above :P

It utilizes a Master-Slave technique, with ADC1 set as the master and ADC2 and 3 as slaves. Interrupts occur based on TIM2 frequency, which can be modified.

Hope it helps :)

μC Board: STM32F429ZI-Discovery

IDE: μVision V5.13.0.0

TIM2 Enable

#define ADC_PER 4499

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_BaseStruct_ST.TIM_Prescaler = 0;
TIM_BaseStruct_ST.TIM_CounterMode = TIM_CounterMode_Up;
// 899 => 100kHz, 2249 => 40kHz, 4499 => 20kHz, 8999 => 10kHz
TIM_BaseStruct_ST.TIM_ClockDivision = TIM_CKD_DIV1;

TIM_TimeBaseInit(TIM2, &TIM_BaseStruct_ST);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);


#include "stm32f4xx_adc.h"


ADC set to Triple Mode, DMA Enable, No Prescaler

ADC_CommonInitTypeDef ADC_CommonInitStructure;



3 conversions for each ADC, hence 9 in total. Highest resolution of 12 bits, right data alignment and event trigger by TIM2 interrupts.

ADC_InitTypeDef ADC_InitStructure;

TIM_SelectOutputTrigger (TIM2, TIM_TRGOSource_Update);



Set each channel to the right pin, with 15 cycles for sampling the signal

ADC_RegularChannelConfig (ADC1, ADC_Channel_13, 1, ADC_SampleTime_15Cycles);



DMA Clock Enable.

Setup DMA data transport, base address, matrix size, half word data transfer and circular mode.

Enable Stream4 of DMA2.

#include "stm32f4xx_dma.h"
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

DMA_InitTypeDef DMA_InitStructure;

DMA_InitStructure.DMA_DIR     = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;

DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_BufferSize = 9;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

DMA_Init(DMA2_Stream4, &DMA_InitStructure)


Setup and enable interrupt event of DMA

NVIC_InitTypeDef ADCNVICConfig;

DMA_ITConfig(DMA2_Stream4, DMA_IT_TC, ENABLE);

DMA_Cmd(DMA2_Stream4, ENABLE);


ADC_DMACmd(ADC1, ENABLE);