2024-02-06 11:15 AM
Hi everyone,
I have a STM32F042F6P6 chip. I want to read 9 analog channels on it.
I guess there are at least 2 options: configure a scan mode with DMA, and staying in polling mode.
I decided to stay in polling mode to avoid a potential problem due to DMA config.
I defined 1 adc handle and did channel configurations (8 first channels are gpios, the 9th is internal temp)
It seems that when calling those 2 methods repeatedly, the ADC auto-increments and read each channel starting from the lowest ID.
Let's say now that I want to have a method that reads only the internal temperature, how do I manage to tell to HAL_ADC_PollConversion to point on the internal temperature channel only ?
I have had an idea but it is quite radical:
- create other channel config structure, with only the Temp channel defined, and each time I call a conversion method, before that I call HAL_ADC_ConfigChannel with the good channel config structure.
Also, I have 2 questions:
- What is the rank of an ADC channel, and what is it used for ? I see sometimes people defining several channel but with the same rank, I don't understand why.
- Does someone have a breve explanation of multichannel conversion in DMA mode ? I do not find clear information for newbie in embedded systems like me.
Thank you very much,
Arthur
Solved! Go to Solution.
2024-02-06 12:09 PM
HAL_ADC_PollConversion always returns the last conversion. You need to manage internally what channel that is. Have an internal variable that increments by 1 for each conversion. Use that to know what channel is being returned.
Doing multi-channel without DMA isn't recommended as you can easily run into overruns.
On the F0, rank and channel number are the same thing. Rank 0 is channel 0, etc. On other families rank determines which order channels are converted.
See "13.5.5 Managing converted data using the DMA" in the reference manual for an explanation of DMA. Also should be examples you can follow in the CubeMX repository.
2024-02-06 12:09 PM
HAL_ADC_PollConversion always returns the last conversion. You need to manage internally what channel that is. Have an internal variable that increments by 1 for each conversion. Use that to know what channel is being returned.
Doing multi-channel without DMA isn't recommended as you can easily run into overruns.
On the F0, rank and channel number are the same thing. Rank 0 is channel 0, etc. On other families rank determines which order channels are converted.
See "13.5.5 Managing converted data using the DMA" in the reference manual for an explanation of DMA. Also should be examples you can follow in the CubeMX repository.
2024-07-29 4:26 PM
@TDK wrote: Doing multi-channel without DMA isn't recommended as you can easily run into overruns.
What do you mean by overruns in this case? Do you mean that the calling code will not call HAL_ADC_PollForConversion() and HAL_ADC_GetValue() fast enough, and the sample will get overwritten or corrupted?
2024-07-29 6:10 PM
Effectively, yes. Particularly when the conversions are continuously generated and it's just a question of calling HAL_ADC_GetValue fast enough.
The various STM32 families have different ADC peripherals. Most of them do not "wait" for you to read the value before converting the next channel.
2025-09-17 6:33 AM
It is really a shame there is no function to simply read from specified channel regardless it was overwritten or not.
2025-09-18 1:15 AM
There is no data register for each channel, so there is only IRQ or DMA method available for multi-channel configuration. I've mixed it with Microchips ADC design.
2025-09-19 7:58 AM
@trzeci wrote:It is really a shame there is no function to simply read from specified channel regardless it was overwritten or not.
I wrote a function like that. It isn't light nor fast. Nevertheless, there are situations where it's convenient.
/**
* @brief Selects the ADC channel and makes one polled reading. The intended use is housekeeping measurements, such as battery voltage.
* @PAram[in] handle of the ADC
* @PAram[in] ADC channel. Regular channel.
* @retval The ADC reading
* @pre ADC isn't running
* @post ADC is stopped
*/
uint32_t ADC_ReadChannel(ADC_HandleTypeDef* hadc, const uint32_t channel)
{
// make sure that the ADC is stopped before reinitializing it with new parameters
HAL_ADC_Stop(&hadc2);
HAL_ADC_Stop_DMA(&hadc2);
/* <note> Not calling HAL_ADC_DeInit(...) . I noticed that if I call it, the ADC gets an offset (82 counts).
Maybe I need to call HAL_ADCEx_Calibration_Start(...) after deinitialization and reinitialization.
Maybe I can call HAL_ADC_Init(...) without having to deinitialize first and recalibrate afterwards. </note> */
/* Configure the ADC for general purpose single measurements.
Assuming that Assume that the previous ADC configuration was completely different. It could ave been scan mode with DMA. */
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.GainCompensation = 0;
hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE; // single channel (polling probably), not a scan
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc2.Init.LowPowerAutoWait = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; // overwrite old sample
hadc2.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc2) != HAL_OK) Error_Handler();
/* <note> Working assumption: I can call HAL_ADC_Init(...) without having to call HAL_ADC_DeInit(...) first.
That way I wouldn't have to redo calibration with HAL_ADCEx_Calibration_Start(...) . </note> */
// Configure Regular Channel
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(hadc, &sConfig) != HAL_OK) Error_Handler();
HAL_ADC_Start(&hadc2);
if (HAL_ADC_PollForConversion(&hadc2, 1) != HAL_OK) Error_Handler();
uint32_t ret = HAL_ADC_GetValue(&hadc2);
HAL_ADC_Stop(&hadc2);
return ret;
}
2025-09-19 8:18 AM
I mixed ADC with other architecture where exists separate data register for each channel.
There is only IRQ or DMA method without these registers when you need:
- high efficiency (CPU is heavily loaded)
- don't need to be interrupted for every sample
I used DMA method, but don't know how to switch off callbacks from DMA after every ADC scan.
It consumes CPU only.
Precisely - I need callbacks from other 3 high speed channels on ADC1 & ADC2, but do not need callbacks from ADC3.