1
use core::marker::PhantomData;
2

            
3
use crate::analog::adc::{
4
    AdcCalEfuse,
5
    AdcCalLine,
6
    AdcCalScheme,
7
    AdcHasLineCal,
8
    Attenuation,
9
    CalibrationAccess,
10
};
11

            
12
const 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.
16
type CurveCoeff = i64;
17

            
18
/// Polynomial coefficients for a specified attenuation.
19
pub struct CurveCoeffs {
20
    /// Attenuation.
21
    atten: Attenuation,
22
    /// Polynomial coefficients.
23
    coeff: &'static [CurveCoeff],
24
}
25

            
26
type CurvesCoeffs = &'static [CurveCoeffs];
27

            
28
/// Marker trait for ADCs that support curve fitting.
29
///
30
/// See also [`AdcCalCurve`].
31
pub trait AdcHasCurveCal {
32
    /// Coefficients for calculating the reading voltage error.
33
    ///
34
    /// A set of coefficients for each attenuation, selected for the eFuse
35
    /// calibration version on this chip.
36
    fn curves_coeffs() -> CurvesCoeffs;
37
}
38

            
39
/// Curve fitting ADC calibration scheme.
40
///
41
/// This scheme implements polynomial error correction using predefined
42
/// coefficient sets for each attenuation. It returns readings in mV.
43
///
44
/// This scheme also includes basic calibration ([`super::AdcCalBasic`]) and
45
/// line fitting ([`AdcCalLine`]).
46
#[derive(Clone, Copy)]
47
pub struct AdcCalCurve<ADCX> {
48
    line: AdcCalLine<ADCX>,
49

            
50
    /// Coefficients of the error estimation polynomial.
51
    ///
52
    /// The constant coefficient comes first; the error polynomial is
53
    /// `coeff[0] + coeff[1] * x + ... + coeff[n] * x^n`.
54
    ///
55
    /// This calibration works by first applying linear calibration. Then
56
    /// the error polynomial is applied to the output of linear calibration.
57
    /// The output of the polynomial is our estimate of the error; it gets
58
    /// subtracted from linear calibration's output to get the final reading.
59
    coeff: &'static [CurveCoeff],
60

            
61
    _phantom: PhantomData<ADCX>,
62
}
63

            
64
impl<ADCX> crate::private::Sealed for AdcCalCurve<ADCX> {}
65

            
66
impl<ADCX> AdcCalScheme<ADCX> for AdcCalCurve<ADCX>
67
where
68
    ADCX: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + CalibrationAccess,
69
{
70
    fn new_cal(atten: Attenuation) -> Self {
71
        Self::new_cal_with_channel(atten, 0)
72
    }
73

            
74
    fn new_cal_with_channel(atten: Attenuation, channel: u8) -> Self {
75
        let line = AdcCalLine::<ADCX>::new_cal_with_channel(atten, channel);
76

            
77
        let coeff = ADCX::curves_coeffs()
78
            .iter()
79
            .find(|item| item.atten == atten)
80
            .expect("No curve coefficients for given attenuation")
81
            .coeff;
82

            
83
        Self {
84
            line,
85
            coeff,
86
            _phantom: PhantomData,
87
        }
88
    }
89

            
90
    fn adc_val(&self, val: u16) -> u16 {
91
        let val = self.line.adc_val(val);
92

            
93
        // Truncate each polynomial term before summing to match integer division
94
        // in the curve-fitting reference model.
95
        //
96
        // The products need i128: the highest-order term (ESP32-S3, x^4) times its
97
        // coefficient does not fit an i64.
98
        let err = if val == 0 || self.coeff.is_empty() {
99
            0
100
        } else {
101
            let mut var: i128 = 1; // val^i
102
            let mut err: i128 = 0;
103

            
104
            for &coeff in self.coeff {
105
                err += var * i128::from(coeff) / i128::from(COEFF_MUL);
106
                var *= i128::from(val);
107
            }
108

            
109
            err as i32
110
        };
111

            
112
        (val as i32 - err) as u16
113
    }
114
}
115

            
116
macro_rules! coeff_tables {
117
    ($($(#[$($meta:meta)*])* $name:ident [ $($att:ident => [ $($val:literal,)* ],)* ];)*) => {
118
        $(
119
            $(#[$($meta)*])*
120
            const $name: CurvesCoeffs = &[
121
                $(CurveCoeffs {
122
                    atten: Attenuation::$att,
123
                    coeff: &[
124
                        $(($val as f64 * COEFF_MUL as f64) as CurveCoeff,)*
125
                    ],
126
                },)*
127
            ];
128
        )*
129
    };
130
}
131

            
132
#[cfg(adc_curve_calibration)]
133
mod impls {
134
    use super::*;
135

            
136
    impl AdcHasCurveCal for crate::peripherals::ADC1<'_> {
137
        fn curves_coeffs() -> CurvesCoeffs {
138
            #[cfg(esp32c6)]
139
            if crate::efuse::rtc_calib_version() == 2 {
140
                return CURVES_COEFFS1_V2;
141
            }
142

            
143
            CURVES_COEFFS1
144
        }
145
    }
146

            
147
    #[cfg(adc_adc2)]
148
    impl AdcHasCurveCal for crate::peripherals::ADC2<'_> {
149
        fn curves_coeffs() -> CurvesCoeffs {
150
            CURVES_COEFFS2
151
        }
152
    }
153

            
154
    coeff_tables! {
155
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32c3/curve_fitting_coefficients.c>
156
        #[cfg(esp32c3)]
157
        CURVES_COEFFS1 [
158
            _0dB => [
159
                -0.225966470500043,
160
                -0.0007265418501948,
161
                0.0000109410402681,
162
            ],
163
            _2p5dB => [
164
                0.4229623392600516,
165
                -0.0000731527490903,
166
                0.0000088166562521,
167
            ],
168
            _6dB => [
169
                -1.017859239236435,
170
                -0.0097159265299153,
171
                0.0000149794028038,
172
            ],
173
            _11dB => [
174
                -1.4912262772850453,
175
                -0.0228549975564099,
176
                0.0000356391935717,
177
                -0.0000000179964582,
178
                0.0000000000042046,
179
            ],
180
        ];
181

            
182
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/c602e55/components/esp_adc/esp32c5/curve_fitting_coefficients.c>
183
        #[cfg(esp32c5)]
184
        CURVES_COEFFS1 [
185
            _0dB => [
186
                -0.2941017829027464,
187
                0.0007368674918527,
188
                0.0,
189
            ],
190
            _2p5dB => [
191
                -0.3224276125615327,
192
                0.0005325658467636,
193
                0.0,
194
            ],
195
            _6dB => [
196
                -0.3307554632960901,
197
                0.000409244304226,
198
                0.0,
199
            ],
200
            _11dB => [
201
                1.463642578413965,
202
                -0.003349642363147,
203
                0.0000011676836451,
204
            ],
205
        ];
206

            
207
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/027613140/components/esp_adc/esp32c6/curve_fitting_coefficients.c#L29-L35>
208
        #[cfg(esp32c6)]
209
        CURVES_COEFFS1 [
210
            _0dB => [
211
                -0.487166399931449,
212
                0.0006436483033201,
213
                0.0000030410131806,
214
            ],
215
            _2p5dB => [
216
                -0.8665498165817785,
217
                0.0015239070452946,
218
                0.0000013818878844,
219
            ],
220
            _6dB => [
221
                -1.2277821756674387,
222
                0.0022275554717885,
223
                0.0000005924302667,
224
            ],
225
            _11dB => [
226
                -0.3801417550380255,
227
                -0.0006020352420772,
228
                0.0000012442478488,
229
            ],
230
        ];
231

            
232
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/1e76669a8b940f5dc25adc35065cb53de3c71423/components/esp_adc/esp32c61/curve_fitting_coefficients.c>
233
        #[cfg(esp32c61)]
234
        CURVES_COEFFS1 [
235
            _0dB => [
236
                -0.8668885650149671,
237
                0.0015630376830615,
238
            ],
239
            _2p5dB => [
240
                -1.090569589734153,
241
                0.0013859487941542,
242
            ],
243
            _6dB => [
244
                -1.4231790752153335,
245
                0.00122016745867,
246
            ],
247
            _11dB => [
248
                -1.3204544579940347,
249
                -0.0011762579610906,
250
                0.0000007639928529,
251
            ],
252
        ];
253

            
254
        /// 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>
255
        ///
256
        /// Version 2 does not apply a second-step polynomial at 0 dB and 2.5 dB.
257
        #[cfg(esp32c6)]
258
        CURVES_COEFFS1_V2 [
259
            _0dB => [],
260
            _2p5dB => [],
261
            _6dB => [
262
                -1.2217864764388775,
263
                -0.0001954123107752,
264
                0.0000006409679727,
265
            ],
266
            _11dB => [
267
                -0.3915910437042445,
268
                -0.0031536470857564,
269
                0.0000012493873014,
270
            ],
271
        ];
272

            
273
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/465b159cd8771ffab6be70c7675ecf6705b62649/components/esp_adc/esp32h2/curve_fitting_coefficients.c>
274
        #[cfg(esp32h2)]
275
        CURVES_COEFFS1 [
276
            _0dB => [
277
                -0.5081991760658888,
278
                0.0000007858995319,
279
                0,
280
            ],
281
            _2p5dB => [
282
                -0.8359230818901277,
283
                0.0000009025419089,
284
                0,
285
            ],
286
            _6dB => [
287
                -1.165668771581976,
288
                0.0000008294679249,
289
                0,
290
            ],
291
            _11dB => [
292
                -0.3637329628677273,
293
                -0.0000196072597389,
294
                0.0000007871689227,
295
            ],
296
        ];
297

            
298
        /// Error curve coefficients for ADC1, derived from <https://github.com/espressif/esp-idf/blob/08e0d30a74a/components/esp_adc/esp32p4/curve_fitting_coefficients.c>
299
        #[cfg(esp32p4)]
300
        CURVES_COEFFS1 [
301
            _0dB => [
302
                -0.7170501832480995,
303
                0.0010598497992115,
304
            ],
305
            _2p5dB => [
306
                -0.9960085535084866,
307
                0.0015840076608145,
308
            ],
309
            _6dB => [
310
                -1.4711053224678996,
311
                0.00001594266424857,
312
            ],
313
            _11dB => [
314
                -2.8811493455181565,
315
                0.0010082311568625,
316
            ],
317
        ];
318

            
319
        /// Error curve coefficients for ADC2, derived from <https://github.com/espressif/esp-idf/blob/08e0d30a74a/components/esp_adc/esp32p4/curve_fitting_coefficients.c>
320
        #[cfg(esp32p4)]
321
        CURVES_COEFFS2 [
322
            _0dB => [
323
                -0.4900967548489932,
324
                0.0005037402667913,
325
            ],
326
            _2p5dB => [
327
                -0.7296214814536025,
328
                0.0011021577596635,
329
            ],
330
            _6dB => [
331
                -1.0991620450220592,
332
                0.0011623930881896,
333
            ],
334
            _11dB => [
335
                -2.442140102462673,
336
                0.0009458501263393,
337
            ],
338
        ];
339

            
340
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32s3/curve_fitting_coefficients.c>
341
        #[cfg(esp32s3)]
342
        CURVES_COEFFS1 [
343
            _0dB => [
344
                -2.7856531419538344,
345
                -0.0050871540569528,
346
                0.0000097982495890,
347
            ],
348
            _2p5dB => [
349
                -2.9831022915028695,
350
                -0.0049393185868806,
351
                0.0000101379430548,
352
            ],
353
            _6dB => [
354
                -2.3285545746296417,
355
                -0.0147640181047414,
356
                0.0000208385525314,
357
            ],
358
            _11dB => [
359
                -0.644403418269478,
360
                -0.0644334888647536,
361
                0.0001297891447611,
362
                -0.0000000707697180,
363
                0.0000000000135150,
364
            ],
365
        ];
366

            
367
        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32s3/curve_fitting_coefficients.c>
368
        #[cfg(esp32s3)]
369
        CURVES_COEFFS2 [
370
            _0dB => [
371
                -2.5668651654328927,
372
                0.0001353548869615,
373
                0.0000036615265189,
374
            ],
375
            _2p5dB => [
376
                -2.3690184690298404,
377
                -0.0066319894226185,
378
                0.0000118964995959,
379
            ],
380
            _6dB => [
381
                -0.9452499397020617,
382
                -0.0200996773954387,
383
                0.0000259011467956,
384
            ],
385
            _11dB => [
386
                1.2247719764336924,
387
                -0.0755717904943462,
388
                0.0001478791187119,
389
                -0.0000000796725280,
390
                0.0000000000150380,
391
            ],
392
        ];
393
    }
394
}