Skip to main content
LZale.1
Associate
May 19, 2026
Question

Trouble with ADC on STM32G071G8U6 using HAL from STM32CubeMX

  • May 19, 2026
  • 1 reply
  • 167 views

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...

1 reply

Technical Moderator
May 19, 2026

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

"To give better visibility on the answered topics, please click on ""Accept as Solution"" on the reply which solved your issue or answered your question.Saket_Om"
LZale.1
LZale.1Author
Associate
May 19, 2026

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?

LZale.1
LZale.1Author
Associate
May 19, 2026

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