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
38/// Curve fitting ADC calibration scheme
39///
40/// This scheme implements polynomial error correction using predefined
41/// coefficient sets for each attenuation. It returns readings in mV.
42///
43/// This scheme also includes basic calibration ([`super::AdcCalBasic`]) and
44/// line fitting ([`AdcCalLine`]).
45#[derive(Clone, Copy)]
46pub struct AdcCalCurve<ADCI> {
47    line: AdcCalLine<ADCI>,
48
49    /// Coefficients of the error estimation polynomial.
50    ///
51    /// The constant coefficient comes first; the error polynomial is
52    /// `coeff[0] + coeff[1] * x + ... + coeff[n] * x^n`.
53    ///
54    /// This calibration works by first applying linear calibration. Then
55    /// the error polynomial is applied to the output of linear calibration.
56    /// The output of the polynomial is our estimate of the error; it gets
57    /// subtracted from linear calibration's output to get the final reading.
58    coeff: &'static [CurveCoeff],
59
60    _phantom: PhantomData<ADCI>,
61}
62
63impl<ADCI> crate::private::Sealed for AdcCalCurve<ADCI> {}
64
65impl<ADCI> AdcCalScheme<ADCI> for AdcCalCurve<ADCI>
66where
67    ADCI: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + CalibrationAccess,
68{
69    fn new_cal(atten: Attenuation) -> Self {
70        let line = AdcCalLine::<ADCI>::new_cal(atten);
71
72        let coeff = ADCI::CURVES_COEFFS
73            .iter()
74            .find(|item| item.atten == atten)
75            .expect("No curve coefficients for given attenuation")
76            .coeff;
77
78        Self {
79            line,
80            coeff,
81            _phantom: PhantomData,
82        }
83    }
84
85    fn adc_cal(&self) -> u16 {
86        self.line.adc_cal()
87    }
88
89    fn adc_val(&self, val: u16) -> u16 {
90        let val = self.line.adc_val(val);
91
92        let err = if val == 0 {
93            0
94        } else {
95            // err = coeff[0] + coeff[1] * val + coeff[2] * val^2 + ... + coeff[n] * val^n
96            let mut var = 1i64;
97            let mut err = (var * self.coeff[0] / COEFF_MUL) as i32;
98
99            for coeff in &self.coeff[1..] {
100                var *= val as i64;
101                err += (var * *coeff / COEFF_MUL) as i32;
102            }
103
104            err
105        };
106
107        (val as i32 - err) as u16
108    }
109}
110
111macro_rules! coeff_tables {
112    ($($(#[$($meta:meta)*])* $name:ident [ $($att:ident => [ $($val:literal,)* ],)* ];)*) => {
113        $(
114            $(#[$($meta)*])*
115            const $name: CurvesCoeffs = &[
116                $(CurveCoeffs {
117                    atten: Attenuation::$att,
118                    coeff: &[
119                        $(($val as f64 * COEFF_MUL as f64) as CurveCoeff,)*
120                    ],
121                },)*
122            ];
123        )*
124    };
125}
126
127#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
128mod impls {
129    use super::*;
130
131    impl AdcHasCurveCal for crate::peripherals::ADC1<'_> {
132        const CURVES_COEFFS: CurvesCoeffs = CURVES_COEFFS1;
133    }
134
135    #[cfg(esp32c3)]
136    impl AdcHasCurveCal for crate::peripherals::ADC2<'_> {
137        const CURVES_COEFFS: CurvesCoeffs = CURVES_COEFFS1;
138    }
139
140    #[cfg(esp32s3)]
141    impl AdcHasCurveCal for crate::peripherals::ADC2<'_> {
142        const CURVES_COEFFS: CurvesCoeffs = CURVES_COEFFS2;
143    }
144
145    coeff_tables! {
146        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32c3/curve_fitting_coefficients.c>
147        #[cfg(esp32c3)]
148        CURVES_COEFFS1 [
149            _0dB => [
150                -0.225966470500043,
151                -0.0007265418501948,
152                0.0000109410402681,
153            ],
154            _2p5dB => [
155                0.4229623392600516,
156                -0.0000731527490903,
157                0.0000088166562521,
158            ],
159            _6dB => [
160                -1.017859239236435,
161                -0.0097159265299153,
162                0.0000149794028038,
163            ],
164            _11dB => [
165                -1.4912262772850453,
166                -0.0228549975564099,
167                0.0000356391935717,
168                -0.0000000179964582,
169                0.0000000000042046,
170            ],
171        ];
172
173        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32c6/curve_fitting_coefficients.c>
174        #[cfg(esp32c6)]
175        CURVES_COEFFS1 [
176            _0dB => [
177                -0.0487166399931449,
178                0.0006436483033201,
179                0.0000030410131806,
180            ],
181            _2p5dB => [
182                -0.8665498165817785,
183                0.0015239070452946,
184                0.0000013818878844,
185            ],
186            _6dB => [
187                -1.2277821756674387,
188                0.0022275554717885,
189                0.0000005924302667,
190            ],
191            _11dB => [
192                -0.3801417550380255,
193                -0.0006020352420772,
194                0.0000012442478488,
195            ],
196        ];
197
198        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/465b159cd8771ffab6be70c7675ecf6705b62649/components/esp_adc/esp32h2/curve_fitting_coefficients.c>
199        #[cfg(esp32h2)]
200        CURVES_COEFFS1 [
201            _0dB => [
202                -0.5081991760658888,
203                0.0000007858995319,
204                0,
205            ],
206            _2p5dB => [
207                -0.8359230818901277,
208                0.0000009025419089,
209                0,
210            ],
211            _6dB => [
212                -1.165668771581976,
213                0.0000008294679249,
214                0,
215            ],
216            _11dB => [
217                -0.3637329628677273,
218                -0.0000196072597389,
219                0.0000007871689227,
220            ],
221        ];
222
223        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32s3/curve_fitting_coefficients.c>
224        #[cfg(esp32s3)]
225        CURVES_COEFFS1 [
226            _0dB => [
227                -2.7856531419538344,
228                -0.0050871540569528,
229                0.0000097982495890,
230            ],
231            _2p5dB => [
232                -2.9831022915028695,
233                -0.0049393185868806,
234                0.0000101379430548,
235            ],
236            _6dB => [
237                -2.3285545746296417,
238                -0.0147640181047414,
239                0.0000208385525314,
240            ],
241            _11dB => [
242                -0.644403418269478,
243                -0.0644334888647536,
244                0.0001297891447611,
245                -0.0000000707697180,
246                0.0000000000135150,
247            ],
248        ];
249
250        /// Error curve coefficients derived from <https://github.com/espressif/esp-idf/blob/903af13e8/components/esp_adc/esp32s3/curve_fitting_coefficients.c>
251        #[cfg(esp32s3)]
252        CURVES_COEFFS2 [
253            _0dB => [
254                -2.5668651654328927,
255                0.0001353548869615,
256                0.0000036615265189,
257            ],
258            _2p5dB => [
259                -2.3690184690298404,
260                -0.0066319894226185,
261                0.0000118964995959,
262            ],
263            _6dB => [
264                -0.9452499397020617,
265                -0.0200996773954387,
266                0.00000259011467956,
267            ],
268            _11dB => [
269                1.2247719764336924,
270                -0.0755717904943462,
271                0.0001478791187119,
272                -0.0000000796725280,
273                0.0000000000150380,
274            ],
275        ];
276    }
277}