use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, AtomicBool, Ordering};
use core::task::Poll;
use embassy_hal_internal::Peripheral;
use embassy_sync::waitqueue::AtomicWaker;
use sifli_pac::HPSYS_CFG;
use crate::_generated::{FIRST_CHANNEL_PIN, VBAT_CHANNEL_ID, VOL_OFFSET, VOL_RATIO};
use crate::{blocking_delay_us, interrupt, rcc};
use crate::mode::{Async, Blocking, Mode};
use crate::gpio::{self, Analog};
use crate::interrupt::typelevel::{Binding};
use crate::interrupt::InterruptExt;
use crate::pac::gpadc::vals as AdcVals;
use crate::pac::GPADC;
use crate::peripherals;
static WAKER: AtomicWaker = AtomicWaker::new();
static IRQ_DONE: AtomicBool = AtomicBool::new(false);
#[non_exhaustive]
pub struct Config {
pub sample_width: u32,
pub conv_width: u8,
pub data_samp_dly: u8,
}
impl Default for Config {
fn default() -> Self {
Self {
sample_width: 0x71,
conv_width: 75,
data_samp_dly: 0x4,
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
ConversionFailed,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(transparent)]
pub struct Sample(u16);
impl Sample {
pub fn value(&self) -> u16 {
self.0
}
pub fn to_mv(&self) -> u16 {
if self.0 <= VOL_OFFSET {
0 } else {
((self.0 - VOL_OFFSET) as u32 * VOL_RATIO as u32 / 1000) as u16
}
}
pub fn to_v_float(&self) -> f32 {
if self.0 <= VOL_OFFSET {
0.0 } else {
(self.0 - VOL_OFFSET) as f32 * VOL_RATIO as f32 / 1_000_000.0
}
}
}
pub struct Channel<'p> {
pub id: u8,
phantom: PhantomData<&'p ()>,
}
pub trait AdcPin: gpio::Pin {
fn adc_channel_id(&self) -> u8 {
self.pin() - FIRST_CHANNEL_PIN
}
}
impl<'p> Channel<'p> {
pub fn new_pin(pin: impl AdcPin + 'p) -> Self {
let id = pin.adc_channel_id();
Analog::new(pin);
Self {
id,
phantom: PhantomData,
}
}
pub fn new_vbat(_vbat: impl Peripheral<P = peripherals::ADC_VBAT> + 'p) -> Self {
Self {
id: VBAT_CHANNEL_ID,
phantom: PhantomData,
}
}
}
pub struct Adc<'d, M: Mode> {
_phantom: PhantomData<(&'d peripherals::GPADC, M)>,
}
impl<'d, M: Mode> Adc<'d, M> {
fn new_inner(
_inner: impl Peripheral<P = peripherals::GPADC> + 'd,
config: Config,
) -> Self {
rcc::enable_and_reset::<peripherals::GPADC>();
let regs = GPADC;
HPSYS_CFG.anau_cr().modify(|r| r.set_en_bg(true));
regs.cfg_reg1().modify(|r| r.set_anau_gpadc_se(true));
regs.ctrl_reg2().write(|w| {
w.set_samp_width(config.sample_width);
w.set_conv_width(config.conv_width);
});
regs.ctrl_reg().modify(|r| {
r.set_data_samp_dly(config.data_samp_dly);
r.set_init_time(8);
r.set_timer_trig_en(false);
});
regs.cfg_reg1().modify(|r| {
r.set_anau_gpadc_vsp(AdcVals::Vsp::V0_642); r.set_anau_gpadc_cmm(0x10);
r.set_anau_gpadc_en_v18(false); });
for i in 0..8 {
regs.slot(i).modify(|r| r.set_slot_en(false));
}
Self {
_phantom: PhantomData,
}
}
fn prepare(&mut self, channel: &Channel) {
if channel.id == VBAT_CHANNEL_ID {
HPSYS_CFG.anau_cr().modify(|r| r.set_en_vbat_mon(true));
}
GPADC.slot(channel.id as _).modify(|r| r.set_slot_en(true));
GPADC.cfg_reg1().modify(|r| r.set_anau_gpadc_ldoref_en(true));
blocking_delay_us(200);
GPADC.cfg_reg1().modify(|r| {r.set_anau_gpadc_mute(false)});
GPADC.ctrl_reg().modify(|r| r.set_frc_en_adc(true));
blocking_delay_us(200);
}
fn finish(&mut self, channel: &Channel) {
if channel.id == VBAT_CHANNEL_ID {
HPSYS_CFG.anau_cr().modify(|r| r.set_en_vbat_mon(false));
}
GPADC.ctrl_reg().modify(|r| r.set_frc_en_adc(false));
GPADC.cfg_reg1().modify(|r| {
r.set_anau_gpadc_ldoref_en(false);
r.set_anau_gpadc_mute(true);
});
}
pub fn blocking_read(&mut self, ch: &mut Channel) -> Result<Sample, Error> {
self.prepare(ch);
GPADC.ctrl_reg().modify(|r| {
r.set_adc_op_mode(false); r.set_chnl_sel_frc_en(true); });
GPADC.cfg_reg1().modify(|r| r.set_anau_gpadc_sel_pch(ch.id));
GPADC.ctrl_reg().modify(|r| r.set_adc_start(true));
while !GPADC.gpadc_irq().read().gpadc_irsr() {}
GPADC.gpadc_irq().write(|w| w.set_gpadc_icr(true));
let result = GPADC.rdata(0).read().even_slot_rdata();
self.finish(ch);
Ok(Sample(result & 0xfff))
}
}
impl<'d, M: Mode> Drop for Adc<'d, M> {
fn drop(&mut self) {
GPADC.ctrl_reg().modify(|r| r.set_frc_en_adc(false));
GPADC.cfg_reg1().modify(|r| r.set_anau_gpadc_ldoref_en(false));
}
}
impl<'d> Adc<'d, Blocking> {
pub fn new_blocking(
inner: impl Peripheral<P = peripherals::GPADC> + 'd,
config: Config,
) -> Self {
Self::new_inner(inner, config)
}
}
pub struct InterruptHandler;
impl interrupt::typelevel::Handler<interrupt::typelevel::GPADC> for InterruptHandler {
unsafe fn on_interrupt() {
if GPADC.gpadc_irq().read().gpadc_irsr() {
IRQ_DONE.store(true, Ordering::SeqCst);
}
GPADC.gpadc_irq().modify(|w| w.set_gpadc_icr(true));
WAKER.wake();
}
}
impl<'d> Adc<'d, Async> {
pub fn new(
inner: impl Peripheral<P = peripherals::GPADC> + 'd,
_irq: impl Binding<interrupt::typelevel::GPADC, InterruptHandler>,
config: Config,
) -> Self {
let s = Self::new_inner(inner, config);
let irq = crate::interrupt::GPADC;
irq.unpend();
unsafe { irq.enable() };
s
}
async fn wait_for_completion(&mut self) {
let regs = GPADC;
poll_fn(move |cx| {
WAKER.register(cx.waker());
regs.gpadc_irq().modify(|r| r.set_gpadc_imr(false));
compiler_fence(Ordering::SeqCst);
if IRQ_DONE.load(Ordering::SeqCst) {
IRQ_DONE.store(false, Ordering::SeqCst);
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
}
pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result<Sample, Error> {
self.prepare(ch);
GPADC.ctrl_reg().modify(|r| {
r.set_adc_op_mode(false);
r.set_chnl_sel_frc_en(true);
});
GPADC.cfg_reg1().modify(|r| r.set_anau_gpadc_sel_pch(ch.id));
GPADC.gpadc_irq().modify(|r| r.set_gpadc_imr(false));
IRQ_DONE.store(false, Ordering::SeqCst);
compiler_fence(Ordering::SeqCst);
GPADC.ctrl_reg().modify(|r| r.set_adc_start(true));
self.wait_for_completion().await;
let result = GPADC.rdata(0).read().even_slot_rdata();
self.finish(ch);
Ok(Sample(result & 0xfff))
}
}
#[allow(private_interfaces)]
pub(crate) trait SealedInstance: crate::rcc::RccEnableReset + crate::rcc::RccGetFreq {}
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
type Interrupt: interrupt::typelevel::Interrupt;
}
impl SealedInstance for peripherals::GPADC {}
impl Instance for peripherals::GPADC {
type Interrupt = crate::interrupt::typelevel::GPADC;
}
dma_trait!(Dma, Instance);