H7 ADC revisited (Bandpass sampling and analog bandwidth of the 16-bit ADC peripheral of the STM32H7, Part IV)

The question that interested me was:

How strongly does the SNR of the ADC-peripheral of the H7 depend on the chosen clock source?

Unfortunately, neither the H7 datasheets nor application notes like AN5354 treat this topic. The FFT plot shown in figure 1 of AN5354 looks astonishingly good. Unfortunately, ST does not publish the code and the measurement setup that would make it possible to reproduce this measurement.

The whole topic of clocking the ADC is missing in this AN, so I had to make my own measurements.

According to the manual and to the clock configurator of STM32CubeIDE/CubeMX, one can choose between 3 different clock sources for the ADC:
1. PLL2P
2. PLL3R

I was especially interested in using PER_CK, because the PER source MUX allows the choice between HSI, CSI and HSE as the clock source of this signal. I was interested in using HSE, which is the high-speed external clock signal that can be fed in to the PH0 pin of the MCU.

I modified the NUCLEO-H743ZI2 board in order to use an external clock oscillator as described in the previous blog entry. For this I used a 40 MHz SMD quartz oscillator (ABRACON ASAAIG-40.000MHZ-K-C-S-T) with a RMS phase jitter of < 1 ps (in a BW of 12 kHz…20MHz).

The idea was to drive the ADC by a low-noise clock that can go directly to the ADC without the detour via a PLL oscillator.

PLL oscillators can be sources of high phase noise unless they are designed especially for low-noise operation.

The following FFT plots show measurements at different signal frequencies with a signal power of 13 dBm and a balun transfomer like the one described in one of the older blog entries.

In order to choose between PLL2P, PLL3R and PER_CK, I used the graphical clock configurator of STM32CubeIDE/CubeMX.

For PPL2P, I used purely integer dividers, i.e. DIVM2=3, DIVN2=12 and DIVP2=4 and FRACN2=0, so that the output frequency of PLL2P would be the same as the input frequency, namely 40 MHz.

For PLL3R I decided to switch on fractional divider FRACN3 in order to see if it makes any difference to use a PLL with fractional dividers. This, of course, means to choose a slightly different PLL output frequency. With a setting of DIVM3=32, DIVN3=127, DIVP3=2 and FRACN3=8100 the ADC clock frequency should be 39.99649 MHz. The deviation of this clock frequency should be high enough in order to be seen at least at the higher input signal frequencies, but low enough in order to disturb the plots of the lower frequencies.

The following crops from the clock tree for the PLL3R version should illustrate this:


The generated code for the different clock selections can be found in the file stm32h7xx_hal_msp.c. The following table shows the different code lines that are generated for the different clock sources:

ADC clocked by HSEADC clocked by PLL2PADC clocked PLL2R, fractional mode
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)

/** Initializes the peripherals clock */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_CLKP;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)

/** Initializes the peripherals clock */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL2.PLL2M = 3;
PeriphClkInitStruct.PLL2.PLL2N = 12;
PeriphClkInitStruct.PLL2.PLL2P = 4;
PeriphClkInitStruct.PLL2.PLL2Q = 2;
PeriphClkInitStruct.PLL2.PLL2R = 2;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0.0;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)

/** Initializes the peripherals clock*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL3.PLL3M = 32;
PeriphClkInitStruct.PLL3.PLL3N = 127;
PeriphClkInitStruct.PLL3.PLL3P = 2;
PeriphClkInitStruct.PLL3.PLL3Q = 2;
PeriphClkInitStruct.PLL3.PLL3R = 4;
PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_0;
PeriphClkInitStruct.PLL3.PLL3FRACN = 8100.0;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL3;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)

In order to write the FFT data into Excel files, I modified the python script of https://github.com/papamidas/H7ADCBW
and added the following few lines:

import pandas as pd
        if keyboard.is_pressed("s"):
           filename = input("save to filename:")
           print("saving data to file --->", filename)
           specHeader = ["frequency","amplitude"]
           specDataArr = np.stack((freqs.T, YdB.T), axis=-1)
           dfspec = pd.DataFrame(specDataArr, columns=specHeader)
           timeHeader = ["ADCval"]
           dftime = pd.DataFrame(adc_frame, columns=timeHeader)
           with pd.ExcelWriter(filename + '.xlsx') as writer:  
               dfspec.to_excel(writer, sheet_name='Spectrum')
               dftime.to_excel(writer, sheet_name='ADCFrame')

Input Frequency 0.5 MHz:

The ADC spectra for HSE and PPL2P are almost the undistinguishable. The spectrum of the ADC clocked by the fractional PLL2R is about 20 dB noisier than the others

Input Frequency 1.5 MHz:

Already at 1.5 MHz the higher phase noise of PLL2P vs. HSE becomes apparent

Input Frequency 2.5 MHz:

At 2.5 MHz one can see slight asymmetries in the HSE spectrum. Why?

Input Frequency 20.5 MHz:

At 20.5 MHz the HSE-clocked spectrum shows clear asymmetries. One can now even see an asymmetric bump close to the peak on the right side. I have no explanation for this.

Input Frequency 100.5 MHz:

At 100.5 MHz the small deviation of the clock frequency of PLL3R from 40 MHz becomes apparent (watch the frequency shift of yellow curve). One can also see that the noisy PLL3R spreads so much spectral energy over the whole range of frequencies that the energy of the central peak is significantly reduced in comparison to HSE and PLL2P

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert