cancel
Showing results for 
Search instead for 
Did you mean: 

ADC problems

DCarr.1
Senior

Hello all.

I have a custom stm32439VITx board running at 60MHz clocked off the HSI:

void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
 /** Configure the main internal regulator output voltage
 */
 
 __HAL_RCC_PWR_CLK_ENABLE();
 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
 
 /** 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 = 8;
 RCC_OscInitStruct.PLL.PLLN = 64;
 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
 RCC_OscInitStruct.PLL.PLLQ = 4;
 
 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_DIV2;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
 {
  Error_Handler();
 }
}
 

with a signal driven by an amplifier into a 1K/1M resistor divider feeding into ADC1 channel 10 which I need to read every ten seconds or so:

void MX_ADC1_Init(void)
{
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV6;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_10;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
  /* USER CODE END ADC1_Init 2 */
}

I've tried just about everything I can think of to get this to work, and I've got the code down to a simple test case:

static void test_adc() {
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_ADC1_Init();
 
	HAL_ADC_Start(&hadc1);
 
	while(true) {
		HAL_Delay(1000);
 
		if(HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) {
			static const float Vref = 3.3f;
			static const float R14 = 1000.0f;
			static const float R15 = 1000000.0f;
 
			const float adc = HAL_ADC_GetValue(&hadc1);
			const float vadc = adc * Vref / 4096.0f;
			const float vin = vadc * (R14 + R15) / R15;
 
			__NOP();
		}
	}
 
	HAL_ADC_Stop(&hadc1);
}

A voltmeter on the input pin is telling me 1200mV and the adc is reading between 890 and 910, which is an ajusted reading of around 720mV.

Like I say, I've tried all sorts of things to get a sensible reading, maybe there's something simple I'm missing. Is there anything obviously wrong with my code?

Thankyou

David

1 ACCEPTED SOLUTION

Accepted Solutions
Peter BENSCH
ST Employee

It is not a software problem, but primarily a hardware one. The measurement cannot yield any meaningful values with your measurement setup, at least not the expected ones. With this ADC, too, you have to consider the input parameters.

Let's summarise:

  • you feed a voltage divider with an amplifier
  • the voltage divider has a divider factor of 1/1000 = 0.001 and a GND resistance of 1Mohms
  • according to the datasheet, the ADC has a max (!) external input impedance of 50kohms (table 74, pg 159)
  • the actual input impedance can be estimated with equation 1 (below table 74, page 160)

What happens to the voltage divider with this constellation?

Correct, the voltage collapses.

Solution: use the amplifier to apply the voltage to be measured directly to the input as long as it is below the maximum value, i.e. remove R15 and substitue R14 by a short.

Typically, an opamp in front of the ADC is used precisely for this purpose and also for anti-aliasing.

Regards

/Peter

In order 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.

View solution in original post

10 REPLIES 10
MM..1
Chief III

You try is maybe miss something. Is signal static DC 1200mV or Vrms 1200mV dynamic signal?

Your sampling time is very low try bigger

sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

For dynamic signals you need ADC with buffer with amount of values and calculate ...

I thought the sampling frequency might be off, but the signal is actually mesauring a battery; just about as stable DC as you could hope for without actually putting it through a regulator.

 I changed sConfig.SamplingTime to ADC_SAMPLETIME_480CYCLES and, if anything, the measurements are actually worse, I'm now getting around 632 from the ADC for a corrected voltage reading of around 509 mV

TDK
Guru

Ground the pin, does ADC give you 0 (or close)?

Tie pin to 3.3V, does ADC give 4095 (or close)?

Try measuring VREFINT as a sanity check.

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

It is not a software problem, but primarily a hardware one. The measurement cannot yield any meaningful values with your measurement setup, at least not the expected ones. With this ADC, too, you have to consider the input parameters.

Let's summarise:

  • you feed a voltage divider with an amplifier
  • the voltage divider has a divider factor of 1/1000 = 0.001 and a GND resistance of 1Mohms
  • according to the datasheet, the ADC has a max (!) external input impedance of 50kohms (table 74, pg 159)
  • the actual input impedance can be estimated with equation 1 (below table 74, page 160)

What happens to the voltage divider with this constellation?

Correct, the voltage collapses.

Solution: use the amplifier to apply the voltage to be measured directly to the input as long as it is below the maximum value, i.e. remove R15 and substitue R14 by a short.

Typically, an opamp in front of the ADC is used precisely for this purpose and also for anti-aliasing.

Regards

/Peter

In order 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.

Thanks Peter. I've pretty much reached the conclusion that this is some sort of electrical problem.

The precise configuration has the resistor divider on the input to the amplifier which is configured with a gain of one. Do you your comments apply to this setup?

I forgot to mention in my original post that I have Vref supplied from an external 3.3v precision reference. Could this be relevant?

<edit>Accidentally inserted data removed</edit>

First of all: please do not answer by email, even though this is unfortunately still offered as an option. This will flood the thread with your email history, as well as your private data, which will then be visible to everyone worldwide.

I had obviously not interpreted your question correctly, which once again shows that a drawing with the part of the circuit in question says more than many words.

If you have the resistor divider at the input of a voltage follower, which then directly drives the input of the ADC, things look different. Nevertheless, the input impedance of the voltage follower must be taken into account, even if it is usually very high. Also e.g. the output impedance, because it is connected to the input.resistance Rain.

We are then back to the point that @TDK​ has already touched upon and which amounts to the examination of all influencing factors as they are also listed in AN2834 - How to get the best ADC accuracy in STM32 microcontrollers:

  • Checking the measurement range
  • Checking the reference source and its connection to VREF+
  • Layout
  • ADC settings
  • etc

Regards

/Peter

In order 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.

Yes, I apologise for the email response; I hadn't realised that it was going to copy in the entire thread.

I'm going to follow your and TDK's suggestions and try to get down to the lab today and make as many measurements as possible.

I've been through AN2834 as well as just about any on-line tutorial I can find trying to pick up clues, but the values I'm getting are so far off that I've convinced myself that there must be something fundamental that I've overlooked rather than a subtelty concerning sampling frequency or board layout.

Following your excellent advice, I just got back from the lab, and after a bit of head-scratching realised two things

The amplifier wasn't turned on so I was just reading noise

The external reference was at 2400mV not 3300mV so I was scaling incorrectly

So everything works works fine now. Thanks a lot.

Following your excellent advice, I just got back from the lab, and after a bit of head-scratching realised two things

The amplifier wasn't turned on so I was just reading noise

The external reference was at 2400mV not 3300mV so I was scaling incorrectly

So everything works works fine now. Thanks a lot.