use crate::peripherals::Gadc as GadcPeriph;
use core::marker::PhantomData;
#[inline]
fn delay_us(us: u32) {
let cycles = us.saturating_mul(64);
for _ in 0..cycles {
core::hint::spin_loop();
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AdcChannel {
Ain0 = 0,
Ain1 = 1,
Ain2 = 2,
Ain3 = 3,
Ain4 = 4,
Ain5 = 5,
Ain6 = 6,
Ain7 = 7,
}
const VSSAFE1_BIT: u32 = 9;
const GADC_DONE_POLL_LIMIT: u32 = 1_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum GadcError {
ConversionTimeout,
}
pub struct Gadc<'d> {
_gadc: PhantomData<GadcPeriph<'d>>,
first_sample: bool,
}
impl<'d> Gadc<'d> {
fn regs(&self) -> &'static crate::soc::pac::gadc::RegisterBlock {
unsafe { &*GadcPeriph::ptr() }
}
fn pmu_regs(&self) -> &'static crate::soc::pac::adc_pmu_afe::RegisterBlock {
unsafe { &*crate::soc::pac::AdcPmuAfe::ptr() }
}
fn aon_afe_regs(&self) -> &'static crate::soc::pac::aon_afe::RegisterBlock {
unsafe { &*crate::soc::pac::AonAfe::ptr() }
}
fn write_gadc_cfg(
pmu: &'static crate::soc::pac::adc_pmu_afe::RegisterBlock,
en: bool,
mux_en: bool,
iso_en: bool,
) {
pmu.afe_gadc_cfg().write(|w| {
let w = if en { w.s2d_gadc_en().set_bit() } else { w.s2d_gadc_en().clear_bit() };
let w = if mux_en {
w.s2d_gadc_mux_en().set_bit()
} else {
w.s2d_gadc_mux_en().clear_bit()
};
if iso_en { w.s2d_gadc_iso_en().set_bit() } else { w.s2d_gadc_iso_en().clear_bit() }
});
}
pub fn new(_gadc: GadcPeriph<'d>) -> Self {
let this = Self { _gadc: PhantomData, first_sample: true };
let r = this.regs();
let pmu = this.pmu_regs();
let aon = this.aon_afe_regs();
aon.afe_iso().modify(|_, w| w.afe_iso_en().set_bit()); pmu.afe_iso_cfg().write(|w| unsafe { w.bits(0) }); delay_us(30);
pmu.afe_dig_pwr_en().write(|w| w.afe_pwr_en().set_bit().afe_iso_en().set_bit());
delay_us(50);
pmu.afe_dig_pwr_en().modify(|_, w| w.afe_iso_en().clear_bit()); pmu.afe_adc_rst_n().write(|w| w.afe_adc_rst_n().set_bit()); pmu.afe_clk_en().write(|w| w.afe_clk_en().set_bit()); pmu.afe_soft_rst().write(|w| w.afe_soft_rst().set_bit()); r.cfg_clken().reset();
r.cfg_rstn().reset();
r.cfg_clken().write(|w| {
w.cfg_clken_tst()
.set_bit()
.cfg_gadc_clken_bc()
.set_bit()
.cfg_gadc_clken_fc()
.set_bit()
.cfg_gadc_clken_byp()
.set_bit()
.cfg_gadc_clken_prechg()
.set_bit()
.cfg_gadc_clken_ctrl()
.set_bit()
});
r.cfg_ana_6().write(|w| w.cfg_afe_afeldo_en().set_bit()); delay_us(150);
r.cfg_ana_7().modify(|_, w| w.cfg_afe_afeldo_en_dly().set_bit());
r.cfg_ana_4().write(|w| w.cfg_afe_adcldo_en().set_bit()); delay_us(150);
r.cfg_ana_5().modify(|_, w| w.cfg_afe_adcldo_en_dly().set_bit());
r.cfg_ana_0().modify(|_, w| w.cfg_vrefldo_en().set_bit()); delay_us(150);
r.cfg_clk_div_0().write(|w| unsafe { w.cfg_adc_ana_div_th().bits(0x27) }); r.cfg_ana_1().modify(|_, w| w.cfg_bufp_en().set_bit().cfg_bufn_en().set_bit());
r.cfg_freg_5().reset();
r.cfg_freg_9().write(|w| unsafe { w.bits(2) });
r.cfg_tst_1().write(|w| unsafe { w.diag_node().bits(2) }); r.cfg_rstn().write(|w| {
w.cfg_rstn_tst()
.set_bit()
.cfg_gadc_rstn_bc()
.set_bit()
.cfg_gadc_rstn_fc()
.set_bit()
.cfg_gadc_rstn_data()
.set_bit()
.cfg_gadc_rstn_ana()
.set_bit()
}); r.cfg_iso().write(|w| unsafe { w.bits(0) });
Self::write_gadc_cfg(pmu, false, true, true);
delay_us(30);
Self::write_gadc_cfg(pmu, false, true, false);
delay_us(30);
Self::write_gadc_cfg(pmu, true, true, false);
this
}
pub fn read(&mut self, channel: AdcChannel) -> Result<i32, GadcError> {
let r = self.regs();
let amuxp = 1u16 << (channel as u16);
let amuxn = 1u16 << VSSAFE1_BIT;
r.cfg_amux_1().write(|w| unsafe {
w.amuxn_sensor_ch_sel()
.bits(amuxn)
.amuxn_devide_disable()
.set_bit()
.amuxp_sensor_ch_sel()
.bits(amuxp)
.amuxp_devide_disable()
.set_bit()
});
r.cfg_amux_2().reset();
if self.first_sample {
self.convert_once()?;
self.first_sample = false;
}
self.convert_once()
}
fn convert_once(&self) -> Result<i32, GadcError> {
let r = self.regs();
let pmu = self.pmu_regs();
delay_us(5);
pmu.afe_gadc_cfg().modify(|_, w| w.s2d_gadc_mux_en().set_bit());
pmu.afe_gadc_cfg().modify(|_, w| w.s2d_gadc_iso_en().clear_bit());
delay_us(5);
pmu.afe_gadc_cfg().modify(|_, w| w.s2d_gadc_en().set_bit());
let mut done = false;
for _ in 0..GADC_DONE_POLL_LIMIT {
if r.rpt_gadc_data_3().read().single_sample_done().bit_is_set() {
done = true;
break;
}
core::hint::spin_loop();
}
pmu.afe_gadc_cfg().modify(|_, w| w.s2d_gadc_en().clear_bit());
delay_us(5);
pmu.afe_gadc_cfg().modify(|_, w| {
w.s2d_gadc_mux_en().clear_bit();
w.s2d_gadc_iso_en().set_bit()
});
if !done {
return Err(GadcError::ConversionTimeout);
}
let raw = r.rpt_gadc_data_2().read().sample_data().bits();
Ok(sign_extend18(raw))
}
}
#[inline]
fn sign_extend18(raw: u32) -> i32 {
let v = raw & 0x3FFFF;
if v & (1 << 17) != 0 { (v as i32) - 0x4_0000 } else { v as i32 }
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod tests {
use super::*;
fn amux_1(channel: AdcChannel) -> u32 {
let amuxp = 1u32 << (channel as u32);
let amuxn = 1u32 << VSSAFE1_BIT;
(amuxn & 0x7FF) | (1 << 11) | ((amuxp & 0x7FF) << 12) | (1 << 23)
}
#[test]
fn channel_discriminants_are_index() {
assert_eq!(AdcChannel::Ain0 as u32, 0);
assert_eq!(AdcChannel::Ain3 as u32, 3);
assert_eq!(AdcChannel::Ain7 as u32, 7);
}
#[test]
fn sign_extend_positive_passthrough() {
assert_eq!(sign_extend18(0), 0);
assert_eq!(sign_extend18(1), 1);
assert_eq!(sign_extend18(0x1FFFF), 131_071);
}
#[test]
fn sign_extend_negative_values() {
assert_eq!(sign_extend18(0x2_0000), -131_072);
assert_eq!(sign_extend18(0x3_FFFF), -1);
assert_eq!(sign_extend18(0x3_FFFE), -2);
}
#[test]
fn sign_extend_ignores_high_bits() {
assert_eq!(sign_extend18(0xFFFC_0000), 0);
assert_eq!(sign_extend18(0xFFFF_FFFF), -1);
assert_eq!(sign_extend18(0xDEAD_0000 | 0x1FFFF), 131_071);
}
#[test]
fn sign_extend_range_is_18bit_signed() {
for raw in [0u32, 0x1, 0x1_FFFF, 0x2_0000, 0x3_FFFF, 0xFFFF_FFFF] {
let v = sign_extend18(raw);
assert!((-131_072..=131_071).contains(&v), "raw={raw:#x} -> {v}");
}
}
#[test]
fn amux_n_side_fixed_to_vssafe1() {
for ch in [AdcChannel::Ain0, AdcChannel::Ain4, AdcChannel::Ain7] {
let v = amux_1(ch);
assert_eq!(v & 0x7FF, 1 << VSSAFE1_BIT, "amuxn for {ch:?}");
assert_ne!(v & (1 << 11), 0, "div-n bit for {ch:?}");
assert_ne!(v & (1 << 23), 0, "div-p bit for {ch:?}");
}
}
#[test]
fn amux_p_side_is_channel_onehot() {
assert_eq!((amux_1(AdcChannel::Ain0) >> 12) & 0x7FF, 1 << 0);
assert_eq!((amux_1(AdcChannel::Ain5) >> 12) & 0x7FF, 1 << 5);
assert_eq!((amux_1(AdcChannel::Ain7) >> 12) & 0x7FF, 1 << 7);
}
#[test]
fn amux_known_value_ain0() {
let expected = (1 << 9) | (1 << 11) | (1 << 12) | (1 << 23);
assert_eq!(amux_1(AdcChannel::Ain0), expected);
}
#[test]
fn delay_us_saturates() {
assert_eq!(0u32.saturating_mul(64), 0);
assert_eq!(1u32.saturating_mul(64), 64);
assert_eq!(u32::MAX.saturating_mul(64), u32::MAX);
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn sign_extend_in_range_and_masked(raw in any::<u32>()) {
let v = sign_extend18(raw);
prop_assert!((-131_072..=131_071).contains(&v));
prop_assert_eq!(v, sign_extend18(raw & 0x3FFFF));
}
#[test]
fn sign_extend_roundtrips(raw in any::<u32>()) {
let v = sign_extend18(raw);
let reencoded = (v as u32) & 0x3FFFF;
prop_assert_eq!(reencoded, raw & 0x3FFFF);
}
#[test]
fn delay_cycles_never_panic(us in any::<u32>()) {
let cycles = us.saturating_mul(64);
prop_assert!(cycles <= u32::MAX);
let exact = us as u64 * 64;
if exact <= u32::MAX as u64 {
prop_assert_eq!(cycles as u64, exact);
} else {
prop_assert_eq!(cycles, u32::MAX);
}
}
}
}