cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 Eval board ADC accuracy

krausiul
Associate

Hello everyone,

I am currently trying to get the ADC on the STM32H745ZIQ to work like it should according to the AN *Getting started with the STM32H7 Series MCU 16-bit ADC - Application note.

On page 3 the spectrum is shown along with the measurement settings.

I tried to replicate that spectrum but I'm stuck.

To my measurement setup:

  • A function Generator is connected to the ADC Input (ADC2 CH18) in differential mode.
  • An Input sine wave (2 kHz) is sampled and 64000 data points are stored in a buffer and subsequently sent via USART to the PC where I process them with Matlab.
  • Because of the available Function Generator and the Number of samples, I chose to measure the 12-bit ADC instead of the 16-bit.
  • The ADC runs with an Asynchronous Clock (40 MHz with DIV2) and sampling time of 1.5 Cycles. According to a datasheet the ADC needs 6.5 cycles in 12-bit mode. This would result in a sampling frequency of 2.5 Msps
  • The ADC runs in Continuous conversion mode with the data stored via DMA

I would expect the SINAD to ideally to be around 12*6.02+1,76 = 74 dB.

I'm aware that this will obviously not be the case as the ENOB is always lower than the specified accuracy.

Nontheless I stagnate at around 60dB with some weird noise floor.

I also tried to reduce noise by using hardware oversampling but that seems to increase the phase noise, which is just as bad for my application.

The last few days I played around with different Settings, looked into issues like pin crosstalk, power supply noise, noisy input signals and low-pass filtered the input.

Below, I will post some code and the resulting SINAD.

Thank you in advance for any ideas that you have that might help me reach the accuracy that is promised on the DS.

0693W00000StyXvQAJ.png 

/* Includes ------------------------------------------------------------------*/
#include "main.h
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
 
#ifndef HSEM_ID_0
#define HSEM_ID_0 (0U) /* HW semaphore 0*/
#endif
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define ADC_LEN 64000
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc2;
DMA_HandleTypeDef hdma_adc2;
UART_HandleTypeDef huart3;
 
/* USER CODE BEGIN PV */
uint16_t adc_buf[ADC_LEN];
uint8_t rx = 0;
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART3_UART_Init(void);
static void MX_ADC2_Init(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 */
 
  /* USER CODE END 1 */
/* USER CODE BEGIN Boot_Mode_Sequence_0 */
  int32_t timeout;
/* USER CODE END Boot_Mode_Sequence_0 */
 
/* USER CODE BEGIN Boot_Mode_Sequence_1 */
  /* Wait until CPU2 boots and enters in stop mode or timeout*/
  timeout = 0xFFFF;
  while((__HAL_RCC_GET_FLAG(RCC_FLAG_D2CKRDY) != RESET) && (timeout-- > 0));
  if ( timeout < 0 )
  {
  Error_Handler();
  }
/* USER CODE END Boot_Mode_Sequence_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();
/* USER CODE BEGIN Boot_Mode_Sequence_2 */
/* When system initialization is finished, Cortex-M7 will release Cortex-M4 by means of
HSEM notification */
/*HW semaphore Clock enable*/
__HAL_RCC_HSEM_CLK_ENABLE();
/*Take HSEM */
HAL_HSEM_FastTake(HSEM_ID_0);
/*Release HSEM in order to notify the CPU2(CM4)*/
HAL_HSEM_Release(HSEM_ID_0,0);
/* wait until CPU2 wakes up from stop mode */
timeout = 0xFFFF;
while((__HAL_RCC_GET_FLAG(RCC_FLAG_D2CKRDY) == RESET) && (timeout-- > 0));
if ( timeout < 0 )
{
Error_Handler();
}
/* USER CODE END Boot_Mode_Sequence_2 */
 
  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART3_UART_Init();
  MX_ADC2_Init();
  /* USER CODE BEGIN 2 */
 
  HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET_LINEARITY, ADC_DIFFERENTIAL_ENDED);
  HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET, ADC_DIFFERENTIAL_ENDED);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  HAL_UART_Receive(&huart3, &rx, 1, 1000);
	  // check if UART received data -> Signal from PC to start Conversion + Transmission
	  if(rx != 0) {
		  rx=0;
		  HAL_Delay(250);
		  HAL_ADC_Start_DMA(&hadc2,(uint32_t *)adc_buf,ADC_LEN);
	  }
	  HAL_Delay(250);
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_DIRECT_SMPS_SUPPLY);
  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
 
  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
  /** Macro to configure the PLL clock source
  */
  __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSI);
  /** 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_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 9;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOMEDIUM;
  RCC_OscInitStruct.PLL.PLLFRACN = 3072;
  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_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}
 
static void MX_ADC2_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  /** Common config
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = ENABLE;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_ONESHOT;
  hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc2.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_18;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  sConfig.SingleDiff = ADC_DIFFERENTIAL_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  } 
}
 
/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
  /* DMA interrupt init */
  /* DMA1_Stream2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
}

4 REPLIES 4
AScha.3
Chief II

btw the diagram is in mHz , milli-herz , ok so ??? or should be : MHz , but then it goes up to 1 GHz, i would not trust any of this... 🙂

i would try

first: increase  sampling time of 1.5 Cycles to 6.5 or so;

next : test inputs to GND, no cables connected -> what abaut noise and spurs now ?

and 16 bit resolution of adc, you gain nothing by cutting away lower bits.

and maybe a FFT, that looks more realistic from frequ. range 🙂

If you feel a post has answered your question, please click "Accept as Solution".
MasterT
Senior III

>>>> Nontheless I stagnate at around 60dB with some weird noise floor.

I'd say, not bad. Check data sheet for your uCPU, ADC specification section. Usually, noise-error a few LSB, so counting to get 12-bits theoretical limits is a mistake. 60 dB roughly 10-bits, pretty good result for internal ADC. 100 MHz noise may comes from other peripheral like Ethernet, I had similar issue with nucleo-H743zi, where I had to cut power for Ethernet completely.

>>>>I also tried to reduce noise by using hardware oversampling but that seems to increase the phase noise, which is just as bad for my application.

Phase noise indicates for bad clock, what is a initial source for PLL - HSI? For low jitter crystal is a must. 

>>>The last few days I played around with different Settings, looked into issues like pin crosstalk, power supply noise, noisy input signals and low-pass filtered the input.

What about Reference voltage for ADC? Is it the same as V-analog? 

What kind of ADC driver OPA? Is it capable to interface SAR ADC - there are not many good quality OPA on a market, 74 dB at 1 MHz - is very tough requirements.

Thank you for the response.

>>>>100 MHz noise may comes from other peripheral like Ethernet, I had similar issue with nucleo-H743zi, where I had to cut power for Ethernet completely.

I have the same suspicion regarding that noise. I used the same measurement setup for other ADCs and/or channels on the board and every adc had this characteristic noise spike.

>>>>Phase noise indicates for bad clock, what is a initial source for PLL - HSI? For low jitter crystal is a must.

Yes, currently the PLL source is HSI. Do you recommend using HSE or LSE with a Crystal/Ceramic resonator instead?

>>>>What about Reference voltage for ADC? Is it the same as V-analog?

Yes, basically I power the board via USB. I understand that noise coming from the power supply directly influences the measurements because Vref is derived from it. Sadly however, I don't have access to a sufficiently noise-free DC power supply.

The measurement results for 16-bit from the AN also show a deviation from ideal SINAD of around 14dB. It could be that 60dB is just the limit for 12-bit for this eval board. Nontheless if any improvement is possible, i would like to try it still.

MasterT
Senior III

What kind of EVAL board you have? I can't see STM32H745ZIQ in the list

https://www.st.com/en/evaluation-tools/stm32-eval-boards.html#products

There is nucleo- https://www.st.com/en/evaluation-tools/nucleo-h745zi-q.html#cad-resources

On a web page, you can download CAD resources, circuits & BOM as well, it shows a crystall at HSE inputs. For Nucleo, there is also an option to get clean clock source out of STLink (8.33 MHz). Check ST web resources on this matter, a lot of discussion/ advises posted on this board.

My mistake, test freq. 2 kHz - not 1 MHz as I think, still question persist how SG - Input RC filter & driver build, please post connection diagram /circuits or at least picture of the test setup