esp_hal/analog/adc/calibration/line/
s2.rs1use core::marker::PhantomData;
2
3use super::AdcHasLineCal;
4use crate::analog::adc::{
5 AdcCalBasic,
6 AdcCalEfuse,
7 AdcCalScheme,
8 Attenuation,
9 CalibrationAccess,
10 Error,
11};
12
13const COEFF_A_SCALING: i64 = 65536;
14const COEFF_B_SCALING: i64 = 1024;
15const V_HIGH: [i64; 4] = [600, 800, 1000, 2000];
16const V_LOW: i64 = 250;
17
18#[derive(Clone, Copy)]
27pub struct AdcCalLine<ADCX> {
28 basic: AdcCalBasic<ADCX>,
29 coeff_a: u32,
30 coeff_b: i32,
31 _phantom: PhantomData<ADCX>,
32}
33
34fn characterize_two_point(atten: Attenuation, high: i64, low: i64) -> Result<(u32, i32), Error> {
35 let v_high = V_HIGH[atten as usize];
36 let denom = high - low;
39 if denom <= 0 {
40 return Err(Error::MissingCalibrationData);
41 }
42 let coeff_a = (COEFF_A_SCALING * (v_high - V_LOW) / denom) as u32;
43 let coeff_b = (COEFF_B_SCALING * (V_LOW * high - v_high * low) / denom) as i32;
44 Ok((coeff_a, coeff_b))
45}
46
47impl<ADCX> crate::private::Sealed for AdcCalLine<ADCX> {}
48
49impl<ADCX> AdcCalScheme<ADCX> for AdcCalLine<ADCX>
50where
51 ADCX: AdcCalEfuse + AdcHasLineCal + CalibrationAccess,
52{
53 fn new_cal(atten: Attenuation) -> Result<Self, Error> {
54 Self::new_cal_with_channel(atten, 0)
55 }
56
57 fn new_cal_with_channel(atten: Attenuation, channel: u8) -> Result<Self, Error> {
62 let basic = AdcCalBasic::<ADCX>::new_cal_with_channel(atten, channel)?;
63 let version = crate::efuse::rtc_calib_version();
64
65 let (coeff_a, coeff_b) = match version {
66 1 => {
67 let low = crate::efuse::rtc_calib_reading(
68 version,
69 ADCX::UNIT,
70 atten,
71 crate::efuse::RtcCalibParam::Vlow,
72 );
73 let high = crate::efuse::rtc_calib_reading(
74 version,
75 ADCX::UNIT,
76 atten,
77 crate::efuse::RtcCalibParam::Vhigh,
78 );
79 characterize_two_point(atten, high as i64, low as i64)?
80 }
81 2 => {
82 let Some(code) = ADCX::cal_code(atten) else {
85 return Err(Error::MissingCalibrationData);
86 };
87 if code == 0 {
88 return Err(Error::MissingCalibrationData);
89 }
90 let Some(mv) = ADCX::cal_mv(atten) else {
91 return Err(Error::MissingCalibrationData);
92 };
93 (((COEFF_A_SCALING * mv as i64) / code as i64) as u32, 0)
94 }
95 _ => {
96 return Err(Error::MissingCalibrationData);
99 }
100 };
101
102 Ok(Self {
103 basic,
104 coeff_a,
105 coeff_b,
106 _phantom: PhantomData,
107 })
108 }
109
110 fn adc_val(&self, val: u16) -> u16 {
111 let val = self.basic.adc_val(val) as i64;
112 let voltage = (val * self.coeff_a as i64 / (COEFF_A_SCALING / COEFF_B_SCALING)
113 + self.coeff_b as i64)
114 / COEFF_B_SCALING;
115 voltage.clamp(0, u16::MAX as i64) as u16
116 }
117}