cancel
Showing results for 
Search instead for 
Did you mean: 

stm32l072 multi channel adc correct value reading problem

moksan
Associate

I am trying to read multiple ADC channels using the Stm32l072 microcontroller. As we know VREFINT_CAL is the VREFINT calibration value.but , I noticed that this value is not in the reference manual.(There is no memory adress.Is it a problem ?)

I'm current using 3 adc channel.I have shared the relevant codes and microcontroller adc settings in cubemx below.

ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;

/* 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_DIV2;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.SamplingTime = ADC_SAMPLETIME_160CYCLES_5;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ContinuousConvMode = ENABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.DMAContinuousRequests = ENABLE;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerFrequencyMode = DISABLE;
  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_9;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_VREFINT;
  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_GPIOB_CLK_ENABLE();
    /**ADC GPIO Configuration
    PB1     ------> ADC_IN9
    */
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC Init */
    hdma_adc.Instance = DMA1_Channel1;
    hdma_adc.Init.Request = DMA_REQUEST_0;
    hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc.Init.Mode = DMA_CIRCULAR;
    hdma_adc.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc);

  /* 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
    PB1     ------> ADC_IN9
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_1);

    /* ADC1 DMA DeInit */
    HAL_DMA_DeInit(adcHandle->DMA_Handle);
  /* USER CODE BEGIN ADC1_MspDeInit 1 */

  /* USER CODE END ADC1_MspDeInit 1 */
  }
}

#define CAL_VREF_ADDR                     // ??? There is no information
#define VREFINT_CAL_VOLTAGE 3.3f          // Nominal referans voltage
float Vdda;

uint16_t VREFINT_VALUE;
float vrefint_voltage;
uint32_t adc_value[3];
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_ADC_Init();
HAL_ADC_Start_DMA(&hadc, &adc_value[0], 3);
while (1)
  {
    VREFINT_VALUE = *CAL_VREF_ADDR;
    vrefint_voltage = ((float)(VREFINT_VALUE )/ (float)adc_value[1]) * 3.3;
    Vdda = ((float)adc_value[0]/4095 * 1.85126582278481) * (vrefint_voltage);
  }

 When we physically measure the vrefint voltage, we see 3.26 volts on the vrefint pin, while we see the vrefint_voltage value as 3.4551 in the software. When we try the same software on the STM32F407 discovery board without changing the codes, we see that the board, which is fed with 3.3 volts, is 3.33.. in the software. The reason I share this is that there is no error in the software. However, we cannot determine what is causing this situation.

3 REPLIES 3
TDK
Guru

VREFINT is an internal voltage. It is not exposed anywhere so I'm not sure how you could be measuring it. In any case, it should be around 1.2 V, but there is no "VREFINT" pin.

TDK_1-1722336441941.png

 

VREFINT_CAL is a calibration value taken of the VREFINT channel during production. The address this is stored is in the datasheet.

TDK_0-1722336376405.png

 

If you feel a post has answered your question, please click "Accept as Solution".

moksan_0-1722868193667.pngmoksan_1-1722868203598.png

The difference between the two images shows the result obtained after taking the vrefint_cal value from the register or giving the appropriate reference value. While we should see 3.09V as a result of the vrefint_cal value coming from the register, we see 3.29V as seen in the image. If the reference value is 1585, there is less than %1 difference between the value shown by the battery and the value we measure.

Considering this situation, is it the right step to change the vref_cal value manually rather than in the register?

TDK
Guru

The math doesn't make a whole lot of sense. Use the VBAT channel to estimate VBAT, but you have to know your VREF+ voltage to do this.

You can use VREFINT to estimate what VREF+ is. Is that what you are trying to do? If you already know VREF+, there is no need to use this channel for anything except to sanity check the voltage, which should be around 1.2 V.

If you feel a post has answered your question, please click "Accept as Solution".