use core::marker::PhantomData;
cfg_if::cfg_if! {
if #[cfg(esp32c6)] {
use Interrupt::APB_SARADC as InterruptSource;
} else {
use Interrupt::APB_ADC as InterruptSource;
}
}
use core::{
pin::Pin,
task::{Context, Poll},
};
#[cfg(all(adc_adc1, adc_adc2))]
use portable_atomic::{AtomicU32, Ordering};
use procmacros::handler;
pub use self::calibration::*;
use super::{AdcCalSource, AdcConfig, Attenuation};
#[cfg(any(esp32c2, esp32c3, esp32c5, esp32c6, esp32h2))]
use crate::efuse::AdcCalibUnit;
use crate::{
Async,
Blocking,
asynch::AtomicWaker,
interrupt::{InterruptConfigurable, InterruptHandler},
peripherals::{APB_SARADC, Interrupt},
soc::regi2c,
system::{GenericPeripheralGuard, Peripheral},
};
mod calibration;
cfg_if::cfg_if! {
if #[cfg(adc_adc1)] {
const ADC_VAL_MASK: u16 = 0xfff;
const ADC_CAL_CNT_MAX: u16 = 32;
const ADC_CAL_CHANNEL: u16 = 15;
}
}
cfg_if::cfg_if! {
if #[cfg(esp32c6)] {
pub(super) const NUM_ATTENS: usize = 7;
} else if #[cfg(esp32c5)] {
pub(super) const NUM_ATTENS: usize = 6;
} else {
pub(super) const NUM_ATTENS: usize = 5;
}
}
impl<ADCI> AdcConfig<ADCI>
where
ADCI: RegisterAccess,
{
pub fn adc_calibrate(atten: Attenuation, source: AdcCalSource) -> u16
where
ADCI: super::CalibrationAccess,
{
let mut adc_max: u16 = 0;
let mut adc_min: u16 = u16::MAX;
let mut adc_sum: u32 = 0;
ADCI::enable_vdef(true);
ADCI::config_onetime_sample(ADC_CAL_CHANNEL as u8, atten as u8);
ADCI::connect_cal(source, true);
ADCI::calibration_init();
for _ in 0..ADC_CAL_CNT_MAX {
ADCI::set_init_code(0);
ADCI::start_onetime_sample();
while !ADCI::is_done() {}
let adc = ADCI::read_data() & 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 / (ADC_CAL_CNT_MAX - 2);
ADCI::connect_cal(source, false);
cal_val
}
}
#[doc(hidden)]
pub trait RegisterAccess {
fn config_onetime_sample(channel: u8, attenuation: u8);
fn start_onetime_sample();
fn is_done() -> bool;
fn read_data() -> u16;
fn reset();
fn calibration_init();
fn set_init_code(data: u16);
}
#[cfg(adc_adc1)]
impl RegisterAccess for crate::peripherals::ADC1<'_> {
fn config_onetime_sample(channel: u8, attenuation: u8) {
APB_SARADC::regs().onetime_sample().modify(|_, w| unsafe {
w.saradc1_onetime_sample().set_bit();
w.onetime_channel().bits(channel);
w.onetime_atten().bits(attenuation)
});
}
fn start_onetime_sample() {
APB_SARADC::regs()
.onetime_sample()
.modify(|_, w| w.onetime_start().set_bit());
}
fn is_done() -> bool {
APB_SARADC::regs().int_raw().read().adc1_done().bit()
}
fn read_data() -> u16 {
APB_SARADC::regs()
.sar1data_status()
.read()
.saradc1_data()
.bits() as u16
& 0xfff
}
fn reset() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.adc1_done().clear_bit_by_one());
APB_SARADC::regs()
.onetime_sample()
.modify(|_, w| w.onetime_start().clear_bit());
}
#[cfg(any(esp32c2, esp32c3, esp32c5, esp32c6, esp32h2))]
fn calibration_init() {
regi2c::ADC_SAR1_DREF.write_field(1);
}
fn set_init_code(data: u16) {
let [msb, lsb] = data.to_be_bytes();
regi2c::ADC_SAR1_INITIAL_CODE_HIGH.write_field(msb);
regi2c::ADC_SAR1_INITIAL_CODE_LOW.write_field(lsb);
}
}
#[cfg(adc_adc1)]
impl super::CalibrationAccess for crate::peripherals::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) {
regi2c::ADC_SAR1_DREF.write_field(enable as _);
}
fn connect_cal(source: AdcCalSource, enable: bool) {
match source {
AdcCalSource::Gnd => regi2c::ADC_SAR1_ENCAL_GND.write_field(enable as _),
#[cfg(not(esp32h2))]
AdcCalSource::Ref => regi2c::ADC_SAR1_ENCAL_REF.write_field(enable as _),
#[cfg(esp32h2)]
AdcCalSource::Ref => regi2c::ADC_SAR1_ENCAL_GND.write_field(!enable as _),
}
}
}
#[cfg(adc_adc2)]
impl RegisterAccess for crate::peripherals::ADC2<'_> {
fn config_onetime_sample(channel: u8, attenuation: u8) {
APB_SARADC::regs().onetime_sample().modify(|_, w| unsafe {
w.saradc2_onetime_sample().set_bit();
w.onetime_channel().bits(channel);
w.onetime_atten().bits(attenuation)
});
}
fn start_onetime_sample() {
APB_SARADC::regs()
.onetime_sample()
.modify(|_, w| w.onetime_start().set_bit());
}
fn is_done() -> bool {
APB_SARADC::regs().int_raw().read().adc2_done().bit()
}
fn read_data() -> u16 {
APB_SARADC::regs()
.sar2data_status()
.read()
.saradc2_data()
.bits() as u16
& 0xfff
}
fn reset() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.adc2_done().clear_bit_by_one());
APB_SARADC::regs()
.onetime_sample()
.modify(|_, w| w.onetime_start().clear_bit());
}
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
fn calibration_init() {
regi2c::ADC_SAR2_DREF.write_field(1);
}
fn set_init_code(data: u16) {
let [msb, lsb] = data.to_be_bytes();
regi2c::ADC_SAR2_INITIAL_CODE_HIGH.write_field(msb as _);
regi2c::ADC_SAR2_INITIAL_CODE_LOW.write_field(lsb as _);
}
}
#[cfg(adc_adc2)]
impl super::CalibrationAccess for crate::peripherals::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) {
regi2c::ADC_SAR2_DREF.write_field(enable as _);
}
fn connect_cal(source: AdcCalSource, enable: bool) {
match source {
AdcCalSource::Gnd => regi2c::ADC_SAR2_ENCAL_GND.write_field(enable as _),
AdcCalSource::Ref => regi2c::ADC_SAR2_ENCAL_REF.write_field(enable as _),
}
}
}
pub struct Adc<'d, ADCI, Dm: crate::DriverMode> {
_adc: ADCI,
attenuations: [Option<Attenuation>; NUM_ATTENS],
active_channel: Option<u8>,
_guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>,
_phantom: PhantomData<(Dm, &'d mut ())>,
}
impl<'d, ADCI> Adc<'d, ADCI, Blocking>
where
ADCI: RegisterAccess + 'd,
{
pub fn new(adc_instance: ADCI, config: AdcConfig<ADCI>) -> Self {
let guard = GenericPeripheralGuard::new();
APB_SARADC::regs().ctrl().modify(|_, w| unsafe {
w.start_force().set_bit();
w.start().set_bit();
w.sar_clk_gated().set_bit();
w.xpd_sar_force().bits(0b11)
});
Adc {
_adc: adc_instance,
attenuations: config.attenuations,
active_channel: None,
_guard: guard,
_phantom: PhantomData,
}
}
pub fn into_async(mut self) -> Adc<'d, ADCI, Async> {
acquire_async_adc();
self.set_interrupt_handler(adc_interrupt_handler);
ADCI::reset();
Adc {
_adc: self._adc,
attenuations: self.attenuations,
active_channel: self.active_channel,
_guard: self._guard,
_phantom: PhantomData,
}
}
pub fn read_oneshot<PIN, CS>(
&mut self,
pin: &mut super::AdcPin<PIN, ADCI, CS>,
) -> nb::Result<u16, ()>
where
PIN: super::AdcChannel,
CS: super::AdcCalScheme<ADCI>,
{
if self.attenuations[pin.pin.adc_channel() as usize].is_none() {
panic!(
"Channel {} is not configured reading!",
pin.pin.adc_channel()
);
}
if let Some(active_channel) = self.active_channel {
if active_channel != pin.pin.adc_channel() {
return Err(nb::Error::WouldBlock);
}
} else {
self.active_channel = Some(pin.pin.adc_channel());
ADCI::calibration_init();
ADCI::set_init_code(pin.cal_scheme.adc_cal());
let channel = self.active_channel.unwrap();
let attenuation = self.attenuations[channel as usize].unwrap() as u8;
ADCI::config_onetime_sample(channel, attenuation);
ADCI::start_onetime_sample();
#[cfg(esp32c6)]
{
crate::rom::ets_delay_us(40);
ADCI::start_onetime_sample();
}
}
let conversion_finished = ADCI::is_done();
if !conversion_finished {
return Err(nb::Error::WouldBlock);
}
let converted_value = ADCI::read_data();
ADCI::reset();
let converted_value = pin.cal_scheme.adc_val(converted_value);
self.active_channel = None;
Ok(converted_value)
}
}
impl<ADCI> crate::private::Sealed for Adc<'_, ADCI, Blocking> {}
impl<ADCI> InterruptConfigurable for Adc<'_, ADCI, Blocking> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
for core in crate::system::Cpu::other() {
crate::interrupt::disable(core, InterruptSource);
}
crate::interrupt::bind_handler(InterruptSource, handler);
}
}
#[cfg(adc_adc1)]
impl super::AdcCalEfuse for crate::peripherals::ADC1<'_> {
fn init_code(atten: Attenuation) -> Option<u16> {
crate::efuse::rtc_calib_init_code(AdcCalibUnit::ADC1, atten)
}
fn cal_mv(atten: Attenuation) -> u16 {
crate::efuse::rtc_calib_cal_mv(AdcCalibUnit::ADC1, atten)
}
fn cal_code(atten: Attenuation) -> Option<u16> {
crate::efuse::rtc_calib_cal_code(AdcCalibUnit::ADC1, atten)
}
#[cfg(esp32c5)]
fn cal_chan_compens(atten: Attenuation, channel: u16) -> Option<i32> {
crate::efuse::rtc_calib_get_chan_compens(AdcCalibUnit::ADC1, channel, atten)
}
}
#[cfg(adc_adc2)]
impl super::AdcCalEfuse for crate::peripherals::ADC2<'_> {
fn init_code(atten: Attenuation) -> Option<u16> {
crate::efuse::rtc_calib_init_code(AdcCalibUnit::ADC2, atten)
}
fn cal_mv(atten: Attenuation) -> u16 {
crate::efuse::rtc_calib_cal_mv(AdcCalibUnit::ADC2, atten)
}
fn cal_code(atten: Attenuation) -> Option<u16> {
crate::efuse::rtc_calib_cal_code(AdcCalibUnit::ADC2, atten)
}
}
impl<'d, ADCI> Adc<'d, ADCI, Async>
where
ADCI: RegisterAccess + 'd,
{
pub fn into_blocking(self) -> Adc<'d, ADCI, Blocking> {
if release_async_adc() {
for cpu in crate::system::Cpu::all() {
crate::interrupt::disable(cpu, InterruptSource);
}
}
Adc {
_adc: self._adc,
attenuations: self.attenuations,
active_channel: self.active_channel,
_guard: self._guard,
_phantom: PhantomData,
}
}
pub async fn read_oneshot<PIN, CS>(&mut self, pin: &mut super::AdcPin<PIN, ADCI, CS>) -> u16
where
ADCI: Instance,
PIN: super::AdcChannel,
CS: super::AdcCalScheme<ADCI>,
{
let channel = pin.pin.adc_channel();
if self.attenuations[channel as usize].is_none() {
panic!("Channel {} is not configured reading!", channel);
}
ADCI::calibration_init();
ADCI::set_init_code(pin.cal_scheme.adc_cal());
let attenuation = self.attenuations[channel as usize].unwrap() as u8;
ADCI::config_onetime_sample(channel, attenuation);
ADCI::start_onetime_sample();
let adc_ready_future = AdcFuture::new(self);
adc_ready_future.await;
let converted_value = ADCI::read_data();
ADCI::reset();
pin.cal_scheme.adc_val(converted_value)
}
}
#[cfg(all(adc_adc1, adc_adc2))]
static ASYNC_ADC_COUNT: AtomicU32 = AtomicU32::new(0);
pub(super) fn acquire_async_adc() {
#[cfg(all(adc_adc1, adc_adc2))]
ASYNC_ADC_COUNT.fetch_add(1, Ordering::Relaxed);
}
pub(super) fn release_async_adc() -> bool {
cfg_if::cfg_if! {
if #[cfg(all(adc_adc1, adc_adc2))] {
ASYNC_ADC_COUNT.fetch_sub(1, Ordering::Relaxed) == 1
} else {
true
}
}
}
#[handler]
pub(crate) fn adc_interrupt_handler() {
let saradc = APB_SARADC::regs();
let interrupt_status = saradc.int_st().read();
#[cfg(adc_adc1)]
if interrupt_status.adc1_done().bit_is_set() {
unsafe { handle_async(crate::peripherals::ADC1::steal()) }
}
#[cfg(adc_adc2)]
if interrupt_status.adc2_done().bit_is_set() {
unsafe { handle_async(crate::peripherals::ADC2::steal()) }
}
}
fn handle_async<ADCI: Instance>(_instance: ADCI) {
ADCI::waker().wake();
ADCI::unlisten();
}
pub trait Instance: crate::private::Sealed {
fn listen();
fn unlisten();
fn clear_interrupt();
fn waker() -> &'static AtomicWaker;
}
#[cfg(adc_adc1)]
impl Instance for crate::peripherals::ADC1<'_> {
fn listen() {
APB_SARADC::regs()
.int_ena()
.modify(|_, w| w.adc1_done().set_bit());
}
fn unlisten() {
APB_SARADC::regs()
.int_ena()
.modify(|_, w| w.adc1_done().clear_bit());
}
fn clear_interrupt() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.adc1_done().clear_bit_by_one());
}
fn waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
}
#[cfg(adc_adc2)]
impl Instance for crate::peripherals::ADC2<'_> {
fn listen() {
APB_SARADC::regs()
.int_ena()
.modify(|_, w| w.adc2_done().set_bit());
}
fn unlisten() {
APB_SARADC::regs()
.int_ena()
.modify(|_, w| w.adc2_done().clear_bit());
}
fn clear_interrupt() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.adc2_done().clear_bit_by_one());
}
fn waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub(crate) struct AdcFuture<ADCI: Instance> {
phantom: PhantomData<ADCI>,
}
impl<ADCI: Instance> AdcFuture<ADCI> {
pub fn new(_self: &super::Adc<'_, ADCI, Async>) -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<ADCI: Instance + super::RegisterAccess> core::future::Future for AdcFuture<ADCI> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if ADCI::is_done() {
ADCI::clear_interrupt();
Poll::Ready(())
} else {
ADCI::waker().register(cx.waker());
ADCI::listen();
Poll::Pending
}
}
}
impl<ADCI: Instance> Drop for AdcFuture<ADCI> {
fn drop(&mut self) {
ADCI::unlisten();
}
}