diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fae80b5169..a0384d474ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add initial LP-IO support for ESP32-C6 (#639) - Implement sleep with some wakeup methods for `esp32` (#574) - Add a new RMT driver (#653, #667) +- Implemented calibrated ADC API for ESP32-S3 (#641) - Add MCPWM DeadTime configuration (#406) ### Changed diff --git a/esp-hal-common/src/analog/adc/cal_basic.rs b/esp-hal-common/src/analog/adc/cal_basic.rs index 3dc279fe050..cfa3508fc63 100644 --- a/esp-hal-common/src/analog/adc/cal_basic.rs +++ b/esp-hal-common/src/analog/adc/cal_basic.rs @@ -1,6 +1,13 @@ use core::marker::PhantomData; -use crate::adc::{AdcCalEfuse, AdcCalScheme, AdcCalSource, AdcConfig, Attenuation, RegisterAccess}; +use crate::adc::{ + AdcCalEfuse, + AdcCalScheme, + AdcCalSource, + AdcConfig, + Attenuation, + CalibrationAccess, +}; /// Basic ADC calibration scheme /// @@ -18,7 +25,7 @@ pub struct AdcCalBasic { impl AdcCalScheme for AdcCalBasic where - ADCI: AdcCalEfuse + RegisterAccess, + ADCI: AdcCalEfuse + CalibrationAccess, { fn new_cal(atten: Attenuation) -> Self { // Try to get init code (Dout0) from efuse diff --git a/esp-hal-common/src/analog/adc/cal_curve.rs b/esp-hal-common/src/analog/adc/cal_curve.rs index 1beaa542a82..75bfe4d5603 100644 --- a/esp-hal-common/src/analog/adc/cal_curve.rs +++ b/esp-hal-common/src/analog/adc/cal_curve.rs @@ -6,7 +6,7 @@ use crate::adc::{ AdcCalScheme, AdcHasLineCal, Attenuation, - RegisterAccess, + CalibrationAccess, }; const COEFF_MUL: i64 = 1 << 52; @@ -52,7 +52,7 @@ pub struct AdcCalCurve { impl AdcCalScheme for AdcCalCurve where - ADCI: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + RegisterAccess, + ADCI: AdcCalEfuse + AdcHasLineCal + AdcHasCurveCal + CalibrationAccess, { fn new_cal(atten: Attenuation) -> Self { let line = AdcCalLine::::new_cal(atten); diff --git a/esp-hal-common/src/analog/adc/cal_line.rs b/esp-hal-common/src/analog/adc/cal_line.rs index 73d72455895..e0bae18a972 100644 --- a/esp-hal-common/src/analog/adc/cal_line.rs +++ b/esp-hal-common/src/analog/adc/cal_line.rs @@ -7,7 +7,7 @@ use crate::adc::{ AdcCalSource, AdcConfig, Attenuation, - RegisterAccess, + CalibrationAccess, }; /// Marker trait for ADC units which support line fitting @@ -46,7 +46,7 @@ pub struct AdcCalLine { impl AdcCalScheme for AdcCalLine where - ADCI: AdcCalEfuse + AdcHasLineCal + RegisterAccess, + ADCI: AdcCalEfuse + AdcHasLineCal + CalibrationAccess, { fn new_cal(atten: Attenuation) -> Self { let basic = AdcCalBasic::::new_cal(atten); @@ -93,8 +93,8 @@ where } } -#[cfg(any(esp32c2, esp32c3, esp32c6))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] impl AdcHasLineCal for crate::adc::ADC1 {} -#[cfg(esp32c3)] +#[cfg(any(esp32c3, esp32s3))] impl AdcHasLineCal for crate::adc::ADC2 {} diff --git a/esp-hal-common/src/analog/adc/riscv.rs b/esp-hal-common/src/analog/adc/riscv.rs index 43d3ab75845..32b56f888dd 100644 --- a/esp-hal-common/src/analog/adc/riscv.rs +++ b/esp-hal-common/src/analog/adc/riscv.rs @@ -60,7 +60,7 @@ cfg_if::cfg_if! { const ADC_VAL_MASK: u16 = 0xfff; const ADC_CAL_CNT_MAX: u16 = 32; - const ADC_CAL_CHANNEL: u32 = 0xf; + const ADC_CAL_CHANNEL: u16 = 15; const ADC_SAR1_ENCAL_GND_ADDR: u8 = 0x7; const ADC_SAR1_ENCAL_GND_ADDR_MSB: u8 = 5; @@ -166,7 +166,10 @@ pub struct AdcPin { _phantom: PhantomData, } -impl, ADCI, CS> Channel for AdcPin { +impl Channel for AdcPin +where + PIN: Channel, +{ type ID = u8; fn channel() -> Self::ID { @@ -188,11 +191,10 @@ where Self::default() } - pub fn enable_pin>( - &mut self, - pin: PIN, - attenuation: Attenuation, - ) -> AdcPin { + pub fn enable_pin(&mut self, pin: PIN, attenuation: Attenuation) -> AdcPin + where + PIN: Channel, + { self.attenuations[PIN::channel() as usize] = Some(attenuation); AdcPin { @@ -202,11 +204,16 @@ where } } - pub fn enable_pin_with_cal, CS: AdcCalScheme>( + pub fn enable_pin_with_cal( &mut self, pin: PIN, attenuation: Attenuation, - ) -> AdcPin { + ) -> AdcPin + where + ADCI: CalibrationAccess, + PIN: Channel, + CS: AdcCalScheme, + { self.attenuations[PIN::channel() as usize] = Some(attenuation); AdcPin { @@ -217,7 +224,10 @@ where } /// Calibrate ADC with specified attenuation and voltage source - pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16 { + pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16 + where + ADCI: CalibrationAccess, + { let mut adc_max: u16 = 0; let mut adc_min: u16 = u16::MAX; let mut adc_sum: u32 = 0; @@ -290,13 +300,19 @@ pub trait RegisterAccess { /// Reset flags fn reset(); + /// Set calibration parameter to ADC hardware + fn set_init_code(data: u16); +} + +pub trait CalibrationAccess: RegisterAccess { + const ADC_CAL_CNT_MAX: u16; + const ADC_CAL_CHANNEL: u16; + const ADC_VAL_MASK: u16; + fn enable_vdef(enable: bool); - /// Enable internal connect GND (for calibration) + /// Enable internal calibration voltage source fn connect_cal(source: AdcCalSource, enable: bool); - - /// Set calibration parameter to ADC hardware - fn set_init_code(data: u16); } impl RegisterAccess for ADC1 { @@ -347,6 +363,33 @@ impl RegisterAccess for ADC1 { .modify(|_, w| w.saradc_onetime_start().clear_bit()); } + fn set_init_code(data: u16) { + let [msb, lsb] = data.to_be_bytes(); + + regi2c_write_mask( + I2C_SAR_ADC, + I2C_SAR_ADC_HOSTID, + ADC_SAR1_INITIAL_CODE_HIGH_ADDR, + ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB, + ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB, + msb as _, + ); + regi2c_write_mask( + I2C_SAR_ADC, + I2C_SAR_ADC_HOSTID, + ADC_SAR1_INITIAL_CODE_LOW_ADDR, + ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB, + ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB, + lsb as _, + ); + } +} + +impl CalibrationAccess for ADC1 { + const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX; + const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL; + const ADC_VAL_MASK: u16 = ADC_VAL_MASK; + fn enable_vdef(enable: bool) { let value = enable as _; regi2c_write_mask( @@ -380,27 +423,6 @@ impl RegisterAccess for ADC1 { ), } } - - fn set_init_code(data: u16) { - let [msb, lsb] = data.to_be_bytes(); - - regi2c_write_mask( - I2C_SAR_ADC, - I2C_SAR_ADC_HOSTID, - ADC_SAR1_INITIAL_CODE_HIGH_ADDR, - ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB, - ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB, - msb as _, - ); - regi2c_write_mask( - I2C_SAR_ADC, - I2C_SAR_ADC_HOSTID, - ADC_SAR1_INITIAL_CODE_LOW_ADDR, - ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB, - ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB, - lsb as _, - ); - } } #[cfg(esp32c3)] @@ -450,6 +472,34 @@ impl RegisterAccess for ADC2 { .modify(|_, w| w.saradc_onetime_start().clear_bit()); } + fn set_init_code(data: u16) { + let [msb, lsb] = data.to_be_bytes(); + + regi2c_write_mask( + I2C_SAR_ADC, + I2C_SAR_ADC_HOSTID, + ADC_SAR2_INITIAL_CODE_HIGH_ADDR, + ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB, + ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB, + msb as _, + ); + regi2c_write_mask( + I2C_SAR_ADC, + I2C_SAR_ADC_HOSTID, + ADC_SAR2_INITIAL_CODE_LOW_ADDR, + ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB, + ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB, + lsb as _, + ); + } +} + +#[cfg(esp32c3)] +impl CalibrationAccess for ADC2 { + const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX; + const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL; + const ADC_VAL_MASK: u16 = ADC_VAL_MASK; + fn enable_vdef(enable: bool) { let value = enable as _; regi2c_write_mask( @@ -483,27 +533,6 @@ impl RegisterAccess for ADC2 { ), } } - - fn set_init_code(data: u16) { - let [msb, lsb] = data.to_be_bytes(); - - regi2c_write_mask( - I2C_SAR_ADC, - I2C_SAR_ADC_HOSTID, - ADC_SAR2_INITIAL_CODE_HIGH_ADDR, - ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB, - ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB, - msb as _, - ); - regi2c_write_mask( - I2C_SAR_ADC, - I2C_SAR_ADC_HOSTID, - ADC_SAR2_INITIAL_CODE_LOW_ADDR, - ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB, - ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB, - lsb as _, - ); - } } pub struct ADC<'d, ADCI> { diff --git a/esp-hal-common/src/analog/adc/xtensa.rs b/esp-hal-common/src/analog/adc/xtensa.rs index 53d67d16c4c..cfba895a55c 100644 --- a/esp-hal-common/src/analog/adc/xtensa.rs +++ b/esp-hal-common/src/analog/adc/xtensa.rs @@ -2,12 +2,88 @@ use core::marker::PhantomData; use embedded_hal::adc::{Channel, OneShot}; +#[cfg(esp32s3)] +use crate::efuse::Efuse; use crate::{ analog::{ADC1, ADC2}, peripheral::PeripheralRef, peripherals::{APB_SARADC, SENS}, }; +#[cfg(esp32s3)] +mod cal_basic; +#[cfg(esp32s3)] +mod cal_curve; +#[cfg(esp32s3)] +mod cal_line; + +#[cfg(esp32s3)] +pub use cal_basic::AdcCalBasic; +#[cfg(esp32s3)] +pub use cal_curve::{AdcCalCurve, AdcHasCurveCal}; +#[cfg(esp32s3)] +pub use cal_line::{AdcCalLine, AdcHasLineCal}; + +pub use crate::analog::{AdcCalEfuse, AdcCalScheme}; + +// Constants taken from: +// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32s2/include/soc/regi2c_saradc.h +// https://github.com/espressif/esp-idf/blob/903af13e8/components/soc/esp32s3/include/soc/regi2c_saradc.h +cfg_if::cfg_if! { + if #[cfg(any(esp32s2, esp32s3))] { + const I2C_SAR_ADC: u8 = 0x69; + const I2C_SAR_ADC_HOSTID: u8 = 1; + + const ADC_VAL_MASK: u16 = 0xfff; + const ADC_CAL_CNT_MAX: u16 = 32; + const ADC_CAL_CHANNEL: u16 = 15; + + const ADC_SAR1_ENCAL_GND_ADDR: u8 = 0x7; + const ADC_SAR1_ENCAL_GND_ADDR_MSB: u8 = 5; + const ADC_SAR1_ENCAL_GND_ADDR_LSB: u8 = 5; + + const ADC_SAR1_INITIAL_CODE_HIGH_ADDR: u8 = 0x1; + const ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB: u8 = 0x3; + const ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB: u8 = 0x0; + + const ADC_SAR1_INITIAL_CODE_LOW_ADDR: u8 = 0x0; + const ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB: u8 = 0x7; + const ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB: u8 = 0x0; + + const ADC_SAR1_DREF_ADDR: u8 = 0x2; + const ADC_SAR1_DREF_ADDR_MSB: u8 = 0x6; + const ADC_SAR1_DREF_ADDR_LSB: u8 = 0x4; + + const ADC_SARADC1_ENCAL_REF_ADDR: u8 = 0x7; + const ADC_SARADC1_ENCAL_REF_ADDR_MSB: u8 = 4; + const ADC_SARADC1_ENCAL_REF_ADDR_LSB: u8 = 4; + } +} + +cfg_if::cfg_if! { + if #[cfg(any(esp32s2, esp32s3))] { + const ADC_SAR2_ENCAL_GND_ADDR: u8 = 0x7; + const ADC_SAR2_ENCAL_GND_ADDR_MSB: u8 = 5; + const ADC_SAR2_ENCAL_GND_ADDR_LSB: u8 = 5; + + const ADC_SAR2_INITIAL_CODE_HIGH_ADDR: u8 = 0x4; + const ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB: u8 = 0x3; + const ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB: u8 = 0x0; + + const ADC_SAR2_INITIAL_CODE_LOW_ADDR: u8 = 0x3; + const ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB: u8 = 0x7; + const ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB: u8 = 0x0; + + const ADC_SAR2_DREF_ADDR: u8 = 0x5; + const ADC_SAR2_DREF_ADDR_MSB: u8 = 0x6; + const ADC_SAR2_DREF_ADDR_LSB: u8 = 0x4; + + const ADC_SARADC2_ENCAL_REF_ADDR: u8 = 0x7; + const ADC_SARADC2_ENCAL_REF_ADDR_MSB: u8 = 4; + const ADC_SARADC2_ENCAL_REF_ADDR_LSB: u8 = 4; + } +} + /// The sampling/readout resolution of the ADC #[derive(PartialEq, Eq, Clone, Copy)] pub enum Resolution { @@ -17,18 +93,53 @@ pub enum Resolution { /// The attenuation of the ADC pin #[derive(PartialEq, Eq, Clone, Copy)] pub enum Attenuation { + /// 0 dB attenuation, measurement range: 0 - 800 mV Attenuation0dB = 0b00, + /// 2.5 dB attenuation, measurement range: 0 - 1100 mV Attenuation2p5dB = 0b01, + /// 6 dB attenuation, measurement range: 0 - 1350 mV Attenuation6dB = 0b10, + /// 11 dB attenuation, measurement range: 0 - 2600 mV Attenuation11dB = 0b11, } -pub struct AdcPin { +impl Attenuation { + /// List of all supported attenuations + pub const ALL: &'static [Attenuation] = &[ + Attenuation::Attenuation0dB, + Attenuation::Attenuation2p5dB, + Attenuation::Attenuation6dB, + Attenuation::Attenuation11dB, + ]; + + /// Reference voltage in millivolts + /// + /// Vref = 10 ^ (Att / 20) * Vref0 + /// where Vref0 = 1.1 V, Att - attenuation in dB + /// + /// To convert raw value to millivolts use formula: + /// V = D * Vref / 2 ^ R + /// where D - raw ADC value, R - resolution in bits + pub const fn ref_mv(&self) -> u16 { + match self { + Attenuation::Attenuation0dB => 1100, + Attenuation::Attenuation2p5dB => 1467, + Attenuation::Attenuation6dB => 2195, + Attenuation::Attenuation11dB => 3903, + } + } +} + +pub struct AdcPin { pub pin: PIN, + pub cal_scheme: CS, _phantom: PhantomData, } -impl, ADCI> Channel for AdcPin { +impl Channel for AdcPin +where + PIN: Channel, +{ type ID = u8; fn channel() -> Self::ID { @@ -50,18 +161,82 @@ where Self::default() } - pub fn enable_pin>( + pub fn enable_pin(&mut self, pin: PIN, attenuation: Attenuation) -> AdcPin + where + PIN: Channel, + { + self.attenuations[PIN::channel() as usize] = Some(attenuation); + + AdcPin { + pin, + cal_scheme: AdcCalScheme::<()>::new_cal(attenuation), + _phantom: PhantomData::default(), + } + } + + pub fn enable_pin_with_cal( &mut self, pin: PIN, attenuation: Attenuation, - ) -> AdcPin { + ) -> AdcPin + where + ADCI: CalibrationAccess, + PIN: Channel, + CS: AdcCalScheme, + { self.attenuations[PIN::channel() as usize] = Some(attenuation); AdcPin { pin, + cal_scheme: CS::new_cal(attenuation), _phantom: PhantomData::default(), } } + + /// Calibrate ADC with specified attenuation and voltage source + pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16 + where + ADCI: CalibrationAccess, + { + let mut adc_max: u16 = 0; + let mut adc_min: u16 = u16::MAX; + let mut adc_sum: u32 = 0; + + ADCI::enable_vdef(true); + + // Start sampling + ADCI::adc_samplecfg(ADCI::ADC_CAL_CHANNEL); + ADCI::set_attenuation(ADCI::ADC_CAL_CHANNEL as usize, atten as u8); + + // Connect calibration source + ADCI::connect_cal(source, true); + + for _ in 0..ADCI::ADC_CAL_CNT_MAX { + ADCI::set_init_code(0); + + // Trigger ADC sampling + ADCI::start_sample(); + + // Wait until ADC1 sampling is done + while !ADCI::is_done() {} + + let adc = ADCI::read_data() & ADCI::ADC_VAL_MASK; + + ADCI::reset(); + + adc_sum += adc as u32; + adc_max = adc.max(adc_max); + adc_min = adc.min(adc_min); + } + + let cal_val = + (adc_sum - adc_max as u32 - adc_min as u32) as u16 / (ADCI::ADC_CAL_CNT_MAX - 2); + + // Disconnect calibration source + ADCI::connect_cal(source, false); + + cal_val + } } impl Default for AdcConfig { @@ -74,11 +249,15 @@ impl Default for AdcConfig { } } +#[derive(Clone, Copy)] +pub enum AdcCalSource { + Gnd, + Ref, +} + #[doc(hidden)] pub trait RegisterAccess { - fn set_bit_width(resolution: u8); - - fn set_sample_bit(resolution: u8); + fn adc_samplecfg(channel: u16); fn set_attenuation(channel: usize, attenuation: u8); @@ -90,22 +269,52 @@ pub trait RegisterAccess { fn set_en_pad(channel: u8); - fn clear_start_sar(); + fn clear_start_sample(); + + fn start_sample(); + + /// Check if sampling is done + fn is_done() -> bool; - fn set_start_sar(); + /// Read sample data + fn read_data() -> u16; - fn read_done_sar() -> bool; + /// Set calibration parameter to ADC hardware + fn set_init_code(data: u16); - fn read_data_sar() -> u16; + /// Reset flags + fn reset(); +} + +pub trait CalibrationAccess: RegisterAccess { + const ADC_CAL_CNT_MAX: u16; + const ADC_CAL_CHANNEL: u16; + const ADC_VAL_MASK: u16; + + fn enable_vdef(enable: bool); + + /// Enable internal calibration voltage source + fn connect_cal(source: AdcCalSource, enable: bool); } impl RegisterAccess for ADC1 { - fn set_bit_width(_resolution: u8) { - // no-op - } + fn adc_samplecfg(channel: u16) { + let sensors = unsafe { &*SENS::ptr() }; - fn set_sample_bit(_resolution: u8) { - // no-op + // Configure for RTC control + sensors.sar_meas1_mux.modify(|_r, w| { + w.sar1_dig_force().clear_bit() // 1: Select digital control; + // 0: Select RTC control. + }); + sensors.sar_meas1_ctrl2.modify(|_r, w| { + w.meas1_start_force() + .set_bit() // 1: SW control RTC ADC start; 0: ULP control RTC ADC start. + .sar1_en_pad_force() + .set_bit() // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map; + // Enable internal connect GND (for calibration). + .sar1_en_pad() + .variant(channel) // only one channel is selected. + }); } fn set_attenuation(channel: usize, attenuation: u8) { @@ -146,38 +355,93 @@ impl RegisterAccess for ADC1 { .modify(|_, w| unsafe { w.sar1_en_pad().bits(1 << channel) }); } - fn clear_start_sar() { + fn clear_start_sample() { let sensors = unsafe { &*SENS::ptr() }; sensors .sar_meas1_ctrl2 .modify(|_, w| w.meas1_start_sar().clear_bit()); } - fn set_start_sar() { + fn start_sample() { let sensors = unsafe { &*SENS::ptr() }; sensors .sar_meas1_ctrl2 .modify(|_, w| w.meas1_start_sar().set_bit()); } - fn read_done_sar() -> bool { + fn is_done() -> bool { let sensors = unsafe { &*SENS::ptr() }; sensors.sar_meas1_ctrl2.read().meas1_done_sar().bit_is_set() } - fn read_data_sar() -> u16 { + fn read_data() -> u16 { let sensors = unsafe { &*SENS::ptr() }; sensors.sar_meas1_ctrl2.read().meas1_data_sar().bits() as u16 } + + fn set_init_code(data: u16) { + let [msb, lsb] = data.to_be_bytes(); + + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_INITIAL_CODE_HIGH_ADDR, msb as u32); + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_INITIAL_CODE_LOW_ADDR, lsb as u32); + } + + fn reset() { + let adc = unsafe { &*APB_SARADC::ptr() }; + let sensors = unsafe { &*SENS::ptr() }; + + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + adc.int_clr + .write(|w| w.adc1_done_int_clr().set_bit()); + } else { + adc.int_clr + .write(|w| w.apb_saradc1_done_int_clr().set_bit()); + } + } + + sensors + .sar_meas1_ctrl2 + .modify(|_, w| w.meas1_start_sar().clear_bit()); + } } -impl RegisterAccess for ADC2 { - fn set_bit_width(_resolution: u8) { - // no-op +#[cfg(esp32s3)] +impl CalibrationAccess for ADC1 { + const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX; + const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL; + const ADC_VAL_MASK: u16 = ADC_VAL_MASK; + + fn enable_vdef(enable: bool) { + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_DREF_ADDR, enable as u8); + } + + fn connect_cal(source: AdcCalSource, enable: bool) { + match source { + AdcCalSource::Gnd => { + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR1_ENCAL_GND_ADDR, enable as u8); + } + AdcCalSource::Ref => { + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SARADC1_ENCAL_REF_ADDR, enable as u8); + } + } } +} - fn set_sample_bit(_resolution: u8) { - // no-op +impl RegisterAccess for ADC2 { + fn adc_samplecfg(channel: u16) { + let sensors = unsafe { &*SENS::ptr() }; + + // Configure for RTC control + sensors.sar_meas2_ctrl2.modify(|_r, w| { + w.meas2_start_force() + .set_bit() // 1: SW control RTC ADC start; 0: ULP control RTC ADC start. + .sar2_en_pad_force() + .set_bit() // 1: SW control RTC ADC bit map; 0: ULP control RTC ADC bit map; + // Enable internal connect GND (for calibration). + .sar2_en_pad() + .variant(channel) // only one channel is selected. + }); } fn set_attenuation(channel: usize, attenuation: u8) { @@ -223,29 +487,77 @@ impl RegisterAccess for ADC2 { .modify(|_, w| unsafe { w.sar2_en_pad().bits(1 << channel) }); } - fn clear_start_sar() { + fn clear_start_sample() { let sensors = unsafe { &*SENS::ptr() }; sensors .sar_meas2_ctrl2 .modify(|_, w| w.meas2_start_sar().clear_bit()); } - fn set_start_sar() { + fn start_sample() { let sensors = unsafe { &*SENS::ptr() }; sensors .sar_meas2_ctrl2 .modify(|_, w| w.meas2_start_sar().set_bit()); } - fn read_done_sar() -> bool { + fn is_done() -> bool { let sensors = unsafe { &*SENS::ptr() }; sensors.sar_meas2_ctrl2.read().meas2_done_sar().bit_is_set() } - fn read_data_sar() -> u16 { + fn read_data() -> u16 { let sensors = unsafe { &*SENS::ptr() }; sensors.sar_meas2_ctrl2.read().meas2_data_sar().bits() as u16 } + + fn set_init_code(data: u16) { + let [msb, lsb] = data.to_be_bytes(); + + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_INITIAL_CODE_HIGH_ADDR, msb as u32); + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_INITIAL_CODE_LOW_ADDR, lsb as u32); + } + + fn reset() { + let adc = unsafe { &*APB_SARADC::ptr() }; + let sensors = unsafe { &*SENS::ptr() }; + + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + adc.int_clr + .write(|w| w.adc2_done_int_clr().set_bit()); + } else { + adc.int_clr + .write(|w| w.apb_saradc2_done_int_clr().set_bit()); + } + } + + sensors + .sar_meas2_ctrl2 + .modify(|_, w| w.meas2_start_sar().clear_bit()); + } +} + +#[cfg(esp32s3)] +impl CalibrationAccess for ADC2 { + const ADC_CAL_CNT_MAX: u16 = ADC_CAL_CNT_MAX; + const ADC_CAL_CHANNEL: u16 = ADC_CAL_CHANNEL; + const ADC_VAL_MASK: u16 = ADC_VAL_MASK; + + fn enable_vdef(enable: bool) { + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_DREF_ADDR, enable as u8); + } + + fn connect_cal(source: AdcCalSource, enable: bool) { + match source { + AdcCalSource::Gnd => { + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SAR2_ENCAL_GND_ADDR, enable as u8); + } + AdcCalSource::Ref => { + crate::regi2c_write_mask!(I2C_SAR_ADC, ADC_SARADC2_ENCAL_REF_ADDR, enable as u8); + } + } + } } pub struct ADC<'d, ADC> { @@ -264,12 +576,6 @@ where ) -> Result { let sensors = unsafe { &*SENS::ptr() }; - // Set reading and sampling resolution - let resolution: u8 = config.resolution as u8; - - ADCI::set_bit_width(resolution); - ADCI::set_sample_bit(resolution); - // Set attenuation for pins let attenuations = config.attenuations; @@ -342,15 +648,46 @@ where } } -impl<'d, ADCI, WORD, PIN> OneShot> for ADC<'d, ADCI> +#[cfg(esp32s3)] +impl AdcCalEfuse for ADC1 { + fn get_init_code(atten: Attenuation) -> Option { + Efuse::get_rtc_calib_init_code(1, atten) + } + + fn get_cal_mv(atten: Attenuation) -> u16 { + Efuse::get_rtc_calib_cal_mv(1, atten) + } + + fn get_cal_code(atten: Attenuation) -> Option { + Efuse::get_rtc_calib_cal_code(1, atten) + } +} + +#[cfg(esp32s3)] +impl AdcCalEfuse for ADC2 { + fn get_init_code(atten: Attenuation) -> Option { + Efuse::get_rtc_calib_init_code(2, atten) + } + + fn get_cal_mv(atten: Attenuation) -> u16 { + Efuse::get_rtc_calib_cal_mv(2, atten) + } + + fn get_cal_code(atten: Attenuation) -> Option { + Efuse::get_rtc_calib_cal_code(2, atten) + } +} + +impl<'d, ADCI, WORD, PIN, CS> OneShot> for ADC<'d, ADCI> where WORD: From, PIN: Channel, ADCI: RegisterAccess, + CS: AdcCalScheme, { type Error = (); - fn read(&mut self, _pin: &mut AdcPin) -> nb::Result { + fn read(&mut self, pin: &mut AdcPin) -> nb::Result { if self.attenuations[AdcPin::::channel() as usize] == None { panic!( "Channel {} is not configured reading!", @@ -369,20 +706,24 @@ where // If no conversions are in progress, start a new one for given channel self.active_channel = Some(AdcPin::::channel()); + // Set ADC unit calibration according used scheme for pin + ADCI::set_init_code(pin.cal_scheme.adc_cal()); + ADCI::set_en_pad(AdcPin::::channel() as u8); - ADCI::clear_start_sar(); - ADCI::set_start_sar(); + ADCI::clear_start_sample(); + ADCI::start_sample(); } // Wait for ADC to finish conversion - let conversion_finished = ADCI::read_done_sar(); + let conversion_finished = ADCI::is_done(); if !conversion_finished { return Err(nb::Error::WouldBlock); } // Get converted value - let converted_value = ADCI::read_data_sar(); + let converted_value = ADCI::read_data(); + ADCI::reset(); // Mark that no conversions are currently in progress self.active_channel = None; diff --git a/esp-hal-common/src/clock/clocks_ll/esp32c2.rs b/esp-hal-common/src/clock/clocks_ll/esp32c2.rs index fed446f9743..1c65a19b36f 100644 --- a/esp-hal-common/src/clock/clocks_ll/esp32c2.rs +++ b/esp-hal-common/src/clock/clocks_ll/esp32c2.rs @@ -2,7 +2,6 @@ use crate::{ clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock}, regi2c_write, regi2c_write_mask, - rom::{rom_i2c_writeReg, rom_i2c_writeReg_Mask}, }; extern "C" { @@ -43,18 +42,18 @@ const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 3; pub(crate) fn esp32c2_rtc_bbpll_configure(xtal_freq: XtalClock, _pll_freq: PllClock) { let system = unsafe { &*crate::peripherals::SYSTEM::ptr() }; - unsafe { - let div_ref: u32; - let div7_0: u32; - let dr1: u32; - let dr3: u32; - let dchgp: u32; - let dcur: u32; - let dbias: u32; - let i2c_bbpll_lref: u32; - let i2c_bbpll_div_7_0: u32; - let i2c_bbpll_dcur: u32; + let div_ref: u32; + let div7_0: u32; + let dr1: u32; + let dr3: u32; + let dchgp: u32; + let dcur: u32; + let dbias: u32; + let i2c_bbpll_lref: u32; + let i2c_bbpll_div_7_0: u32; + let i2c_bbpll_dcur: u32; + unsafe { let clear_reg_mask = |reg, mask: u32| { (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask) }; @@ -64,53 +63,52 @@ pub(crate) fn esp32c2_rtc_bbpll_configure(xtal_freq: XtalClock, _pll_freq: PllCl clear_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH); set_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW); + } - // Set this register to let the digital part know 480M PLL is used - system - .cpu_per_conf - .modify(|_, w| w.pll_freq_sel().set_bit()); - - // Configure 480M PLL - match xtal_freq { - XtalClock::RtcXtalFreq26M => { - div_ref = 12; - div7_0 = 236; - dr1 = 4; - dr3 = 4; - dchgp = 0; - dcur = 0; - dbias = 2; - } - XtalClock::RtcXtalFreq40M | XtalClock::RtcXtalFreqOther(_) => { - div_ref = 0; - div7_0 = 8; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } + // Set this register to let the digital part know 480M PLL is used + system + .cpu_per_conf + .modify(|_, w| w.pll_freq_sel().set_bit()); + + // Configure 480M PLL + match xtal_freq { + XtalClock::RtcXtalFreq26M => { + div_ref = 12; + div7_0 = 236; + dr1 = 4; + dr3 = 4; + dchgp = 0; + dcur = 0; + dbias = 2; } + XtalClock::RtcXtalFreq40M | XtalClock::RtcXtalFreqOther(_) => { + div_ref = 0; + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; + } + } - regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b); - i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; - i2c_bbpll_div_7_0 = div7_0; - i2c_bbpll_dcur = - (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; + i2c_bbpll_div_7_0 = div7_0; + i2c_bbpll_dcur = (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; - regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); - regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); - regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias); - } + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias); } pub(crate) fn esp32c2_rtc_bbpll_enable() { diff --git a/esp-hal-common/src/clock/clocks_ll/esp32c3.rs b/esp-hal-common/src/clock/clocks_ll/esp32c3.rs index e9243b9465b..3bde5f89820 100644 --- a/esp-hal-common/src/clock/clocks_ll/esp32c3.rs +++ b/esp-hal-common/src/clock/clocks_ll/esp32c3.rs @@ -2,7 +2,6 @@ use crate::{ clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock}, regi2c_write, regi2c_write_mask, - rom::{rom_i2c_writeReg, rom_i2c_writeReg_Mask}, }; extern "C" { @@ -47,18 +46,17 @@ const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 2; pub(crate) fn esp32c3_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClock) { let system = unsafe { &*crate::peripherals::SYSTEM::ptr() }; + let div_ref: u32; + let div7_0: u32; + let dr1: u32; + let dr3: u32; + let dchgp: u32; + let dcur: u32; + let dbias: u32; + let i2c_bbpll_lref: u32; + let i2c_bbpll_div_7_0: u32; + let i2c_bbpll_dcur: u32; unsafe { - let div_ref: u32; - let div7_0: u32; - let dr1: u32; - let dr3: u32; - let dchgp: u32; - let dcur: u32; - let dbias: u32; - let i2c_bbpll_lref: u32; - let i2c_bbpll_div_7_0: u32; - let i2c_bbpll_dcur: u32; - let clear_reg_mask = |reg, mask: u32| { (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask) }; @@ -68,110 +66,109 @@ pub(crate) fn esp32c3_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClo clear_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH); set_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW); + } - if matches!(pll_freq, PllClock::Pll480MHz) { - // Set this register to let the digital part know 480M PLL is used - system - .cpu_per_conf - .modify(|_, w| w.pll_freq_sel().set_bit()); - - // Configure 480M PLL - match xtal_freq { - XtalClock::RtcXtalFreq40M => { - div_ref = 0; - div7_0 = 8; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } - - XtalClock::RtcXtalFreq32M => { - div_ref = 1; - div7_0 = 26; - dr1 = 1; - dr3 = 1; - dchgp = 4; - dcur = 0; - dbias = 2; - } - - XtalClock::RtcXtalFreqOther(_) => { - div_ref = 0; - div7_0 = 8; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } + if matches!(pll_freq, PllClock::Pll480MHz) { + // Set this register to let the digital part know 480M PLL is used + system + .cpu_per_conf + .modify(|_, w| w.pll_freq_sel().set_bit()); + + // Configure 480M PLL + match xtal_freq { + XtalClock::RtcXtalFreq40M => { + div_ref = 0; + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; } - regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b); - } else { - // Clear this register to let the digital part know 320M PLL is used - system - .cpu_per_conf - .modify(|_, w| w.pll_freq_sel().clear_bit()); - - // Configure 320M PLL - match xtal_freq { - XtalClock::RtcXtalFreq40M => { - div_ref = 0; - div7_0 = 4; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } - - XtalClock::RtcXtalFreq32M => { - div_ref = 1; - div7_0 = 6; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } - - XtalClock::RtcXtalFreqOther(_) => { - div_ref = 0; - div7_0 = 4; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } + XtalClock::RtcXtalFreq32M => { + div_ref = 1; + div7_0 = 26; + dr1 = 1; + dr3 = 1; + dchgp = 4; + dcur = 0; + dbias = 2; } - regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69); + XtalClock::RtcXtalFreqOther(_) => { + div_ref = 0; + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; + } } - i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; - i2c_bbpll_div_7_0 = div7_0; - i2c_bbpll_dcur = - (2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b); + } else { + // Clear this register to let the digital part know 320M PLL is used + system + .cpu_per_conf + .modify(|_, w| w.pll_freq_sel().clear_bit()); + + // Configure 320M PLL + match xtal_freq { + XtalClock::RtcXtalFreq40M => { + div_ref = 0; + div7_0 = 4; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; + } - regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); + XtalClock::RtcXtalFreq32M => { + div_ref = 1; + div7_0 = 6; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; + } - regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); + XtalClock::RtcXtalFreqOther(_) => { + div_ref = 0; + div7_0 = 4; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; + } + } - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69); + } - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); + i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; + i2c_bbpll_div_7_0 = div7_0; + i2c_bbpll_dcur = (2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; - regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias); + regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2); + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); - regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1); - } + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); + + regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias); + + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2); + + regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1); } pub(crate) fn esp32c3_rtc_bbpll_enable() { diff --git a/esp-hal-common/src/rom/mod.rs b/esp-hal-common/src/rom/mod.rs index c38e7ee5fe0..c87c3cecbe1 100644 --- a/esp-hal-common/src/rom/mod.rs +++ b/esp-hal-common/src/rom/mod.rs @@ -25,11 +25,14 @@ extern "C" { macro_rules! regi2c_write { ( $block: ident, $reg_add: ident, $indata: expr ) => { paste::paste! { - rom_i2c_writeReg($block, - [<$block _HOSTID>], - $reg_add, - $indata - ); + unsafe { + crate::rom::rom_i2c_writeReg( + $block as u32, + [<$block _HOSTID>] as u32, + $reg_add as u32, + $indata as u32 + ) + } } }; } @@ -39,13 +42,16 @@ macro_rules! regi2c_write { macro_rules! regi2c_write_mask { ( $block: ident, $reg_add: ident, $indata: expr ) => { paste::paste! { - rom_i2c_writeReg_Mask($block, - [<$block _HOSTID>], - $reg_add, - [<$reg_add _MSB>], - [<$reg_add _LSB>], - $indata - ); + unsafe { + crate::rom::rom_i2c_writeReg_Mask( + $block as u32, + [<$block _HOSTID>] as u32, + $reg_add as u32, + [<$reg_add _MSB>] as u32, + [<$reg_add _LSB>] as u32, + $indata as u32 + ) + } } }; } diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32c2.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32c2.rs index e764a651885..5f94c655da6 100644 --- a/esp-hal-common/src/rtc_cntl/rtc/esp32c2.rs +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32c2.rs @@ -4,7 +4,6 @@ use crate::{ clock::XtalClock, peripherals::{APB_CTRL, EXTMEM, RTC_CNTL, SPI0, SPI1, SYSTEM}, regi2c_write_mask, - rom::rom_i2c_writeReg_Mask, rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock}, }; @@ -29,10 +28,8 @@ const I2C_ULP_IR_FORCE_XPD_CK_LSB: u32 = 2; pub(crate) fn init() { let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; - unsafe { - regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0); - regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0); - } + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0); + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0); unsafe { rtc_cntl @@ -53,9 +50,9 @@ pub(crate) fn init() { unsafe { rtc_cntl.int_ena_rtc.write(|w| w.bits(0)); rtc_cntl.int_clr_rtc.write(|w| w.bits(u32::MAX)); - - regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0); } + + regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0); } pub(crate) fn configure_clock() { diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32c3.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32c3.rs index 84ab372b797..9742842b656 100644 --- a/esp-hal-common/src/rtc_cntl/rtc/esp32c3.rs +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32c3.rs @@ -4,7 +4,6 @@ use crate::{ clock::XtalClock, peripherals::{APB_CTRL, EXTMEM, RTC_CNTL, SPI0, SPI1, SYSTEM}, regi2c_write_mask, - rom::rom_i2c_writeReg_Mask, rtc_cntl::{RtcCalSel, RtcClock, RtcFastClock, RtcSlowClock}, }; @@ -29,11 +28,9 @@ const I2C_ULP_IR_FORCE_XPD_CK_LSB: u32 = 2; pub(crate) fn init() { let rtc_cntl = unsafe { &*RTC_CNTL::ptr() }; - unsafe { - regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0); + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_DIG_REG, 0); - regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0); - } + regi2c_write_mask!(I2C_DIG_REG, I2C_DIG_REG_XPD_RTC_REG, 0); rtc_cntl.ana_conf.modify(|_, w| w.pvtmon_pu().clear_bit()); @@ -83,9 +80,9 @@ pub(crate) fn init() { unsafe { rtc_cntl.int_ena_rtc.write(|w| w.bits(0)); rtc_cntl.int_clr_rtc.write(|w| w.bits(u32::MAX)); - - regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0); } + + regi2c_write_mask!(I2C_ULP, I2C_ULP_IR_FORCE_XPD_CK, 0); } pub(crate) fn configure_clock() { diff --git a/esp-hal-common/src/soc/esp32s3/efuse.rs b/esp-hal-common/src/soc/esp32s3/efuse.rs index 300dff12555..7bf79e2a9ac 100644 --- a/esp-hal-common/src/soc/esp32s3/efuse.rs +++ b/esp-hal-common/src/soc/esp32s3/efuse.rs @@ -1,7 +1,7 @@ //! Reading of eFuses -use crate::peripherals::EFUSE; pub use crate::soc::efuse_field::*; +use crate::{analog::adc::Attenuation, peripherals::EFUSE}; pub struct Efuse; @@ -36,6 +36,135 @@ impl Efuse { pub fn get_rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } + + /// Get efuse block version + /// + /// see + pub fn get_block_version() -> (u8, u8) { + // see + // + ( + Self::read_field_le::(BLK_VERSION_MAJOR), + Self::read_field_le::(BLK_VERSION_MINOR), + ) + } + + /// Get version of RTC calibration block + /// + /// see + pub fn get_rtc_calib_version() -> u8 { + let (major, _minor) = Self::get_block_version(); + + if major == 1 { + 1 + } else { + 0 + } + } + + /// Get ADC initial code for specified attenuation from efuse + /// + /// see + pub fn get_rtc_calib_init_code(unit: u8, atten: Attenuation) -> Option { + let version = Self::get_rtc_calib_version(); + + if version != 1 { + return None; + } + + let adc_icode_diff: [u16; 4] = if unit == 0 { + [ + Self::read_field_le(ADC1_INIT_CODE_ATTEN0), + Self::read_field_le(ADC1_INIT_CODE_ATTEN1), + Self::read_field_le(ADC1_INIT_CODE_ATTEN2), + Self::read_field_le(ADC1_INIT_CODE_ATTEN3), + ] + } else { + [ + Self::read_field_le(ADC2_INIT_CODE_ATTEN0), + Self::read_field_le(ADC2_INIT_CODE_ATTEN1), + Self::read_field_le(ADC2_INIT_CODE_ATTEN2), + Self::read_field_le(ADC2_INIT_CODE_ATTEN3), + ] + }; + + // Version 1 logic for calculating ADC ICode based on EFUSE burnt value + + let mut adc_icode = [0; 4]; + if unit == 0 { + adc_icode[0] = adc_icode_diff[0] + 1850; + adc_icode[1] = adc_icode_diff[1] + adc_icode[0] + 90; + adc_icode[2] = adc_icode_diff[2] + adc_icode[1]; + adc_icode[3] = adc_icode_diff[3] + adc_icode[2] + 70; + } else { + adc_icode[0] = adc_icode_diff[0] + 2020; + adc_icode[1] = adc_icode_diff[1] + adc_icode[0]; + adc_icode[2] = adc_icode_diff[2] + adc_icode[1]; + adc_icode[3] = adc_icode_diff[3] + adc_icode[2]; + } + + Some( + adc_icode[match atten { + Attenuation::Attenuation0dB => 0, + Attenuation::Attenuation2p5dB => 1, + Attenuation::Attenuation6dB => 2, + Attenuation::Attenuation11dB => 3, + }], + ) + } + + /// Get ADC reference point voltage for specified attenuation in millivolts + /// + /// see + pub fn get_rtc_calib_cal_mv(_unit: u8, _atten: Attenuation) -> u16 { + 850 + } + + /// Get ADC reference point digital code for specified attenuation + /// + /// see + pub fn get_rtc_calib_cal_code(unit: u8, atten: Attenuation) -> Option { + let version = Self::get_rtc_calib_version(); + + if version != 1 { + return None; + } + + let adc_vol_diff: [u16; 8] = [ + Self::read_field_le(ADC1_CAL_VOL_ATTEN0), + Self::read_field_le(ADC1_CAL_VOL_ATTEN1), + Self::read_field_le(ADC1_CAL_VOL_ATTEN2), + Self::read_field_le(ADC1_CAL_VOL_ATTEN3), + Self::read_field_le(ADC2_CAL_VOL_ATTEN0), + Self::read_field_le(ADC2_CAL_VOL_ATTEN1), + Self::read_field_le(ADC2_CAL_VOL_ATTEN2), + Self::read_field_le(ADC2_CAL_VOL_ATTEN3), + ]; + + let mut adc1_vol = [0; 4]; + let mut adc2_vol = [0; 4]; + adc1_vol[3] = adc_vol_diff[3] + 900; + adc1_vol[2] = adc_vol_diff[2] + adc1_vol[3] + 800; + adc1_vol[1] = adc_vol_diff[1] + adc1_vol[2] + 700; + adc1_vol[0] = adc_vol_diff[0] + adc1_vol[1] + 800; + adc2_vol[3] = adc1_vol[3] - adc_vol_diff[7] + 15; + adc2_vol[2] = adc1_vol[2] - adc_vol_diff[6] + 20; + adc2_vol[1] = adc1_vol[1] - adc_vol_diff[5] + 10; + adc2_vol[0] = adc1_vol[0] - adc_vol_diff[4] + 40; + + let atten = match atten { + Attenuation::Attenuation0dB => 0, + Attenuation::Attenuation2p5dB => 1, + Attenuation::Attenuation6dB => 2, + Attenuation::Attenuation11dB => 3, + }; + + Some(if unit == 0 { + adc1_vol[atten] + } else { + adc2_vol[atten] + }) + } } #[derive(Copy, Clone)] diff --git a/esp32s3-hal/examples/adc_cal.rs b/esp32s3-hal/examples/adc_cal.rs new file mode 100644 index 00000000000..6df866081c8 --- /dev/null +++ b/esp32s3-hal/examples/adc_cal.rs @@ -0,0 +1,66 @@ +//! Connect a potentiometer to PIN3 and see the read values change when +//! rotating the shaft. Alternatively you could also connect the PIN to GND or +//! 3V3 to see the maximum and minimum raw values read. + +#![no_std] +#![no_main] + +use esp32s3_hal::{ + adc::{self, AdcConfig, Attenuation, ADC, ADC1}, + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable MWDT and RWDT (Watchdog) flash boot protection + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Create ADC instances + let analog = peripherals.SENS.split(); + + let mut adc1_config = AdcConfig::new(); + + let atten = Attenuation::Attenuation11dB; + + // You can try any of the following calibration methods by uncommenting them + // type AdcCal = (); + // type AdcCal = adc::AdcCalBasic; + // type AdcCal = adc::AdcCalLine; + type AdcCal = adc::AdcCalCurve; + + let mut pin = adc1_config.enable_pin_with_cal::<_, AdcCal>(io.pins.gpio3.into_analog(), atten); + + let mut adc1 = ADC::::adc(analog.adc1, adc1_config).unwrap(); + + let mut delay = Delay::new(&clocks); + + loop { + let pin_value: u16 = nb::block!(adc1.read(&mut pin)).unwrap(); + let pin_value_mv = pin_value as u32 * atten.ref_mv() as u32 / 4096; + println!("PIN2 ADC reading = {pin_value} ({pin_value_mv} mV)"); + delay.delay_ms(1500u32); + } +}