0
\$\begingroup\$

I have been testing the ADC on my STM32L0 with 12-bit resolution and for some reason, every measurement I take has a 0.048 V offset. I've tried a different supply voltage (from 1.8 V to 3.3 V), but the offset stays there.

This offset starts at the first count, when I apply 0 V to the ADC input and read 130 counts (with 1.8 V Vdda) it stays for the whole range.

My voltage source is very stable and I've verified my ADC input voltage and the STM32 supply voltage with 3 different accurate pieces of measuring equipment.

The board I'm using is a custom PCB, but I only placed the STM32 and some passive components to get it to function (coupling capacitors and reset pull-up).

I also tested multiple boards with different configurations and I always get this offset voltage. I outputed Vrefint and measured it, and I got 1.225 V (specified is 1.224 V) so the ADC reference seems fine too.

I was wondering if someone has a suggestion on the possible cause of this offset error? I've been looking for a solution but I'm having no luck so far, so I would really appreciate the input of more experienced developers.

Here is a simplified version of the code I use :

    /* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    adc.c
  * @brief   This file provides code for the configuration
  *          of the ADC instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "adc.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

ADC_HandleTypeDef hadc;

/* ADC init function */
void MX_ADC_Init(void)
{

  /* USER CODE BEGIN ADC_Init 0 */

  /* USER CODE END ADC_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC_Init 1 */

  /* USER CODE END ADC_Init 1 */

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc.Instance = ADC1;
  hadc.Init.OversamplingMode = DISABLE;
  hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ContinuousConvMode = DISABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.DMAContinuousRequests = DISABLE;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerFrequencyMode = ENABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC_Init 2 */

  /* USER CODE END ADC_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_ADC1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC GPIO Configuration
    PA0-CK_IN     ------> ADC_IN0
    */
    GPIO_InitStruct.Pin = SENSOR_ANALOG_IN_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(SENSOR_ANALOG_IN_GPIO_Port, &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_ADC1_CLK_DISABLE();

    /**ADC GPIO Configuration
    PA0-CK_IN     ------> ADC_IN0
    */

  /* USER CODE BEGIN ADC1_MspDeInit 1 */

  /* USER CODE END ADC1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

And here is the main file where I call the function to read the adc and send it to uart to be read :


#include "main.h"

#include "gpio.h"
#include "adc.h"
#include "usart.h"

#include <stdlib.h>

extern "C" 

void execute(){
   
   HAL_Delay(10);
   HAL_ADC_Start(&hadc);
   HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
   const auto value = HAL_ADC_GetValue(&hadc);
   char buffer [5];
   itoa(value, buffer, 10);
   HAL_UART_Transmit(&huart2, reinterpret_cast<uint8_t*>(buffer), 5, HAL_MAX_DELAY);
}

Here is the schematic, as I said, the test circuit only had the mcu and some passive components :

\$\endgroup\$
7
  • \$\begingroup\$ How do you apply the measured voltage? From where it comes from? What is the source impedance? Do they have a common reference, with no offset? How do you init the pins and ADC, do you do ADC calibration and how you configure and read the channel? Please show the schematics and connections along with source code. \$\endgroup\$
    – Justme
    Commented Aug 3, 2022 at 23:10
  • \$\begingroup\$ The voltage is applied to the circuit through a bnc connector connected to a signal generator, in my case I use the Dc output option on a normal signal generator. For the source impedance, its either 50 ohm or highZ but ill have to check in the morning since i dont have access to the equipement right now. As for the code, I used stm32cubemx to generate some of the code but I didn't use any calibration function, so unless it is enabled by default, I dont do any calibration. As for the adc pin, I just used cubemx to set-up IN0 as an adc input and didn't touch anything else. \$\endgroup\$
    – GbNusko
    Commented Aug 4, 2022 at 3:03
  • \$\begingroup\$ I will post more details tommorow (schematics and source code), I dont have access to it right now, but thank you for taking time out of your day to help me find the issue. \$\endgroup\$
    – GbNusko
    Commented Aug 4, 2022 at 3:08
  • \$\begingroup\$ What happens with the ADC input grounded? \$\endgroup\$
    – bobflux
    Commented Aug 4, 2022 at 13:50
  • \$\begingroup\$ When I ground the ADC input with Vdda = 1.8V, I read 130 count, or 0.048V. \$\endgroup\$
    – GbNusko
    Commented Aug 4, 2022 at 14:01

1 Answer 1

1
\$\begingroup\$

I found out what the issue was, my adc wasn't initialized and that was the cause of my offset, to solve the issue, I simply added this code in initialize() :

    void initialize()
{
    HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
    HAL_Delay(10);
}

My adc precision now is about 3 count (1.2mV) which is really good.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.