#![cfg_attr(docsrs, procmacros::doc_replace(
"analog_pin" => {
cfg(esp32) => "GPIO32",
cfg(any(esp32s2, esp32s3)) => "GPIO3",
cfg(not(any(esp32, esp32s2, esp32s3))) => "GPIO2"
}
))]
use core::marker::PhantomData;
use crate::gpio::AnalogPin;
#[cfg_attr(esp32, path = "esp32.rs")]
#[cfg_attr(riscv, path = "riscv.rs")]
#[cfg_attr(any(esp32s2, esp32s3), path = "xtensa.rs")]
#[cfg(feature = "unstable")]
mod implementation;
#[cfg(feature = "unstable")]
pub use self::implementation::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(clippy::enum_variant_names, reason = "unit of measurement")]
pub enum Attenuation {
_0dB = 0b00,
_2p5dB = 0b01,
_6dB = 0b10,
_11dB = 0b11,
}
#[cfg(not(esp32))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcCalSource {
Gnd,
Ref,
}
pub struct AdcPin<PIN, ADCI, CS = ()> {
pub pin: PIN,
pub cal_scheme: CS,
_phantom: PhantomData<ADCI>,
}
impl<PIN: core::fmt::Debug, ADCI, CS: core::fmt::Debug> core::fmt::Debug for AdcPin<PIN, ADCI, CS> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("AdcPin")
.field("pin", &self.pin)
.field("cal_scheme", &self.cal_scheme)
.finish()
}
}
#[cfg(feature = "defmt")]
impl<PIN: defmt::Format, ADCI, CS: defmt::Format> defmt::Format for AdcPin<PIN, ADCI, CS> {
fn format(&self, fmt: defmt::Formatter<'_>) {
defmt::write!(
fmt,
"AdcPin {{ pin: {}, cal_scheme: {} }}",
self.pin,
self.cal_scheme
);
}
}
#[cfg(feature = "unstable")]
pub struct AdcConfig<ADCI> {
#[cfg(esp32)]
resolution: Resolution,
attenuations: [Option<Attenuation>; NUM_ATTENS],
_phantom: PhantomData<ADCI>,
}
#[cfg(feature = "unstable")]
impl<ADCI> AdcConfig<ADCI> {
pub fn new() -> Self {
Self::default()
}
pub fn enable_pin<PIN>(&mut self, pin: PIN, attenuation: Attenuation) -> AdcPin<PIN, ADCI>
where
PIN: AdcChannel + AnalogPin,
{
pin.set_analog(crate::private::Internal);
self.attenuations[pin.adc_channel() as usize] = Some(attenuation);
AdcPin {
pin,
cal_scheme: AdcCalScheme::<()>::new_cal(attenuation),
_phantom: PhantomData,
}
}
#[cfg(not(esp32))]
#[cfg(feature = "unstable")]
pub fn enable_pin_with_cal<PIN, CS>(
&mut self,
pin: PIN,
attenuation: Attenuation,
) -> AdcPin<PIN, ADCI, CS>
where
ADCI: CalibrationAccess,
PIN: AdcChannel + AnalogPin,
CS: AdcCalScheme<ADCI>,
{
pin.set_analog(crate::private::Internal);
self.attenuations[pin.adc_channel() as usize] = Some(attenuation);
AdcPin {
pin,
cal_scheme: CS::new_cal(attenuation),
_phantom: PhantomData,
}
}
}
#[cfg(feature = "unstable")]
impl<ADCI> Default for AdcConfig<ADCI> {
fn default() -> Self {
Self {
#[cfg(esp32)]
resolution: Resolution::default(),
attenuations: [None; NUM_ATTENS],
_phantom: PhantomData,
}
}
}
#[cfg(not(esp32))]
#[doc(hidden)]
#[cfg(feature = "unstable")]
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);
fn connect_cal(source: AdcCalSource, enable: bool);
}
pub trait AdcChannel {
fn adc_channel(&self) -> u8;
}
pub trait AdcCalScheme<ADCI>: Sized + crate::private::Sealed {
fn new_cal(atten: Attenuation) -> Self;
fn adc_cal(&self) -> u16 {
0
}
fn adc_val(&self, val: u16) -> u16 {
val
}
}
impl crate::private::Sealed for () {}
impl<ADCI> AdcCalScheme<ADCI> for () {
fn new_cal(_atten: Attenuation) -> Self {}
}
#[cfg(not(any(esp32, esp32s2)))]
trait AdcCalEfuse {
fn init_code(atten: Attenuation) -> Option<u16>;
fn cal_mv(atten: Attenuation) -> u16;
fn cal_code(atten: Attenuation) -> Option<u16>;
#[cfg(esp32c5)]
fn cal_chan_compens(atten: Attenuation, channel: u16) -> Option<i32>;
}
for_each_analog_function! {
(($ch_name:ident, ADCn_CHm, $adc:literal, $ch:literal), $gpio:ident) => {
impl $crate::analog::adc::AdcChannel for $crate::peripherals::$gpio<'_> {
fn adc_channel(&self) -> u8 {
$ch
}
}
};
}