use core::{
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use Interrupt::LP_ADC as InterruptSource;
use portable_atomic::{AtomicU32, Ordering};
use procmacros::handler;
use super::{AdcCalScheme, AdcChannel, AdcConfig, AdcPin};
use crate::{
Async,
Blocking,
asynch::AtomicWaker,
interrupt::{InterruptConfigurable, InterruptHandler},
peripherals::{APB_SARADC, Interrupt, LP_PERI},
rtc_cntl::WakeLock,
system::{GenericPeripheralGuard, Peripheral},
};
pub(super) const NUM_ATTENS: usize = 8;
const ADC_DATA_MASK: u32 = 0x1_ffff;
#[instability::unstable]
pub const FULL_SCALE: u16 = 4393;
#[instability::unstable]
pub const ZERO_DIFF_CODE: u16 = 2198;
const TRIGGER_MODE_SW: u8 = 2;
const TRIGGER_MODE_OFF: u8 = 0;
const FORCE_XPD_SAR_PU: u8 = 3;
const CLK_DIV_NUM: u8 = 4;
const CLK_SRC_XTAL: u8 = 1;
fn enable_refgen() {
APB_SARADC::regs().ref_control().modify(|_, w| {
w.rtc_xpd_refgen().set_bit();
w.rtc_pre_charge().set_bit();
w.rtc_ref_delay().set_bit()
});
}
#[doc(hidden)]
pub trait RegisterAccess {
fn enable();
fn program_pattern(channel: u8);
fn start_sample();
fn is_done() -> bool;
fn read_data() -> u16;
fn reset();
}
#[cfg(adc_adc1)]
impl RegisterAccess for crate::peripherals::ADC1<'_> {
fn enable() {
APB_SARADC::regs()
.ctrl2()
.modify(|_, w| w.timer_en().clear_bit());
enable_refgen();
APB_SARADC::regs().ctrl0().modify(|_, w| unsafe {
w.xpd_sar1_force().bits(FORCE_XPD_SAR_PU);
w.sar1_continue_mode_en().clear_bit();
w.sar1_trigger_stop().set_bit()
});
}
fn program_pattern(channel: u8) {
let regs = APB_SARADC::regs();
let entry = (((channel & 0xf) as u32) << 2) << 18;
regs.ctrl0().modify(|_, w| unsafe {
w.sar1_patt_type().set_bit();
w.sar1_patt_len().bits(0)
});
regs.sar1_patt_tab1()
.write(|w| unsafe { w.sar1_patt_tab1().bits(entry) });
regs.ctrl0().modify(|_, w| w.sar1_patt_p_clear().set_bit());
regs.ctrl0()
.modify(|_, w| w.sar1_patt_p_clear().clear_bit());
regs.ctrl0()
.modify(|_, w| unsafe { w.sar1_trigger_mode().bits(TRIGGER_MODE_SW) });
}
fn start_sample() {
APB_SARADC::regs()
.ctrl0()
.modify(|_, w| w.sar1_trigger_start().set_bit());
}
fn is_done() -> bool {
APB_SARADC::regs().int_raw().read().sar1_done().bit_is_set()
}
fn read_data() -> u16 {
(APB_SARADC::regs()
.sar1_data_status()
.read()
.apb_saradc1_data()
.bits()
& ADC_DATA_MASK) as u16
}
fn reset() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.sar1_done().clear_bit_by_one());
APB_SARADC::regs()
.ctrl0()
.modify(|_, w| unsafe { w.sar1_trigger_mode().bits(TRIGGER_MODE_OFF) });
}
}
#[cfg(adc_adc2)]
impl RegisterAccess for crate::peripherals::ADC2<'_> {
fn enable() {
APB_SARADC::regs()
.ctrl2()
.modify(|_, w| w.timer_en().clear_bit());
enable_refgen();
APB_SARADC::regs().ctrl1().modify(|_, w| unsafe {
w.xpd_sar2_force().bits(FORCE_XPD_SAR_PU);
w.sar2_continue_mode_en().clear_bit();
w.sar2_trigger_stop().set_bit()
});
}
fn program_pattern(channel: u8) {
let regs = APB_SARADC::regs();
let entry = (((channel & 0xf) as u32) << 2) << 18;
regs.ctrl1().modify(|_, w| unsafe {
w.sar2_patt_type().set_bit();
w.sar2_patt_len().bits(0)
});
regs.sar2_patt_tab1()
.write(|w| unsafe { w.sar2_patt_tab1().bits(entry) });
regs.ctrl1().modify(|_, w| w.sar2_patt_p_clear().set_bit());
regs.ctrl1()
.modify(|_, w| w.sar2_patt_p_clear().clear_bit());
regs.ctrl1()
.modify(|_, w| unsafe { w.sar2_trigger_mode().bits(TRIGGER_MODE_SW) });
}
fn start_sample() {
APB_SARADC::regs()
.ctrl1()
.modify(|_, w| w.sar2_trigger_start().set_bit());
}
fn is_done() -> bool {
APB_SARADC::regs().int_raw().read().sar2_done().bit_is_set()
}
fn read_data() -> u16 {
(APB_SARADC::regs()
.sar2_data_status()
.read()
.apb_saradc2_data()
.bits()
& ADC_DATA_MASK) as u16
}
fn reset() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.sar2_done().clear_bit_by_one());
APB_SARADC::regs()
.ctrl1()
.modify(|_, w| unsafe { w.sar2_trigger_mode().bits(TRIGGER_MODE_OFF) });
}
}
pub struct Adc<'d, ADC, Dm: crate::DriverMode> {
_adc: ADC,
active_channel: Option<u8>,
_guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>,
_phantom: PhantomData<(Dm, &'d mut ())>,
}
impl<'d, ADCX> Adc<'d, ADCX, Blocking>
where
ADCX: RegisterAccess + 'd,
{
pub fn new(adc_instance: ADCX, _config: AdcConfig<ADCX>) -> Self {
let guard = GenericPeripheralGuard::new();
LP_PERI::regs().adc_ctrl().modify(|_, w| unsafe {
w.lp_adc_clk_sel().bits(CLK_SRC_XTAL);
w.lp_adc_div_num().bits(CLK_DIV_NUM)
});
APB_SARADC::regs()
.ctrl_date()
.modify(|_, w| w.clk_en().set_bit());
ADCX::enable();
Adc {
_adc: adc_instance,
active_channel: None,
_guard: guard,
_phantom: PhantomData,
}
}
pub fn into_async(mut self) -> Adc<'d, ADCX, Async> {
acquire_async_adc();
self.set_interrupt_handler(adc_interrupt_handler);
ADCX::reset();
Adc {
_adc: self._adc,
active_channel: self.active_channel,
_guard: self._guard,
_phantom: PhantomData,
}
}
pub fn read_blocking<PIN, CS>(&mut self, pin: &mut AdcPin<PIN, ADCX, CS>) -> u16
where
PIN: AdcChannel,
CS: AdcCalScheme<ADCX>,
{
ADCX::program_pattern(pin.pin.adc_channel());
ADCX::start_sample();
while !ADCX::is_done() {}
let converted_value = ADCX::read_data();
ADCX::reset();
converted_value
}
pub fn read_oneshot<PIN, CS>(
&mut self,
pin: &mut super::AdcPin<PIN, ADCX, CS>,
) -> nb::Result<u16, ()>
where
PIN: super::AdcChannel,
CS: super::AdcCalScheme<ADCX>,
{
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());
ADCX::program_pattern(pin.pin.adc_channel());
ADCX::start_sample();
}
if !ADCX::is_done() {
return Err(nb::Error::WouldBlock);
}
let converted_value = ADCX::read_data();
ADCX::reset();
self.active_channel = None;
Ok(converted_value)
}
}
impl<ADCX> crate::private::Sealed for Adc<'_, ADCX, Blocking> {}
impl<ADCX> InterruptConfigurable for Adc<'_, ADCX, 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);
}
}
impl<'d, ADCX> Adc<'d, ADCX, Async>
where
ADCX: RegisterAccess + 'd,
{
pub fn into_blocking(self) -> Adc<'d, ADCX, Blocking> {
if release_async_adc() {
for cpu in crate::system::Cpu::all() {
crate::interrupt::disable(cpu, InterruptSource);
}
}
Adc {
_adc: self._adc,
active_channel: self.active_channel,
_guard: self._guard,
_phantom: PhantomData,
}
}
pub async fn read_oneshot<PIN, CS>(&mut self, pin: &mut super::AdcPin<PIN, ADCX, CS>) -> u16
where
ADCX: Instance,
PIN: super::AdcChannel,
CS: super::AdcCalScheme<ADCX>,
{
ADCX::program_pattern(pin.pin.adc_channel());
ADCX::start_sample();
AdcFuture::<ADCX>::new(self).await;
let converted_value = ADCX::read_data();
ADCX::reset();
converted_value
}
}
static ASYNC_ADC_COUNT: AtomicU32 = AtomicU32::new(0);
fn acquire_async_adc() {
ASYNC_ADC_COUNT.fetch_add(1, Ordering::Relaxed);
}
fn release_async_adc() -> bool {
ASYNC_ADC_COUNT.fetch_sub(1, Ordering::Relaxed) == 1
}
#[handler]
pub(crate) fn adc_interrupt_handler() {
let interrupt_status = APB_SARADC::regs().int_st().read();
#[cfg(adc_adc1)]
if interrupt_status.sar1_done().bit_is_set() {
unsafe { handle_async(crate::peripherals::ADC1::steal()) }
}
#[cfg(adc_adc2)]
if interrupt_status.sar2_done().bit_is_set() {
unsafe { handle_async(crate::peripherals::ADC2::steal()) }
}
}
fn handle_async<ADCX: Instance>(_instance: ADCX) {
ADCX::waker().wake();
ADCX::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.sar1_done().set_bit());
}
fn unlisten() {
APB_SARADC::regs()
.int_ena()
.modify(|_, w| w.sar1_done().clear_bit());
}
fn clear_interrupt() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.sar1_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.sar2_done().set_bit());
}
fn unlisten() {
APB_SARADC::regs()
.int_ena()
.modify(|_, w| w.sar2_done().clear_bit());
}
fn clear_interrupt() {
APB_SARADC::regs()
.int_clr()
.write(|w| w.sar2_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<ADCX: Instance> {
phantom: PhantomData<ADCX>,
_wake_lock: WakeLock,
}
impl<ADCX: Instance> AdcFuture<ADCX> {
pub fn new(_self: &super::Adc<'_, ADCX, Async>) -> Self {
Self {
phantom: PhantomData,
_wake_lock: WakeLock::new(),
}
}
}
impl<ADCX: Instance + super::RegisterAccess> core::future::Future for AdcFuture<ADCX> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if ADCX::is_done() {
ADCX::clear_interrupt();
Poll::Ready(())
} else {
ADCX::waker().register(cx.waker());
ADCX::listen();
Poll::Pending
}
}
}
impl<ADCX: Instance> Drop for AdcFuture<ADCX> {
fn drop(&mut self) {
ADCX::unlisten();
}
}