2025-03-18 5:40 AM
Dear ST experts,
we're currently struggling with getting acceptable ADC performance with some of our samples of the STM32H753VIT ADC2. Our setup includes:
While more than half of our parts work more or less as advertised, but with some parts we reproducibly fail to generate decent calibration values; regardless of whether we use the factory-load linear calibration values (by using
HAL_ADCEx_LinearCalibration_FactorLoad(&hadc2);
) or doing a self-calibration with
HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET_LINEARITY, ADC_SINGLE_ENDED);
For testing, we use to apply a slow (1Hz) ramp signal to ADC2 channel3 while the ADC is running, buffer a large number of results in memory, stop the ADC when the buffer is filled, and finally read out the buffer via UART.
From the recorded ADC values, we cut out one ramp, and subtract the linear trend line. With the "bad" STM32H752 samples, we always get rather big jumps in the error curve, like this one:
Notably, the jumps seem to occur at multiples of 0x2000. So, we concluded that the calibration for the bit weights does not work properly with these examples.
Our main application is to accurately sinusodial signals around 0x8000, which gets ugly whenever the amplitude is small (because we then work in a small interval around the main dicontoninuity at 0x8000)
When investigating the issue, we have read out both the factory calibration value, and the linear calibration values resulting from self-calibrating, and noticed that all 10-bit-parts of the 160-bit linearity calibration factor lay around 0x200 (+/- roughly 0x1B). We conjectured that 0x200 may be a "neutral" value, and manually set all 10-bit-parts to 0x200:
uint32_t lincalbuf[ADC_LINEAR_CALIB_REG_COUNT] = {
0x20080200u,
0x20080200u,
0x20080200u,
0x20080200u,
0x20080200u,
0x00000200u,
};
HAL_ADCEx_LinearCalibration_SetValue(&hadc2, lincalbuf);
A bit surprisingly, this gave us much a more "continuous" error curve, with all our parts ("good" and "bad"), e.g.
Thus we have a few questions:
i) Can someone tell us what is exactly the meaning of the 10-bit parts of the linear calibration factor, preferably with some formulae? By experimenting, we concluded, that the lower eight 10-bit parts represent the bit weights of the upper 8 ADC bits. Is this correct?
ii) Did we miss something when calibrating the ADC, which may explain the bad behaviour with some parts?
iii) Are there some risks when we use our "manual" calibration values (16 times 0x200) that we may not be aware of?
Best Regards,
Lukas Rauber
2025-03-18 10:10 AM
Very unlikely internal whereabouts of adc ever be disclosed, proprietary /patented /etc.
I'm curios how exactly you drive adc inputs, mind to show circuits? Draft, hand sketched ?
2025-03-19 1:36 AM
Sure, here is a part the schematics of some of our measurement channels:
The nets MEAS_U_* are directly connected to the STM32H753, i.e. the ADC inputs. VRef is +3.0 Volts (not shown), and VBias+1V5 is derived from VRef.
For testing, I applied a 1 Hz, 600 Volt voltage on X2 between pins 1 and 4, and looked at the ADC samples of MEAS_U_L1.
I doubt that the jumps in the measured signal stem from the input circuitry, since firstly, it works for the majority of our parts, and secondly, even the bad parts seem to work when applying our "manual neutral everything:=0x200" calibration.
2025-03-19 8:15 AM
" For testing, we use to apply a slow (1Hz) ramp signal to ADC2 channel3 while the ADC is running, buffer ....."
There are things I'd measure:
1. Voltage across 100nF capacitor of not driven neighboring channels.
Sure, circuits is not correct, even small 4pF S/H capacitors running at 1 MSPS presents about 40 kOhm load, so error in conjunction with 221 resistors already ~0.0055 or at 11-th bit. Plus charge re-distribution over all channels plays tricks, so first things to try is to short all other channels to ground.
Better, of course, use newer high speed op amps (>50 MHz GBW) and low series resistance.
2025-03-19 8:58 AM
Thank you, MasterT,
I know that the source impedance that the ADC sees is not optimal; so some measurement error is to be expected.
Alas, my main concern is the awkward kind of error, i.e. the *jumps* in the ADC transfer curve, whenever the ADC value goes over an integer multiple of 0x2000. IMHO, this cannot be explained by too-high source impedance or inter-channel crosstalk, don't you agree?
2025-03-19 9:27 AM
W/o knowing internal structure of the adc I'd expect anything.
The fact that adc generates two bits per clock already indicates that it's not ordinary SAR where conversions happens sequentially bit '/cycle.
Segmentation into 4-big chunks may also indicates that 12-bits adc were "upgraded" for higher resolution.
My experience with H743 (though differential mode tested) says that adc is very sensitive to inputs series resistance, but properly driven by ad4891-2 has < -90 dBc THD-2 & 3, so overall linearity quite good. Despite huge noise level and 4-last bits out of 16 nothing else than junk.
2025-03-20 7:31 AM
For the record:
for testing, I shorted R35 (previously 221 Ohms), and removed C23 (previously 100nF) in one affected device. Result: the overall noise increased, and the systematic errors ("jumps") remained. So, this only made matters worse.
Then, I swapped U21 (the quad OpAmp) for a AD8608 (10 MHz GBW), which did not help either:
(Noise is worse by a factor of approx. 10; and the "jumps" in the transfer curve seem to be rougly the same)
2025-03-20 8:13 AM
" for testing, I shorted R35 (previously 221 Ohms), and removed C23 (previously 100nF) in one affected device. "
Bad idea. I suggest R to be in 10-20 Ohm range, and C as high as possible, may be even 10uF if input freq. is low or DC.
Have you try to vary adc internal clock? If high speed conversion not required (?) than lower clock get better accuracy
2025-03-20 9:57 AM
Thanks again, you seem to have a good intuition:
with R35=16.5Ohms, C23=2.2µF, the noise level is very good. Alas, the jumps remain:
2025-03-20 11:16 AM
Usually HAL driver calls
if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, conf_in) != HAL_OK) {....}
wrapping into if-statement, so how do you know if calibration was completed/ successfully ?
Have you verified bit-flag status ?