cancel
Showing results for 
Search instead for 
Did you mean: 

How to measure VBAT using ADC correctly in STM32L476 MCU

eson
Associate II

Hi everyone!
I'm having trouble in measuring the VBAT through ADC. I'm using 3.3V on aNUCLEO_476 board to connect to VBAT and when I measure VBAT through ADC,I cannot obtain the value of the VBAT

Here's my code:

void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  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.ContinuousConvMode = ENABLE;
  hadc1.Init.NbrOfConversion = 7;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_VREFINT;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_5;
  sConfig.Rank = ADC_REGULAR_RANK_4;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = ADC_REGULAR_RANK_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_13;
  sConfig.Rank = ADC_REGULAR_RANK_6;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_VBAT;
  sConfig.Rank = ADC_REGULAR_RANK_7;
  sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}​

 

/* USER CODE BEGIN PV */
uint16_t adc_data_buffer[7]; /* ADC常规转换数据 */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
  //NVIC_Redirect();
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_I2C2_Init();
  MX_I2C3_Init();
  MX_UART5_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  other_Init();

  // ADC Calibration
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_data_buffer, 7);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    ProcessADCValues(adc_data_buffer, 7);
    main_Task();
  }
  /* USER CODE END 3 */
}

 

void ProcessADCValues(volatile uint16_t *adcData, uint8_t length)
{
  for (uint8_t i = 0; i < length; i++)
  {
    // 处理每个通道的数据
    switch (i)
    {
    case 0:
      VREF_voltage = get_VREF(adcData[0]); // 
      break;
    case 1:
      ILIM_MonitorI = get_MP5087_ILIM(adcData[1]); // ADC channel 3, ilim_monitor
      break;
    case 2:
      ILIM_MonitorI2 = get_MP5087_ILIM(adcData[2]); // ADC channel 4, ilim_monitor1
      break;
    case 3:
      Tosa_temp1 = get_NBC103_Temp(adcData[3]); // ADC channel 5, TOSA_TEMP1
      break;
    case 4:
      Moudle_temp = get_TH05_Temp(adcData[4]); // ADC channel 7, module_temp
      break;
    case 5:
      Tosa_temp2 = get_NBC103_Temp(adcData[5]); // ADC channel 13, TOSA_TEMP2
      break;
    case 6:
      Vbat_voltage = get_Vbat(adcData[6], VREF_voltage);
      break;
    default:
      break;
    }
  }
}
 
#define VREFINT_ADDR ((uint16_t *)0x1FFF75AA) 

float get_VREF(uint16_t adc_value)
{
  uint16_t vrefint_cal = *VREFINT_ADDR;
  float vref_actual = (3.0f * vrefint_cal) / adc_value;
  return vref_actual;
}

float get_Vbat(uint16_t adc_value, float vref_voltage)
{
  float vbat = (adc_value * vref_voltage) / 4095.0f;
  return vbat;
}​
 
Can anyone please tell me where I'm doing it wrong?
 
 
1 ACCEPTED SOLUTION

Accepted Solutions

Hi.@Saket_Om

I've found the reason why I can't collect the VBAT.
I mistakenly configured the VREF pin as an external reference voltage mode.

After enabling ENVR and delaying for 100ms, the ADC can now sample normally.

void McuVref_Config(VREF_TypeDef Vref_Type)
{
  if (Vref_Type == VREF_INTERNAL)
  {
    /** Configure the internal voltage reference buffer voltage scale */
    LL_VREFBUF_SetVoltageScaling(LL_VREFBUF_VOLTAGE_SCALE1);

    /** Configure the internal voltage reference buffer high impedance mode	*/
    LL_VREFBUF_DisableHIZ();

    /** Enable the Internal Voltage Reference buffer	*/
    LL_VREFBUF_Enable();
  }

  if (Vref_Type == VREF_EXTERNAL)
  {
    /** Disable the Internal Voltage Reference buffer  */
    LL_VREFBUF_Disable();

    /** Configure the internal voltage reference buffer high impedance mode  */
    LL_VREFBUF_EnableHIZ();
  }
}

 

View solution in original post

8 REPLIES 8
waclawek.jan
Super User

What are the raw readings of VREFINT and VBAT, what is the *VREFINT_ADDR content, and what is the actual voltage measured at VDDA?

You may also read out and check/post content of ADC registers.

JW

Hi JW 

*VREFINT_ADDR value is 0x0681,

But there's a really weird thing: when I connect the VBAT pin on the NUECLEOL476 development board to the 3.3V on the board, all of the adc registers value is 0x0000,I don't konw why

企业微信截图_17568683837516.png

And I change some code in my Project:

 

#define VREFINT_CAL *((__IO uint16_t *)0x1FFF75AA) // VDDA = VREF+ =3.0V(± 10 mV)

float get_VREF(uint16_t adc_value)
{
  float Vdda = (3.0f * VREFINT_CAL) / adc_value;
  return Vdda;
}

float get_Vbat(uint16_t adc_value, float vref_voltage)
{
  float vbat = (vref_voltage / 4095.0f) * adc_value * 3.0f;
  return vbat;
}

eson

waclawek.jan
Super User

Isn't VBAT connected to some other circuitry on that board? (I haven't checked).

JW

eson_0-1756890861834.jpeg

yeah,I connect VBAT with the Onboard 3.3V,and following is mine ADC config

 

ce72b0ed34365827a4b05b1e19cca966.png

 

eson

Saket_Om
ST Employee

Hello @eson 

To check your board, can you try to run the example from STM32L4 FW package :
Projects\NUCLEO-L496ZG\Examples\ADC\ADC_Sequencer

  • The configuration is close to the one of your project:
    It performs 3 ADC conversions (VrefInt, GPIO, internal temperature sensor) with data transfer by DMA.
  • Could you try to run this example, you should get these kinds of data (assuming board setup not modified, with Vref+ pin = 3.3V):
    VrefInt raw data 1500(dec) corresponding to 1200mV
    Temp sensor raw data 920(dec) corresponding to 23degC
  • Then, could you try to replace one of 3 channels by Vbat: "sConfig.Channel = ADC_CHANNEL_VBAT;"
    You should get these kind of data :
    Vbat raw data 1362(dec) corresponding to 1362 * 3 * 3300 / 4095 = 3293mV
     Note : x3 due to Vbat voltage through a divider ladder of factor 1/3
  • About your code :
    In function "get_Vbat()", you have to multiply Vbat variable value by factor 3 (see example above).
    In function "get_VREF()", you can use directly ADC LL helper macro __LL_ADC_CALC_VREFANALOG_VOLTAGE().
    Except this, your code seems correct.
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

Hello Saket_Om

Thanks for your  suggestion,When I tried to use the program under Projects\NUCLEO-L496ZG\Examples\ADC\ADC_Sequencer, it got stuck in the HAL_Init() function. However, I carried out debugging using the program under Projects\NUCLEO-L476RG\Examples_LL\ADC\ADC_MultiChannelSingleConversion.

I have obtained the following data:

3cf7bca42d55df09d561b5931b83baf2.png

These data seem to have some issues.

I noticed that the VBAT and VDD on my board are connected together and I'm not sure if this affects the VBAT acquisition data.

eson

 

Hello @eson 


@eson wrote:

Hello Saket_Om

Thanks for your  suggestion,When I tried to use the program under Projects\NUCLEO-L496ZG\Examples\ADC\ADC_Sequencer, it got stuck in the HAL_Init() function.


What happen exactly in the function HAL_Init()? On which instruction it get stack?

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

Hi.@Saket_Om

I've found the reason why I can't collect the VBAT.
I mistakenly configured the VREF pin as an external reference voltage mode.

After enabling ENVR and delaying for 100ms, the ADC can now sample normally.

void McuVref_Config(VREF_TypeDef Vref_Type)
{
  if (Vref_Type == VREF_INTERNAL)
  {
    /** Configure the internal voltage reference buffer voltage scale */
    LL_VREFBUF_SetVoltageScaling(LL_VREFBUF_VOLTAGE_SCALE1);

    /** Configure the internal voltage reference buffer high impedance mode	*/
    LL_VREFBUF_DisableHIZ();

    /** Enable the Internal Voltage Reference buffer	*/
    LL_VREFBUF_Enable();
  }

  if (Vref_Type == VREF_EXTERNAL)
  {
    /** Disable the Internal Voltage Reference buffer  */
    LL_VREFBUF_Disable();

    /** Configure the internal voltage reference buffer high impedance mode  */
    LL_VREFBUF_EnableHIZ();
  }
}