2026-05-18 6:15 PM - last edited on 2026-05-19 2:19 AM by mƎALLEm
Hello all,
I've been having trouble with the ADC system on the STM32G071G8U6. I'm using the STM32CubeMX to generate initialization code. I have 5 external A/D inputs and it appears I am not getting readings anywhere near the real values, so I have switched to trying to just read the internal reference. I hope that if I can get the internal reference or the temperature to read correctly, I'll be able to solve the problems with the external inputs.
My VDD is provided from a 2.5 volt LDO. Looking at it with a DVOM, that looks accurate.
I am running the A/D at 12 bit resolution and my clcok prescaler is Synchronous clock mode divided by 2. I have the sampling time set to the maximum available in Cube, 160.5 cycles.
I have a polling routine running every 1 second that reads all 5 external and all 3 internal channels.
I set the VREFEN and TSEN bits in the ADC CCR register at the end of init and leave them enabled.
I realize there may be board related problems with the external inputs. However, even when I just try to read the internal inputs, the internal reference or the temperature, I see 0, 1, or 2 A/D counts.
My periodic readings look like this, with some expected noise in the readings. The value on the left is a timestamp, followed by the channel name, followed by the channel value in square braces, followed by the raw A/D value:
[ 1.007] ADC polling:
[ 1.014] Internal Temp Raw ADC[B0001000] =0
[ 1.016] Internal Vref Raw ADC[B4002000] =2
[ 1.017] Internal Vbat Raw ADC[B8004000] =1983
I would expect the internal temperature channel and the internal Vref channel to be the easiest to debug. I assume there's some step I am missing...
The Cube generated ADC1 init routine is as follows:
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** 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_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.LowPowerAutoPowerOff = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_160CYCLES_5;
hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
After all HAL initializations are completed, I run my ADC init routine:
void ADC_Init(void)
{
SET_BIT(ADC->CCR, ADC_CCR_VREFEN);
SET_BIT(ADC->CCR, ADC_CCR_TSEN);
HAL_ADCEx_Calibration_Start(&hadc1);
last_polling_tick = HAL_GetTick();
}
And then my actual readings are taken with this code:
uint32_t ADC_ReadChannel (uint32_t channel)
{
ADC_ChannelConfTypeDef sConfig = {0};
HAL_StatusTypeDef status;
uint32_t raw_adc = 4096;
// Configure the desired channel
sConfig.Channel = channel;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1; // Adjust as needed
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// Do the reading
status = HAL_ADC_Start(&hadc1);
if (status == HAL_OK)
{
status = HAL_ADC_PollForConversion(&hadc1, ADC_TIMEOUT_mS);
}
if (status == HAL_OK)
{
raw_adc = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);
PRINT_APP_DBG_NO_TIME(" Raw ADC[%08lX] =%i\r", channel, raw_adc);
return (raw_adc);
}
I can't see what I'm missing that's causing the ADC_CHANNEL_VREFINT and ADC_CHANNEL_TEMPSENSOR values to always be near 0...
Solved! Go to Solution.
2026-05-26 7:25 PM - edited 2026-05-26 7:25 PM
Use DMA to convert multiple ADC channels as shown in the examples. Otherwise, you have a race condition where you must read DR before the next conversion comes it. That is why it is failing. It can be done without DMA but HAL is not designed for it.
2026-05-19 7:20 AM
Hello @LZale.1
Please find attached the IOC file for the ADC configuration used to measure the ADC internal reference voltage.
For more details, please refer to the article below.
How to use the STM32 ADC's internal reference volt... - STMicroelectronics Community
2026-05-19 12:28 PM
Good afternoon,
(Sorry for the slow response - I needed to update CubeMX from version 6.15 to 6.17 and had some difficulties...)
Thanks, your example helped a lot. I migrated it to the STM32G071G8U6 variant and switched the external input to ADC input #7 (which is called "VBATT" on my board) and used code based on the "How to use" article.
That code is able to read the two channels and I can see that the reference reading looks correct based on the stored calibration data after adjusting for my VDD (2.5 volts) vs the calibration VDD (3.0 volts).
Then it gets odd...
I added the rest of the external inputs I need to read, and those readings report a HAL timeout when I try to read them. I removed the other external inputs from CubeMX, so I was back to just the internal reference and my ADC input #7. Both readings are readable.
I added the internal temperature as a third reading and it also times out.
I re-ordered the inputs in CubeMX so it was Internal ref, internal temp, and then input #7. With this configuration, internal ref and internal temp read, but input #7 times out. It appears that I can read 2 A/D inputs and whatever is the third will time out.
My ADC_TIMEOUT_mS, which is passed to HAL_ADC_PollForConversion, is set to 100 mS.
The read routine, which is called each second, is as follows:
static void ADC_UpdateLastReadings (void)
{
HAL_StatusTypeDef status;
uint32_t raw_adc = ADC_FULL_RANGE_COUNTS+1;
int channel;
// Do the readings
status = HAL_ADC_Start(&hadc1);
for (channel = 0; channel < NUM_ADC_CHANNELS; channel++)
{
raw_adc = ADC_FULL_RANGE_COUNTS+1;
if (status == HAL_OK)
{
status = HAL_ADC_PollForConversion(&hadc1, ADC_TIMEOUT_mS);
}
else
{
PRINT_APP_DBG(" Error waiting for conversion on channel %i: %i\r", channel, status);
break;
}
if (status == HAL_OK)
{
raw_adc = HAL_ADC_GetValue(&hadc1);
}
else
{
PRINT_APP_DBG(" Error reading conversion on channel %i: %i\r", channel, status);
break;
}
last_reading[channel] = raw_adc;
PRINT_APP_DBG(" Raw ADC[%i] =%4li (%s)\r", channel, raw_adc, CHANNEL_NAME[channel]);
}
HAL_ADC_Stop(&hadc1);
}The Cube generated ADC init routine is as follows:
/**
* @brief ADC1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** 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_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.LowPowerAutoPowerOff = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 3;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_160CYCLES_5;
hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
Does this processor variant only support reading 2 inputs?
2026-05-19 12:30 PM
The instrumentation above generates the following:
[ 15.008] ADC polling:
[ 15.008] Raw ADC[0] =1980 (InternalVref)
[ 15.009] Raw ADC[1] = 16 (VBatt)
[ 15.111] Error reading conversion on channel 2: 3
[ 15.112] New VDD = 2.512121
[ 16.008] ADC polling:
[ 16.008] Raw ADC[0] =1981 (InternalVref)
[ 16.009] Raw ADC[1] = 16 (VBatt)
[ 16.111] Error reading conversion on channel 2: 3
[ 16.112] New VDD = 2.510853
[ 17.008] ADC polling:
[ 17.008] Raw ADC[0] =1981 (InternalVref)
[ 17.009] Raw ADC[1] = 16 (VBatt)
[ 17.111] Error reading conversion on channel 2: 3
[ 17.112] New VDD = 2.510853
2026-05-20 7:14 AM
Hello @LZale.1
Please refer to the ADC_MultiChannelSingleConversion example, which reads ADC values from three channels, including ADC_CHANNEL_VREFINT and ADC_CHANNEL_TEMPSENSOR.
2026-05-26 12:41 PM
I reviewed that example, but there are two possibly significant differences
2026-05-26 12:43 PM
My odd workaround (mentioned on 5/19) still doesn't make sense... I configure for 3 conversions and only read the first two. The third always times out. So, I can make it work - sort of - but at some point I'm going to have to explain in a code review why it's handled that way.
2026-05-26 7:25 PM - edited 2026-05-26 7:25 PM
Use DMA to convert multiple ADC channels as shown in the examples. Otherwise, you have a race condition where you must read DR before the next conversion comes it. That is why it is failing. It can be done without DMA but HAL is not designed for it.
We’re moving the ST Community to a new platform to give you a better and more reliable community experience.