Skip to main content

esp_hal/analog/adc/calibration/
curve.rs

1use core::marker::PhantomData;
2
3use crate::analog::adc::{
4    AdcCalEfuse,
5    AdcCalLine,
6    AdcCalScheme,
7    AdcHasLineCal,
8    Attenuation,
9    CalibrationAccess,
10    Error,
11};
12
13const COEFF_MUL: i64 = 1 << 52;
14
15/// Integer type for the error polynomial's coefficients. Despite
16/// the type, this is a fixed-point number with 52 fractional bits.
17type CurveCoeff = i64;
18
19/// Polynomial coefficients for a specified attenuation.
20pub struct CurveCoeffs {
21    /// Attenuation.
22    atten: Attenuation,
23    /// Polynomial coefficients.
24    coeff: &'static [CurveCoeff],
25}
26
27type CurvesCoeffs = &'static [CurveCoeffs];
28
29/// Marker trait for ADCs that support curve fitting.
30///
31/// See also [`AdcCalCurve`].
32pub trait AdcHasCurveCal {
33    /// Coefficients for calculating the reading voltage error.
34    ///
35    /// A set of coefficients for each attenuation, selected for the eFuse
36    /// calibration version on this chip.
37    fn curves_coeffs() -> CurvesCoeffs;
38}
39
40/// Curve fitting ADC calibration scheme.
41///
42/// This scheme implements polynomial error correction using predefined
43/// coefficient sets for each attenuation. It returns readings in mV.
44///
45/// This scheme also includes basic calibration ([`super::AdcCalBasic`]) and
46/// line fitting ([`AdcCalLine`]).
47#[derive(Clone, Copy)]
48pub struct AdcCalCurve<ADCX> {
49    line: AdcCalLine<ADCX>,
50
51    /// Coefficients of the error estimation polynomial.
52    ///
53    /// The constant coefficient comes first; the error polynomial is
54    /// `coeff[0] + coeff[1] * x + ... + coeff[n] * x^n`.
55    ///
56    /// This calibration works by first applying linear calibration. Then
57    /// the error polynomial is applied to the output of linear calibration.
58    /// The output of the polynomial is our estimate of the error; it gets
59    /// subtracted from linear calibration's output to get the final reading.
60    coeff: &'static [CurveCoeff],
61
62    _phantom: PhantomData<ADCX>,
63}
64
65impl<ADCX> crate::private::Sealed for AdcCalCurve<ADCX> {}
66
67impl<ADCX> AdcCalScheme<ADCX> for AdcCalCurve<ADCX>
68where
69    ADCX: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + CalibrationAccess,
70{
71    fn new_cal(atten: Attenuation) -> Result<Self, Error> {
72        Self::new_cal_with_channel(atten, 0)
73    }
74
75    fn new_cal_with_channel(atten: Attenuation, channel: u8) -> Result<Self, Error> {
76        let line = AdcCalLine::<ADCX>::new_cal_with_channel(atten, channel)?;
77
78        // Every shipped coefficient table covers all four attenuations; a miss
79        // means the user requested an attenuation the chip never characterized.
80        let Some(coeff) = ADCX::curves_coeffs()
81            .iter()
82            .find(|item| item.atten == atten)
83        else {
84            return Err(Error::UnsupportedAttenuation(atten));
85        };
86
87        Ok(Self {
88            line,
89            coeff: coeff.coeff,
90            _phantom: PhantomData,
91        })
92    }
93
94    fn adc_val(&self, val: u16) -> u16 {
95        let val = self.line.adc_val(val);
96
97        // Truncate each polynomial term before summing to match integer division
98        // in the curve-fitting reference model.
99        //
100        // The products need i128: the highest-order term (ESP32-S3, x^4) times its
101        // coefficient does not fit an i64.
102        let err = if val == 0 || self.coeff.is_empty() {
103            0
104        } else {
105            let mut var: i128 = 1; // val^i
106            let mut err: i128 = 0;
107
108            for &coeff in self.coeff {
109                err += var * i128::from(coeff) / i128::from(COEFF_MUL);
110                var *= i128::from(val);
111            }
112
113            err as i32
114        };
115
116        (val as i32 - err) as u16
117    }
118}
119
120macro_rules! coeff_tables {
121    ($($(#[$($meta:meta)*])* $name:ident [ $($att:ident => [ $($val:literal,)* ],)* ];)*) => {
122        $(
123            $(#[$($meta)*])*
124            const $name: CurvesCoeffs = &[
125                $(CurveCoeffs {
126                    atten: Attenuation::$att,
127                    coeff: &[
128                        $(($val as f64 * COEFF_MUL as f64) as CurveCoeff,)*
129                    ],
130                },)*
131            ];
132        )*
133    };
134}
135
136#[cfg(adc_curve_calibration)]
137mod impls {
138    use super::*;
139
140    impl AdcHasCurveCal for crate::peripherals::ADC1<'_> {
141        fn curves_coeffs() -> CurvesCoeffs {
142            #[cfg(esp32c6)]
143            if crate::efuse::rtc_calib_version() == 2 {
144                return CURVES_COEFFS1_V2;
145            }
146
147            CURVES_COEFFS1
148        }
149    }
150
151    #[cfg(adc_adc2)]
152    impl AdcHasCurveCal for crate::peripherals::ADC2<'_> {
153        fn curves_coeffs() -> CurvesCoeffs {
154            CURVES_COEFFS2
155        }
156    }
157
158    coeff_tables! {
159        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32c3/curve_fitting_coefficients.c>
160        #[cfg(esp32c3)]
161        CURVES_COEFFS1 [
162            _0dB => [
163                -0.225966470500043,
164                -0.0007265418501948,
165                0.0000109410402681,
166            ],
167            _2p5dB => [
168                0.4229623392600516,
169                -0.0000731527490903,
170                0.0000088166562521,
171            ],
172            _6dB => [
173                -1.017859239236435,
174                -0.0097159265299153,
175                0.0000149794028038,
176            ],
177            _11dB => [
178                -1.4912262772850453,
179                -0.0228549975564099,
180                0.0000356391935717,
181                -0.0000000179964582,
182                0.0000000000042046,
183            ],
184        ];
185
186        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/c602e55/components/esp_adc/esp32c5/curve_fitting_coefficients.c>
187        #[cfg(esp32c5)]
188        CURVES_COEFFS1 [
189            _0dB => [
190                -0.2941017829027464,
191                0.0007368674918527,
192                0.0,
193            ],
194            _2p5dB => [
195                -0.3224276125615327,
196                0.0005325658467636,
197                0.0,
198            ],
199            _6dB => [
200                -0.3307554632960901,
201                0.000409244304226,
202                0.0,
203            ],
204            _11dB => [
205                1.463642578413965,
206                -0.003349642363147,
207                0.0000011676836451,
208            ],
209        ];
210
211        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/027613140/components/esp_adc/esp32c6/curve_fitting_coefficients.c#L29-L35>
212        #[cfg(esp32c6)]
213        CURVES_COEFFS1 [
214            _0dB => [
215                -0.487166399931449,
216                0.0006436483033201,
217                0.0000030410131806,
218            ],
219            _2p5dB => [
220                -0.8665498165817785,
221                0.0015239070452946,
222                0.0000013818878844,
223            ],
224            _6dB => [
225                -1.2277821756674387,
226                0.0022275554717885,
227                0.0000005924302667,
228            ],
229            _11dB => [
230                -0.3801417550380255,
231                -0.0006020352420772,
232                0.0000012442478488,
233            ],
234        ];
235
236        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/1e76669a8b940f5dc25adc35065cb53de3c71423/components/esp_adc/esp32c61/curve_fitting_coefficients.c>
237        #[cfg(esp32c61)]
238        CURVES_COEFFS1 [
239            _0dB => [
240                -0.8668885650149671,
241                0.0015630376830615,
242            ],
243            _2p5dB => [
244                -1.090569589734153,
245                0.0013859487941542,
246            ],
247            _6dB => [
248                -1.4231790752153335,
249                0.00122016745867,
250            ],
251            _11dB => [
252                -1.3204544579940347,
253                -0.0011762579610906,
254                0.0000007639928529,
255            ],
256        ];
257
258        /// Error curve coefficients for calibration version 2 derived from <https://github.com/espressif/esp-idf/blob/027613140/components/esp_adc/esp32c6/curve_fitting_coefficients.c#L36-L42>
259        ///
260        /// Version 2 does not apply a second-step polynomial at 0 dB and 2.5 dB.
261        #[cfg(esp32c6)]
262        CURVES_COEFFS1_V2 [
263            _0dB => [],
264            _2p5dB => [],
265            _6dB => [
266                -1.2217864764388775,
267                -0.0001954123107752,
268                0.0000006409679727,
269            ],
270            _11dB => [
271                -0.3915910437042445,
272                -0.0031536470857564,
273                0.0000012493873014,
274            ],
275        ];
276
277        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/465b159cd8771ffab6be70c7675ecf6705b62649/components/esp_adc/esp32h2/curve_fitting_coefficients.c>
278        #[cfg(esp32h2)]
279        CURVES_COEFFS1 [
280            _0dB => [
281                -0.5081991760658888,
282                0.0000007858995319,
283                0,
284            ],
285            _2p5dB => [
286                -0.8359230818901277,
287                0.0000009025419089,
288                0,
289            ],
290            _6dB => [
291                -1.165668771581976,
292                0.0000008294679249,
293                0,
294            ],
295            _11dB => [
296                -0.3637329628677273,
297                -0.0000196072597389,
298                0.0000007871689227,
299            ],
300        ];
301
302        /// Error curve coefficients for ADC1, derived from <https://github.com/espressif/esp-idf/blob/08e0d30a74a/components/esp_adc/esp32p4/curve_fitting_coefficients.c>
303        #[cfg(esp32p4)]
304        CURVES_COEFFS1 [
305            _0dB => [
306                -0.7170501832480995,
307                0.0010598497992115,
308            ],
309            _2p5dB => [
310                -0.9960085535084866,
311                0.0015840076608145,
312            ],
313            _6dB => [
314                -1.4711053224678996,
315                0.00001594266424857,
316            ],
317            _11dB => [
318                -2.8811493455181565,
319                0.0010082311568625,
320            ],
321        ];
322
323        /// Error curve coefficients for ADC2, derived from <https://github.com/espressif/esp-idf/blob/08e0d30a74a/components/esp_adc/esp32p4/curve_fitting_coefficients.c>
324        #[cfg(esp32p4)]
325        CURVES_COEFFS2 [
326            _0dB => [
327                -0.4900967548489932,
328                0.0005037402667913,
329            ],
330            _2p5dB => [
331                -0.7296214814536025,
332                0.0011021577596635,
333            ],
334            _6dB => [
335                -1.0991620450220592,
336                0.0011623930881896,
337            ],
338            _11dB => [
339                -2.442140102462673,
340                0.0009458501263393,
341            ],
342        ];
343
344        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32s3/curve_fitting_coefficients.c>
345        #[cfg(esp32s3)]
346        CURVES_COEFFS1 [
347            _0dB => [
348                -2.7856531419538344,
349                -0.0050871540569528,
350                0.0000097982495890,
351            ],
352            _2p5dB => [
353                -2.9831022915028695,
354                -0.0049393185868806,
355                0.0000101379430548,
356            ],
357            _6dB => [
358                -2.3285545746296417,
359                -0.0147640181047414,
360                0.0000208385525314,
361            ],
362            _11dB => [
363                -0.644403418269478,
364                -0.0644334888647536,
365                0.0001297891447611,
366                -0.0000000707697180,
367                0.0000000000135150,
368            ],
369        ];
370
371        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32s3/curve_fitting_coefficients.c>
372        #[cfg(esp32s3)]
373        CURVES_COEFFS2 [
374            _0dB => [
375                -2.5668651654328927,
376                0.0001353548869615,
377                0.0000036615265189,
378            ],
379            _2p5dB => [
380                -2.3690184690298404,
381                -0.0066319894226185,
382                0.0000118964995959,
383            ],
384            _6dB => [
385                -0.9452499397020617,
386                -0.0200996773954387,
387                0.0000259011467956,
388            ],
389            _11dB => [
390                1.2247719764336924,
391                -0.0755717904943462,
392                0.0001478791187119,
393                -0.0000000796725280,
394                0.0000000000150380,
395            ],
396        ];
397    }
398}