cancel
Showing results for 
Search instead for 
Did you mean: 

Wrong ADC values. Why?

Haddock
Associate III

Target: STM32L031C6T6

editor: SMT32CubeIDE using HAL

I getting crazy soon. I cannot get this ADC to read correct. I think I missing something important. I googled and googled again and again.

I have two voltage on two separate channels (7 & 8). 6.9V and 14V. Of course, it is led to the pin via a voltage divider. Voltage is around 2.5V messured at the pin.

The function ADC_Read(channel, samples) returns the voltage in mV and I get 6800 for the first one and the 11608 mV for the second. There is the problem. No matter what I'm setting the voltage to (8V or 14.5V) i get around 11600 mV. I can see in the array that it change a few bits here and there but every value has wrong "level" leading to wrong mean. Do I mesure wrong channel?

My code just calls ADC_Init() in the beginning (where CubeIDE place it) and then I do the calls to ADC_Read further below. Forgot, I added a call to calibration in the end of ADC_Init.

I have no more code that I add. STM32CubeIDE adds ADC Init. I don't add no "Clock start" or something else.

The code is below. Please please have a look. Give me a good monday! What you see in ADC_Read is a "lab-code" and written so I can single-step it.

/*
 * adc.c
 *
 *  Created on: 25 Jan 2023
 */
 
#include "adc.h"
#include "main.h"
#include "stm32l0xx_hal_adc.h"
#include <stdint.h>
 
const ADC_ChannelConfTypeDef sConfigs[] = {
  { ADC_CHANNEL_VREFINT,    ADC_RANK_CHANNEL_NUMBER       }, // 1 Internal
  { ADC_CHANNEL_TEMPSENSOR, ADC_RANK_CHANNEL_NUMBER       }, // 2 Internal
  { ADC_CHANNEL_1,          ADC_RANK_CHANNEL_NUMBER       },
  { ADC_CHANNEL_2,          ADC_RANK_CHANNEL_NUMBER       },
  { ADC_CHANNEL_3,          ADC_RANK_CHANNEL_NUMBER       },
  { ADC_CHANNEL_4,          ADC_RANK_CHANNEL_NUMBER       },
  { ADC_CHANNEL_7,          ADC_RANK_CHANNEL_NUMBER       }, //CH_12VSNS
  { ADC_CHANNEL_8,          ADC_RANK_CHANNEL_NUMBER       }  //CH_6V9SNS
};
 
 
const uint16_t ADC_factors[] = { 100,100,196,196,196,196,399,224 };  //12 bitars
 
//************************************************************************************************
uint32_t adc_values[20];
 
uint16_t ADC_Read(uint32_t ch, uint8_t samples)
{
 
	uint8_t right, i;
	uint32_t summa = 0;
 
	if(samples > 20) samples = 20;
 
 
	for(i=0;i<20;i++)
		adc_values[i]=0;
 
    ADC_ChannelConfTypeDef sConfig = {0};
 
    sConfig.Channel = sConfigs[ch].Channel;
    sConfig.Rank = sConfigs[ch].Rank;
 
 
	if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
	{
		_NOP; // ADC channel configuration error
		return(0xFFFF);
	} //End if
 
 
 
	right = 0;
 
	for (i = 0; i < samples; i++)
	{
		if(HAL_ADC_Start(&hadc) == HAL_OK)
		{
			if(HAL_ADC_PollForConversion(&hadc, 1000) == HAL_OK)
			{
				short_delay(50);
				adc_values[i] = HAL_ADC_GetValue(&hadc);
				right = right + 1;
			} //End if
 
		} //End if
	} //End for
	HAL_ADC_Stop(&hadc); //??
 
 
	 summa = 0;
	    for (i = 0; i < samples; i++) 
	            summa += adc_values[i]; 
    
 
	    if(right == samples) 
	    {
	        summa = (summa / (uint32_t)samples) * (uint32_t)ADC_factors[ch] / (uint32_t)100;
	    }  //End if
	    else 
	    {
	        summa = (uint16_t)0xFFFF;
	    } //End else
 
	    short_delay(35);  //35 us
 
	    return (uint16_t)summa;  //mV
}  //End func
 
//*************** ADC Init ****************************
 
static 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_ASYNC_DIV4;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.SamplingTime = ADC_SAMPLETIME_3CYCLES_5;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ContinuousConvMode = DISABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.DMAContinuousRequests = DISABLE;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerFrequencyMode = DISABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    Error_Handler();
  }
#define XXXXx
#ifdef XXXXx
  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_1;
  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_2;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_3;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_4;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_7;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_8;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
#endif
  /* USER CODE BEGIN ADC_Init 2 */
  HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
  /* USER CODE END ADC_Init 2 */
 
}

3 REPLIES 3
mckenney
Senior

By using a voltage divider it's very easy to introduce a high source impedance, which prevents the sampling capacitor from fully charging.

> hadc.Init.SamplingTime = ADC_SAMPLETIME_3CYCLES_5;

3.5 cycles is pretty short. Data sheet [DS10668 rev 6] Table 58 gives the maximum source impedance for different sampling times -- for 3.5 cycles (at 16MHz) it looks to be about 200 Ohms.

As a quick experiment, try setting SamplingTime to something very large and see if your symptom changes.

Haddock
Associate III

Thanx for answer. I have eariler used higher sampl.time just for test and it did not help. Same now. Did not help. (I haven't read Table 58. Thanx for letting me know.)

I do not need to use any setup regarding Vref+ or something? I just accept CubeIDEs ADC_Init and then I put together the ADC_Read and start using it.

Why I have a feeling that I read wrong channel is that my code in ADC_Read are exact the same as all others examples. 1) config ch, 2) start, 3) polling, 4) read. 5) start 6) polling read start polling read.

What I have noticed is that I need a short delay (200us) after polling before I read or else ADC_Start fails. Is that a clue?

The MCU using 24 MHz, btw.

I made the ADC_Read without HAL. Now it is working. I cannot spend more time on this. Must move on.