use crate::gpio::{Analog, Pin, P1, P3};
use crate::pac;
use crate::ref_a::ReferenceVoltage;
pub use crate::comp_ladder::{ladder_millivolts, tap_for_millivolts, LADDER_TAPS};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PowerMode {
HighSpeed,
Normal,
UltraLowPower,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FilterDelay {
Ns450,
Ns900,
Ns1800,
Ns3600,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Threshold {
VccLadder { rising_tap: u8, falling_tap: u8 },
RefLadder {
level: ReferenceVoltage,
rising_tap: u8,
falling_tap: u8,
},
RefDirect { level: ReferenceVoltage },
}
impl Threshold {
pub const fn vcc_ladder(tap: u8) -> Self {
Threshold::VccLadder {
rising_tap: tap,
falling_tap: tap,
}
}
pub const fn vcc_ladder_hysteresis(rising_tap: u8, falling_tap: u8) -> Self {
Threshold::VccLadder {
rising_tap,
falling_tap,
}
}
pub const fn ref_ladder(level: ReferenceVoltage, tap: u8) -> Self {
Threshold::RefLadder {
level,
rising_tap: tap,
falling_tap: tap,
}
}
pub fn vcc_ladder_millivolts(threshold_mv: u16, avcc_mv: u16) -> Self {
Threshold::vcc_ladder(tap_for_millivolts(threshold_mv, avcc_mv))
}
}
#[derive(Clone, Copy, Debug)]
pub struct Config {
power_mode: PowerMode,
filter: Option<FilterDelay>,
invert_output: bool,
}
impl Default for Config {
fn default() -> Self {
Config {
power_mode: PowerMode::Normal,
filter: None,
invert_output: false,
}
}
}
impl Config {
pub fn power_mode(mut self, mode: PowerMode) -> Self {
self.power_mode = mode;
self
}
pub fn filter(mut self, delay: FilterDelay) -> Self {
self.filter = Some(delay);
self
}
pub fn invert_output(mut self) -> Self {
self.invert_output = true;
self
}
}
pub struct CompE {
ce: pac::ComparatorE,
}
impl CompE {
pub fn new(ce: pac::ComparatorE, config: Config) -> Self {
ce.cectl1().write(|w| {
match config.power_mode {
PowerMode::HighSpeed => w.cepwrmd().cepwrmd_0(),
PowerMode::Normal => w.cepwrmd().cepwrmd_1(),
PowerMode::UltraLowPower => w.cepwrmd().cepwrmd_2(),
};
if let Some(delay) = config.filter {
w.cef().set_bit();
match delay {
FilterDelay::Ns450 => w.cefdly().cefdly_0(),
FilterDelay::Ns900 => w.cefdly().cefdly_1(),
FilterDelay::Ns1800 => w.cefdly().cefdly_2(),
FilterDelay::Ns3600 => w.cefdly().cefdly_3(),
};
}
w.ceoutpol().bit(config.invert_output)
});
CompE { ce }
}
pub fn watch_pin<P: CompPin>(&mut self, _pin: &P, threshold: Threshold) {
self.route(P::CHANNEL, threshold);
}
pub fn watch_channel(&mut self, channel: u8, threshold: Threshold) {
self.route(channel & 0x0F, threshold);
}
fn route(&mut self, channel: u8, threshold: Threshold) {
self.ce.cectl1().modify(|_, w| w.ceon().clear_bit());
self.ce.cectl0().write(|w| {
w.ceipsel().set(channel);
w.ceipen().set_bit()
});
self.ce
.cectl3()
.modify(|r, w| unsafe { w.bits(r.bits() | (1u16 << channel)) });
self.ce.cectl2().write(|w| {
w.cersel().set_bit(); match threshold {
Threshold::VccLadder {
rising_tap,
falling_tap,
} => {
w.cers().cers_1(); w.cerefl().cerefl_0(); w.ceref0().set(rising_tap & 0x1F);
w.ceref1().set(falling_tap & 0x1F)
}
Threshold::RefLadder {
level,
rising_tap,
falling_tap,
} => {
w.cers().cers_2(); match level {
ReferenceVoltage::V1_2 => w.cerefl().cerefl_1(),
ReferenceVoltage::V2_0 => w.cerefl().cerefl_2(),
ReferenceVoltage::V2_5 => w.cerefl().cerefl_3(),
};
w.ceref0().set(rising_tap & 0x1F);
w.ceref1().set(falling_tap & 0x1F)
}
Threshold::RefDirect { level } => {
w.cers().cers_3(); match level {
ReferenceVoltage::V1_2 => w.cerefl().cerefl_1(),
ReferenceVoltage::V2_0 => w.cerefl().cerefl_2(),
ReferenceVoltage::V2_5 => w.cerefl().cerefl_3(),
}
}
}
});
self.ce.cectl1().modify(|_, w| w.ceon().set_bit());
}
pub fn set_taps(&mut self, rising_tap: u8, falling_tap: u8) {
self.ce.cectl2().modify(|_, w| {
w.ceref0().set(rising_tap & 0x1F);
w.ceref1().set(falling_tap & 0x1F)
});
}
pub fn output(&self) -> bool {
self.ce.cectl1().read().ceout().bit_is_set()
}
pub fn exchange_inputs(&mut self, exchange: bool) {
self.ce.cectl1().modify(|_, w| w.ceex().bit(exchange));
}
#[cfg(feature = "critical-section")]
pub fn clear_pending_interrupts(&mut self) {
critical_section::with(|_| {
self.ce
.ceint()
.modify(|_, w| w.ceifg().clear_bit().ceiifg().clear_bit());
});
}
#[cfg(feature = "critical-section")]
pub fn enable_output_interrupts(&mut self) {
critical_section::with(|_| {
self.ce.cectl1().modify(|_, w| w.ceies().clear_bit());
self.ce.ceint().modify(|_, w| {
w.ceifg().clear_bit();
w.ceiifg().clear_bit();
w.ceie().set_bit();
w.ceiie().set_bit()
});
});
}
#[cfg(feature = "critical-section")]
pub fn disable_output_interrupts(&mut self) {
critical_section::with(|_| {
self.ce
.ceint()
.modify(|_, w| w.ceie().clear_bit().ceiie().clear_bit());
});
}
#[cfg(feature = "critical-section")]
pub fn free(self) -> pac::ComparatorE {
self.ce.cectl1().modify(|_, w| w.ceon().clear_bit());
critical_section::with(|_| {
self.ce
.ceint()
.modify(|_, w| w.ceie().clear_bit().ceiie().clear_bit());
});
self.ce
}
}
pub const IV_OUTPUT_ROSE: u16 = 0x0002;
pub const IV_OUTPUT_FELL: u16 = 0x0004;
pub fn read_iv() -> u16 {
let ce = unsafe { pac::ComparatorE::steal() };
ce.ceiv().read().bits()
}
mod sealed {
pub trait Sealed {}
}
pub trait CompPin: sealed::Sealed {
const CHANNEL: u8;
}
macro_rules! comp_pins {
($($Port:ident $N:literal => $ch:literal),+ $(,)?) => {$(
impl sealed::Sealed for Pin<$Port, $N, Analog> {}
impl CompPin for Pin<$Port, $N, Analog> {
const CHANNEL: u8 = $ch;
}
)+};
}
comp_pins! {
P1 0 => 0, P1 1 => 1, P1 2 => 2, P1 3 => 3,
P1 4 => 4, P1 5 => 5,
P3 0 => 12, P3 1 => 13, P3 2 => 14, P3 3 => 15,
}