use core::marker::PhantomData;
use embedded_hal::adc::{Channel, OneShot};
#[cfg(esp32c3)]
use crate::analog::ADC2;
use crate::{
analog::ADC1,
peripheral::PeripheralRef,
peripherals::APB_SARADC,
system::{Peripheral, PeripheralClockControl},
};
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Resolution {
Resolution12Bit,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Attenuation {
Attenuation0dB = 0b00,
Attenuation2p5dB = 0b01,
Attenuation6dB = 0b10,
Attenuation11dB = 0b11,
}
pub struct AdcPin<PIN, ADCI> {
pub pin: PIN,
_phantom: PhantomData<ADCI>,
}
impl<PIN: Channel<ADCI, ID = u8>, ADCI> Channel<ADCI> for AdcPin<PIN, ADCI> {
type ID = u8;
fn channel() -> Self::ID {
PIN::channel()
}
}
pub struct AdcConfig<ADCI> {
pub resolution: Resolution,
pub attenuations: [Option<Attenuation>; 5],
_phantom: PhantomData<ADCI>,
}
impl<ADCI> AdcConfig<ADCI>
where
ADCI: RegisterAccess,
{
pub fn new() -> AdcConfig<ADCI> {
Self::default()
}
pub fn enable_pin<PIN: Channel<ADCI, ID = u8>>(
&mut self,
pin: PIN,
attenuation: Attenuation,
) -> AdcPin<PIN, ADCI> {
self.attenuations[PIN::channel() as usize] = Some(attenuation);
AdcPin {
pin,
_phantom: PhantomData::default(),
}
}
}
impl<ADCI> Default for AdcConfig<ADCI> {
fn default() -> Self {
AdcConfig {
resolution: Resolution::Resolution12Bit,
attenuations: [None; 5],
_phantom: PhantomData::default(),
}
}
}
#[doc(hidden)]
pub trait RegisterAccess {
fn start_onetime_sample(channel: u8, attenuation: u8);
fn is_done() -> bool;
fn read_data() -> u16;
fn reset();
}
impl RegisterAccess for ADC1 {
fn start_onetime_sample(channel: u8, attenuation: u8) {
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc.onetime_sample.modify(|_, w| unsafe {
w.saradc1_onetime_sample()
.set_bit()
.saradc_onetime_channel()
.bits(channel)
.saradc_onetime_atten()
.bits(attenuation)
.saradc_onetime_start()
.set_bit()
});
}
fn is_done() -> bool {
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc.int_raw.read().apb_saradc1_done_int_raw().bit()
}
fn read_data() -> u16 {
let sar_adc = unsafe { &*APB_SARADC::PTR };
(sar_adc.sar1data_status.read().apb_saradc1_data().bits() as u16) & 0xfff
}
fn reset() {
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc
.int_clr
.write(|w| w.apb_saradc1_done_int_clr().set_bit());
sar_adc
.onetime_sample
.modify(|_, w| w.saradc_onetime_start().clear_bit());
}
}
#[cfg(esp32c3)]
impl RegisterAccess for ADC2 {
fn start_onetime_sample(channel: u8, attenuation: u8) {
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc.onetime_sample.modify(|_, w| unsafe {
w.saradc2_onetime_sample()
.set_bit()
.saradc_onetime_channel()
.bits(channel)
.saradc_onetime_atten()
.bits(attenuation)
.saradc_onetime_start()
.set_bit()
});
}
fn is_done() -> bool {
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc.int_raw.read().apb_saradc2_done_int_raw().bit()
}
fn read_data() -> u16 {
let sar_adc = unsafe { &*APB_SARADC::PTR };
(sar_adc.sar2data_status.read().apb_saradc2_data().bits() as u16) & 0xfff
}
fn reset() {
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc
.int_clr
.write(|w| w.apb_saradc2_done_int_clr().set_bit());
sar_adc
.onetime_sample
.modify(|_, w| w.saradc_onetime_start().clear_bit());
}
}
pub struct ADC<'d, ADCI> {
_adc: PeripheralRef<'d, ADCI>,
attenuations: [Option<Attenuation>; 5],
active_channel: Option<u8>,
}
impl<'d, ADCI> ADC<'d, ADCI>
where
ADCI: RegisterAccess,
{
pub fn adc(
peripheral_clock_controller: &mut PeripheralClockControl,
adc_instance: impl crate::peripheral::Peripheral<P = ADCI> + 'd,
config: AdcConfig<ADCI>,
) -> Result<Self, ()> {
peripheral_clock_controller.enable(Peripheral::ApbSarAdc);
let sar_adc = unsafe { &*APB_SARADC::PTR };
sar_adc.ctrl.modify(|_, w| unsafe {
w.saradc_start_force()
.set_bit()
.saradc_start()
.set_bit()
.saradc_sar_clk_gated()
.set_bit()
.saradc_xpd_sar_force()
.bits(0b11)
});
let adc = ADC {
_adc: adc_instance.into_ref(),
attenuations: config.attenuations,
active_channel: None,
};
Ok(adc)
}
}
impl<'d, ADCI, WORD, PIN> OneShot<ADCI, WORD, AdcPin<PIN, ADCI>> for ADC<'d, ADCI>
where
WORD: From<u16>,
PIN: Channel<ADCI, ID = u8>,
ADCI: RegisterAccess,
{
type Error = ();
fn read(&mut self, _pin: &mut AdcPin<PIN, ADCI>) -> nb::Result<WORD, Self::Error> {
if self.attenuations[AdcPin::<PIN, ADCI>::channel() as usize] == None {
panic!(
"Channel {} is not configured reading!",
AdcPin::<PIN, ADCI>::channel()
);
}
if let Some(active_channel) = self.active_channel {
if active_channel != AdcPin::<PIN, ADCI>::channel() {
return Err(nb::Error::WouldBlock);
}
} else {
self.active_channel = Some(AdcPin::<PIN, ADCI>::channel());
let channel = self.active_channel.unwrap();
let attenuation = self.attenuations[channel as usize].unwrap() as u8;
ADCI::start_onetime_sample(channel, attenuation);
}
let conversion_finished = ADCI::is_done();
if !conversion_finished {
return Err(nb::Error::WouldBlock);
}
let converted_value = ADCI::read_data();
ADCI::reset();
self.active_channel = None;
Ok(converted_value.into())
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_adc_interface {
($adc:ident [
$( ($pin:ident, $channel:expr) ,)+
]) => {
$(
impl Channel<$adc> for $pin<Analog> {
type ID = u8;
fn channel() -> u8 { $channel }
}
)+
}
}
pub use impl_adc_interface;
#[cfg(esp32c2)]
pub mod implementation {
use embedded_hal::adc::Channel;
use super::impl_adc_interface;
pub use crate::analog::{adc::*, ADC1};
use crate::gpio::*;
impl_adc_interface! {
ADC1 [
(Gpio0, 0),
(Gpio1, 1),
(Gpio2, 2),
(Gpio3, 3),
(Gpio4, 4),
]
}
}
#[cfg(esp32c3)]
pub mod implementation {
use embedded_hal::adc::Channel;
use super::impl_adc_interface;
pub use crate::analog::{adc::*, ADC1, ADC2};
use crate::gpio::*;
impl_adc_interface! {
ADC1 [
(Gpio0, 0),
(Gpio1, 1),
(Gpio2, 2),
(Gpio3, 3),
(Gpio4, 4),
]
}
impl_adc_interface! {
ADC2 [
(Gpio5, 4),
]
}
}
#[cfg(esp32c6)]
pub mod implementation {
use embedded_hal::adc::Channel;
use super::impl_adc_interface;
pub use crate::analog::{adc::*, ADC1};
use crate::gpio::*;
impl_adc_interface! {
ADC1 [
(Gpio0, 0),
(Gpio1, 1),
(Gpio2, 2),
(Gpio3, 3),
(Gpio4, 4),
(Gpio5, 5),
(Gpio6, 6),
]
}
}