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