cancel
Showing results for 
Search instead for 
Did you mean: 

ADC problem with VBat and Temperature measurement

AAlat.1
Associate II

MCU: STM32WLE5CC

IDE: Stm32CubeIDE 1.9.0

I have generated a project using MX and thought I'd try the generated function for reading the temperature SYS_GetTemperatureLevel().

It seemed to output rubbish so I started digging.

SYS_GetTemperatureLevel() calls:

ADC_ReadChannels(ADC_CHANNEL_TEMPSENSOR);

I didnt really understand the macros in the SYS_GetTemperatureLevel() so I wrote an own function, but it doesnt really matter as the error is visible immitiately in the ADC value without calculations.

At room-temperature the result from the ADC_ReadChannels(ADC_CHANNEL_TEMPSENSOR); should be close to the calibration value at 30C, but it isnt.

I Wrote the following test-code to see the intermediate calculations more clearly.

I noted that I started getting sane values (see comments in code) when single-stepping the code and after a while I isolated it to that a 2-3 seconds (!!!) delay between

ADC_Enable() and LL_ADC_REG_StartConversion()

Seemed to give much saner values (see comments below).

It is like the buffer between temperature sensor and ADC is extremely slow (or not working).

VBAT readings have same issue.

VRef reading seem to be fine though, so the ADC it self seems to be working...

What could be going on here? I'm at a total loss :(

int32_t SYS_GetTemperatureLevel2()
{
	int32_t ADC_ref_3v3 = *VREFINT_CAL_ADDR;  //1517
 
	int32_t ADC_ref_raw  = ADC_ReadChannels(ADC_CHANNEL_VREFINT);  //1528, as expected close to ADC_ref_3v3 as we run on 3.3V
 
	// 314 is way off, should be close to the 30C calibration point at 962
	// 944 is read with the 3 seconds delay, which is a sane value corresponding to 22C
	int32_t ADC_T_raw    = ADC_ReadChannels(ADC_CHANNEL_TEMPSENSOR); //314 without delay, 944 with delay
 
	// The temperature reading as if it had been taken at 3v3, which is the unit needed by the temp-sensor calibration.
	int32_t ADC_T = ADC_T_raw * ADC_ref_3v3 / ADC_ref_raw;  //311
 
	//Adc values @ 3v3 vref
	int32_t ADC_T0 = *TEMPSENSOR_CAL1_ADDR;   // 962
	int32_t ADC_T1 = *TEMPSENSOR_CAL2_ADDR;   // 1271
 
	int32_t T0 = TEMPSENSOR_CAL1_TEMP;  // 30
	int32_t T1 = TEMPSENSOR_CAL2_TEMP;  // 130
 
	// T-t0 = K * (ADC_T - ADC_T0)
	// 1000 * 100 * 4000  (28 bitar som värst, dvs inget overflow möjligt)
	int32_t T = (T1 - T0) * (ADC_T - ADC_T0) / (ADC_T1 - ADC_T0)  + T0;    // -180 without delay, 22 with delay
 
	return T;
}

static uint32_t ADC_ReadChannels(uint32_t channel)
{
  /* USER CODE BEGIN ADC_ReadChannels_1 */
 
  /* USER CODE END ADC_ReadChannels_1 */
  uint32_t ADCxConvertedValues = 0;
  ADC_ChannelConfTypeDef sConfig = {0};
 
  MX_ADC_Init();
 
  /* Start Calibration */
  if (HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)
  {
    Error_Handler();
  }
 
  /* Configure Regular Channel */
  sConfig.Channel = channel;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  if (HAL_ADC_Start(&hadc) != HAL_OK)
  {
    /* Start Error */
    Error_Handler();
  }
  /** Wait for end of conversion */
  HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
 
  /** Wait for end of conversion */
  HAL_ADC_Stop(&hadc);   /* it calls also ADC_Disable() */
 
  ADCxConvertedValues = HAL_ADC_GetValue(&hadc);
 
  HAL_ADC_DeInit(&hadc);
 
  return ADCxConvertedValues;
  /* USER CODE BEGIN ADC_ReadChannels_2 */
 
  /* USER CODE END ADC_ReadChannels_2 */
}

HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
{
  HAL_StatusTypeDef tmp_hal_status;
 
  /* Check the parameters */
  assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
 
  /* Perform ADC enable and conversion start if no conversion is on going */
  if (LL_ADC_REG_IsConversionOngoing(hadc->Instance) == 0UL)
  {
    __HAL_LOCK(hadc);
 
    /* Enable the ADC peripheral */
    tmp_hal_status = ADC_Enable(hadc);
 
    /* Start conversion if ADC is effectively enabled */
    if (tmp_hal_status == HAL_OK)
    {
      /* Set ADC state                                                        */
      /* - Clear state bitfield related to regular group conversion results   */
      /* - Set state bitfield related to regular operation                    */
      ADC_STATE_CLR_SET(hadc->State,
                        HAL_ADC_STATE_READY | HAL_ADC_STATE_REG_EOC | HAL_ADC_STATE_REG_OVR | HAL_ADC_STATE_REG_EOSMP,
                        HAL_ADC_STATE_REG_BUSY);
 
      /* Set ADC error code */
      /* Reset all ADC error code fields */
      ADC_CLEAR_ERRORCODE(hadc);
 
      /* Clear ADC group regular conversion flag and overrun flag               */
      /* (To ensure of no unknown state from potential previous ADC operations) */
      __HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_EOC | ADC_FLAG_EOS | ADC_FLAG_OVR));
 
      /* Process unlocked */
      /* Unlock before starting ADC conversions: in case of potential         */
      /* interruption, to let the process to ADC IRQ Handler.                 */
      __HAL_UNLOCK(hadc);
 
      /* Enable conversion of regular group.                                  */
      /* If software start has been selected, conversion starts immediately.  */
      /* If external trigger has been selected, conversion will start at next */
      /* trigger event.                                                       */
      /* Start ADC group regular conversion */
      LL_ADC_REG_StartConversion(hadc->Instance);
    }
    else
    {
      __HAL_UNLOCK(hadc);
    }
  }
  else
  {
    tmp_hal_status = HAL_BUSY;
  }
 
  return tmp_hal_status;
}

Finally in ADC_Enable I note that if I extend the delay-loop to last for seconds, I start getting reasonable results, but running at full speed results are way to low.

HAL_StatusTypeDef ADC_Enable(ADC_HandleTypeDef *hadc)
{
  uint32_t tickstart;
  __IO uint32_t wait_loop_index = 0UL;
 
  /* ADC enable and wait for ADC ready (in case of ADC is disabled or         */
  /* enabling phase not yet completed: flag ADC ready not yet set).           */
  /* Timeout implemented to not be stuck if ADC cannot be enabled (possible   */
  /* causes: ADC clock not running, ...).                                     */
  if (LL_ADC_IsEnabled(hadc->Instance) == 0UL)
  {
    /* Check if conditions to enable the ADC are fulfilled */
    if ((hadc->Instance->CR & (ADC_CR_ADCAL | ADC_CR_ADSTP | ADC_CR_ADSTART | ADC_CR_ADDIS | ADC_CR_ADEN)) != 0UL)
    {
      /* Update ADC state machine to error */
      SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);
 
      /* Set ADC error code to ADC peripheral internal error */
      SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);
 
      return HAL_ERROR;
    }
 
    /* Enable the ADC peripheral */
    LL_ADC_Enable(hadc->Instance);
 
    if ((LL_ADC_GetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(hadc->Instance)) & LL_ADC_PATH_INTERNAL_TEMPSENSOR) != 0UL)
    {
      /* Delay for temperature sensor buffer stabilization time */
      /* Wait loop initialization and execution */
      /* Note: Variable divided by 2 to compensate partially              */
      /*       CPU processing cycles, scaling in us split to not          */
      /*       exceed 32 bits register capacity and handle low frequency. */
 
//**** INSERING A BREAKPOINT HERE OR MODIFYING THE COUNTER 
//**** TO CAUSE A DELAY OF A FEW SECONDS GIVES BETTER RESULTS
 
      wait_loop_index = ((LL_ADC_DELAY_TEMPSENSOR_BUFFER_STAB_US / 10UL) * ((SystemCoreClock / (100000UL * 2UL)) + 1UL));
      while (wait_loop_index != 0UL)
      {
        wait_loop_index--;
      }
    }
 
    /* If low power mode AutoPowerOff is enabled, power-on/off phases are     */
    /* performed automatically by hardware and flag ADC ready is not set.     */
    if (hadc->Init.LowPowerAutoPowerOff != ENABLE)
    {
      /* Wait for ADC effectively enabled */
      tickstart = HAL_GetTick();
 
      while (__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_RDY) == 0UL)
      {
        /*  If ADEN bit is set less than 4 ADC clock cycles after the ADCAL bit
            has been cleared (after a calibration), ADEN bit is reset by the
            calibration logic.
            The workaround is to continue setting ADEN until ADRDY is becomes 1.
            Additionally, ADC_ENABLE_TIMEOUT is defined to encompass this
            4 ADC clock cycle duration */
        /* Note: Test of ADC enabled required due to hardware constraint to     */
        /*       not enable ADC if already enabled.                             */
        if (LL_ADC_IsEnabled(hadc->Instance) == 0UL)
        {
          LL_ADC_Enable(hadc->Instance);
        }
 
        if ((HAL_GetTick() - tickstart) > ADC_ENABLE_TIMEOUT)
        {
          /* New check to avoid false timeout detection in case of preemption */
          if (__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_RDY) == 0UL)
          {
            /* Update ADC state machine to error */
            SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);
 
            /* Set ADC error code to ADC peripheral internal error */
            SET_BIT(hadc->ErrorCode, HAL_ADC_ERROR_INTERNAL);
 
            return HAL_ERROR;
          }
        }
      }
    }
  }

1 ACCEPTED SOLUTION

Accepted Solutions
S.Ma
Principal

In my case it started to return making sense value when the sample and hold time was long enough. And temperature calibrated values to be compensated to real vdda measured value. Chip dependent. Some have 2 pointd, some have one with fixed slope.

View solution in original post

2 REPLIES 2
S.Ma
Principal

In my case it started to return making sense value when the sample and hold time was long enough. And temperature calibrated values to be compensated to real vdda measured value. Chip dependent. Some have 2 pointd, some have one with fixed slope.

AAlat.1
Associate II

Thank you so very much!

I had completely missed the samle time requirements on Vbat (12us) and Temp (5us).

Following modifications gives me 20us @ 32MHz and it works like a charm!

hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_160CYCLES_5;
  hadc.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_160CYCLES_5;

Best Regards

Axel