cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L496RGT6 ADC

DCarr.1
Senior

I'm having serious problems with the ADC

I have the voltage I'm reading going through a 47K/100K voltage divider which feeds into an op-amp configured with a gain of 1 and then to ADC1Ch15 on PB0. VSSA is supplied from an external precision voltage reference. The ADC clock prescaler is configured as "asynchronous clock mode divided by 8"

The STM32L496RGT6 is clocked from HSI through the PLL at 64MHz.

I've set all this up in STM32CubeIDE and left everything else at default (except to change  sConfig.SamplingTime to ADC_SAMPLETIME_47CYCLES_5) in adc.c and my main (with comments stripped) looks like this:

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_ADC1_Init();
 
  if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK) {
	  Error_Handler();
  }
 
  while (1) {
		if (HAL_ADC_Start(&hadc1) != HAL_OK) {
		  Error_Handler();
		}
 
		if (HAL_ADC_PollForConversion(&hadc1, 100) != HAL_OK) {
			Error_Handler();
		}
 
	    float adc = (float)HAL_ADC_GetValue(&hadc1);
 
	    float vref = 2500.0f;
	    float vadc = (adc * vref) / 4096.0f;
 
	    float R14 = 47000;
	    float R15 = 100000;
	    float vbat = vadc * (R14 + R15) / R15;
 
	    __NOP();
  }
}

I've checked the voltages at VSSA and the output of the opamp and with the opamp at around 1.393V I'm consistently reading 1.366V. As the voltage varies, the difference is neither a constant offset nor a linear gain.

Am I missing something, or is this the best precision I can hope for?

Thankyou

David

SystemClock_Config:

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 1;
  RCC_OscInitStruct.PLL.PLLN = 8;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}

generated adc.c

#include "adc.h"
 
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
ADC_HandleTypeDef hadc1;
 
/* ADC1 init function */
void MX_ADC1_Init(void)
{
 
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};
 
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV8;
  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.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.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
 
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }
 
  sConfig.Channel = ADC_CHANNEL_15;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}
 
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(adcHandle->Instance==ADC1)
  {
 
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_SYSCLK;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_RCC_ADC_CLK_ENABLE();
 
    __HAL_RCC_GPIOB_CLK_ENABLE();
 
    GPIO_InitStruct.Pin = Vbat_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(Vbat_GPIO_Port, &GPIO_InitStruct);
  }
}
 
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
  if(adcHandle->Instance==ADC1)
  {
    __HAL_RCC_ADC_CLK_DISABLE();
 
    HAL_GPIO_DeInit(Vbat_GPIO_Port, Vbat_Pin);
  }
}

5 REPLIES 5
S.Ma
Principal

Make sure the gpio is analog input mode, and if the pin also support DAC, it is off. Make sure no sw programmable pull up/down is present. Make sure to calibrate the adc before using it. ADC measure Vref to double check Vdda.

The GPIO mode is set in the generated HAL_ADC_MspInit function:

    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
    GPIO_InitStruct.Pull = GPIO_NOPULL;

The pin does not support DAC, and in any case there's no initialisation code other than the absolute minimum to get the ADC running, certainly no other peripherals.

I calibrate the ADC in main.c, just after the call to MX_ADC_INIT().

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_ADC1_Init();
 
  if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK) {
	  Error_Handler();
  }
 

I just tried reading VREF like this:

main.c

		if (HAL_ADC_Start(&hadc1) != HAL_OK) {
		  Error_Handler();
		}
 
		if (HAL_ADC_PollForConversion(&hadc1, 1000) != HAL_OK) {
			Error_Handler();
		}
 
	    float adc = (float)HAL_ADC_GetValue(&hadc1);
	    float vrefint = (float)(*VREFINT_CAL_ADDR);
	    float vdda = VREFINT_CAL_VREF * vrefint / adc;

adc.c

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

And I'm getting between 2495 and 2497 mV.

Piranha
Chief II

> VSSA is supplied from an external precision voltage reference.

> I've checked the voltages at VSSA

I guess you mean VDDA.

The difference of 27 mV is huge. It seems like the calibration has not really be done - debug it thoroughly. Also it could be a problem with different signal ground levels or just wrong measurement also because of wrong ground reference.

It seems huge to me too although I've single stepped through all the initialisation code and it all seems to be working. I've also gone over the board with the oscilloscope and voltmeter and I certainly can't find any noise or unexpected voltage differences.

Without the call to HAL_ADCEx_Calibration_Start() the errors are bigger, so although it might not be working properly it certainly seems to be doing something.

Right now I'm just hoping that somebody will be able to point out some glaring error in the code (although realistically, it's almost all autogenerated). The real major unknown for me at this point is the ADC clock setup, but I've gone through a load of different settings and they all seem to give pretty much the same error.

Piranha
Chief II
  1. Set the sampling time to maximum.
  2. Measure the voltage at VDDA/VREF+ and PB0 pins relatively to VSSA/VREF- pin, not some other "ground".
  3. Check the signals and supply lines with an oscilloscope.