Author Topic: STM32G0 ADC low value  (Read 8033 times)

0 Members and 1 Guest are viewing this topic.

Offline VekettiTopic starter

  • Regular Contributor
  • *
  • Posts: 188
  • Country: fi
STM32G0 ADC low value
« on: February 06, 2024, 05:11:17 pm »
Dear All,

I added to my CO meter battery voltage measurement when the device is turned on. So single shot ADC measurement. However the measured value is too small and it drifts more, higher the voltage. Eg. real value of 2.495V -> ADC show 0.195V lower value. Real value 2.797V -> ADC show 0.227V lower value. I measured voltages from the MCU pin which reads the ADC. VREF+, VBAT and VDDA are all connected to same rail having 3.333V. I quess this ADC cannot be so inaccurate, so I must once again be doing something wrong? MCU is STM32G070CBT6. I could of course add 0.2 for the value to get close and forget it, but I'd like to understand where's my mistake, so hopefully someone could help to point it out?

Here's the read function:
Code: [Select]
void ReadBatteryVoltage(void)
{   
    float BatVoltage = 0.0;
    char VoltageChar[30];
    uint16_t adc_val = 0;

    if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK) // Calibrate the ADC
    {
      Error_Handler();
    }
    HAL_Delay(100);
    if (HAL_ADC_Start(&hadc1)!= HAL_OK)
    {
      Error_Handler();
    }
    if (HAL_ADC_PollForConversion(&hadc1, 100) != HAL_OK)
    {
      Error_Handler();
    }
    adc_val = HAL_ADC_GetValue(&hadc1); // get the adc value
    HAL_ADC_Stop(&hadc1); // stop adc. I'm not sure is this needed though as it is singe shot?
    BatVoltage = adc_val / 4096.0f * 3.3f;


And here's the initialisation:
Code: [Select]
/* ADC1 init function */
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_1CYCLE_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_4;
  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 */

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */
    /* ADC1 clock enable */
    __HAL_RCC_ADC_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC1 GPIO Configuration
    PA4     ------> ADC1_IN4
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }
}

void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{

  if(adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspDeInit 0 */

  /* USER CODE END ADC1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_ADC_CLK_DISABLE();

    /**ADC1 GPIO Configuration
    PA4     ------> ADC1_IN4
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4);

  /* USER CODE BEGIN ADC1_MspDeInit 1 */

  /* USER CODE END ADC1_MspDeInit 1 */
  }
}

Thank you.
« Last Edit: February 06, 2024, 05:13:14 pm by Veketti »
 

Online wek

  • Frequent Contributor
  • **
  • Posts: 536
  • Country: sk
Re: STM32G0 ADC low value
« Reply #1 on: February 07, 2024, 12:50:30 pm »
What's the impedance of input voltage?

>   hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_1CYCLE_5;

This is probably too short. Try longer.

JW
 
The following users thanked this post: Veketti

Online tszaboo

  • Super Contributor
  • ***
  • Posts: 7949
  • Country: nl
  • Current job: ATEX product design
Re: STM32G0 ADC low value
« Reply #2 on: February 07, 2024, 01:06:00 pm »
Source impedance for example as mentioned. Place a scope on the input pin, you might see a dip or peak (value and architecture dependent) when it's sampling. If you place a 100nF or so capacitor on the ADC pin it might go away.
 
The following users thanked this post: Veketti

Offline VekettiTopic starter

  • Regular Contributor
  • *
  • Posts: 188
  • Country: fi
Re: STM32G0 ADC low value
« Reply #3 on: February 07, 2024, 04:36:33 pm »
That's it. I had 10k series resistor between the battery and adc input, so it had dip when the input charged. I changed the SamplingTimeCommon1 to ADC_SAMPLETIME_79CYCLES_5 and now the value is spot on!

Thank you so much for help and also that link that explains the behaviour.
 

Offline harerod

  • Frequent Contributor
  • **
  • Posts: 471
  • Country: de
  • ee - digital & analog
    • My services:
Re: STM32G0 ADC low value
« Reply #4 on: February 09, 2024, 08:37:04 pm »
Have a look at the reference manual to understand the internal structure of the ADC. During sampling a switch will connect an internal sampling capacitor to the signal source. That internal capacitor must be charged to the signal voltage within the required accuracy during the sampling time. If you add a small buffer capacitor which is much larger than the sampling capacitor, you provide sufficient charge.
Not so important for DC signals, yet usually a requirement for A-to-D conversion, is the avoidance of signal frequencies around half the sampling frequency and above (see Nyquist frequency). An RC combination with adequate time constant does the trick for many applications.
So adding a buffer capacitor to your resistor will provide two important functions.
 
The following users thanked this post: Veketti

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: es
Re: STM32G0 ADC low value
« Reply #5 on: February 09, 2024, 10:21:01 pm »
Haha classic one!
The ADC has a sampling capacitor in the order of  8-10pF,  it's charged for the specific sampling time, then the pin is internally disconnected and the conversion begins.
This is called sample-and-hold technique.

If the sampling is too fast, the cap doesn't fully charge and you get that problem.
Try different values, once you see no difference between two settings you might use the latter one or even slightly larger to provide some margin.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Veketti

Offline VekettiTopic starter

  • Regular Contributor
  • *
  • Posts: 188
  • Country: fi
Re: STM32G0 ADC low value
« Reply #6 on: February 10, 2024, 10:04:01 am »
Opened new thread, so please disregard this post.
« Last Edit: February 10, 2024, 02:43:28 pm by Veketti »
 

Online wek

  • Frequent Contributor
  • **
  • Posts: 536
  • Country: sk
Re: STM32G0 ADC low value
« Reply #7 on: February 10, 2024, 12:53:04 pm »
You should really start a new thread with new questions.

JW
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf