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 */
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_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_ADC_Start_IT(&hadc1);
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
WE_ADC_VAL = HAL_ADC_GetValue(hadc);
uint32_t nrConv = hadc->NbrOfCurrentConversionRank;
// 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 🙂
Best Answer
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
ADC Clock Enable
ADC set to Triple Mode, DMA Enable, No Prescaler
3 conversions for each ADC, hence 9 in total. Highest resolution of 12 bits, right data alignment and event trigger by TIM2 interrupts.
Set each channel to the right pin, with 15 cycles for sampling the signal
DMA Clock Enable.
Setup DMA data transport, base address, matrix size, half word data transfer and circular mode.
Enable Stream4 of DMA2.
Setup and enable interrupt event of DMA
Setup ADC1 as Master and ADC2 and ADC3 as Slaves, enable DMA and all ADCs.