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}