#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::PeripheralType;
use embassy_sync::waitqueue::AtomicWaker;
use stm32_metapac::comp::vals;
use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::rcc::RccInfo;
use crate::{Peri, interrupt};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PowerMode {
HighSpeed,
MediumSpeed,
UltraLowPower,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(comp_u5)]
pub enum Hysteresis {
None,
Low,
Medium,
High,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(comp_v2)]
pub enum Hysteresis {
None,
Hyst10M,
Hyst20M,
Hyst30M,
Hyst40M,
Hyst50M,
Hyst60M,
Hyst70M,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OutputPolarity {
NotInverted,
Inverted,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InvertingInput {
OneQuarterVref,
HalfVref,
ThreeQuarterVref,
Vref,
Dac1,
Dac2,
InputPin,
#[cfg(comp_v2)]
InputPin2,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BlankingSource {
#[default]
None,
Blank1,
Blank2,
Blank3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WindowMode {
#[default]
Disabled,
Enabled,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WindowOutput {
#[default]
OwnValue,
XorValue,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Config {
pub power_mode: PowerMode,
pub hysteresis: Hysteresis,
pub output_polarity: OutputPolarity,
pub inverting_input: InvertingInput,
pub blanking_source: BlankingSource,
pub window_mode: WindowMode,
pub window_output: WindowOutput,
}
impl Default for Config {
fn default() -> Self {
Self {
power_mode: PowerMode::HighSpeed,
hysteresis: Hysteresis::None,
output_polarity: OutputPolarity::NotInverted,
inverting_input: InvertingInput::HalfVref,
blanking_source: BlankingSource::None,
window_mode: WindowMode::Disabled,
window_output: WindowOutput::OwnValue,
}
}
}
pub struct State {
waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
T::disable_exti_interrupt();
T::state().waker.wake();
}
}
pub struct Comp<'d, T: Instance> {
_peri: Peri<'d, T>,
}
impl<'d, T: Instance> Comp<'d, T> {
pub fn new(
peri: Peri<'d, T>,
inp: Peri<'_, impl InputPlusPin<T> + crate::gpio::Pin>,
_irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
config: Config,
) -> Self {
T::info().rcc.enable_and_reset();
inp.set_as_analog();
Self::configure(inp.channel(), config);
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _peri: peri }
}
pub fn new_with_input_minus_pin(
peri: Peri<'d, T>,
inp: Peri<'_, impl InputPlusPin<T> + crate::gpio::Pin>,
inm: Peri<'_, impl InputMinusPin<T> + crate::gpio::Pin>,
_irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
config: Config,
) -> Self {
T::info().rcc.enable_and_reset();
inp.set_as_analog();
inm.set_as_analog();
Self::configure_with_input_minus_pin(inp.channel(), inm.channel(), config);
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _peri: peri }
}
fn configure_raw(inp_channel: u8, inmsel: vals::Inm, config: Config) {
#[cfg(comp_u5)]
let pwrmode = match config.power_mode {
PowerMode::HighSpeed => vals::PowerMode::HIGH_SPEED,
PowerMode::MediumSpeed => vals::PowerMode::MEDIUM_SPEED,
PowerMode::UltraLowPower => vals::PowerMode::ULTRA_LOW,
};
#[cfg(comp_v2)]
let hyst = match config.hysteresis {
Hysteresis::None => vals::Hysteresis::NONE,
Hysteresis::Hyst10M => vals::Hysteresis::HYST10M,
Hysteresis::Hyst20M => vals::Hysteresis::HYST20M,
Hysteresis::Hyst30M => vals::Hysteresis::HYST30M,
Hysteresis::Hyst40M => vals::Hysteresis::HYST40M,
Hysteresis::Hyst50M => vals::Hysteresis::HYST50M,
Hysteresis::Hyst60M => vals::Hysteresis::HYST60M,
Hysteresis::Hyst70M => vals::Hysteresis::HYST70M,
};
#[cfg(comp_u5)]
let hyst = match config.hysteresis {
Hysteresis::None => vals::Hysteresis::NONE,
Hysteresis::Low => vals::Hysteresis::LOW,
Hysteresis::Medium => vals::Hysteresis::MEDIUM,
Hysteresis::High => vals::Hysteresis::HIGH,
};
let polarity = match config.output_polarity {
OutputPolarity::NotInverted => vals::Polarity::NOT_INVERTED,
OutputPolarity::Inverted => vals::Polarity::INVERTED,
};
let blanksel = match config.blanking_source {
BlankingSource::None => vals::Blanking::NO_BLANKING,
BlankingSource::Blank1 => vals::Blanking::BLANK1,
BlankingSource::Blank2 => vals::Blanking::BLANK2,
BlankingSource::Blank3 => vals::Blanking::BLANK3,
};
#[cfg(comp_u5)]
let winmode = match config.window_mode {
WindowMode::Disabled => vals::WindowMode::THIS_INPSEL,
WindowMode::Enabled => vals::WindowMode::OTHER_INPSEL,
};
#[cfg(comp_u5)]
let winout = match config.window_output {
WindowOutput::OwnValue => vals::WindowOut::COMP1_VALUE,
WindowOutput::XorValue => vals::WindowOut::COMP1_VALUE_XOR_COMP2_VALUE,
};
#[cfg(comp_v2)]
let inp_channel = inp_channel != 0;
T::regs().csr().modify(|w| {
w.set_inpsel(inp_channel);
w.set_inmsel(inmsel);
w.set_hyst(hyst);
w.set_polarity(polarity);
w.set_blanksel(blanksel);
#[cfg(comp_v2)]
{
w.set_scalen(matches!(
inmsel,
vals::Inm::QUARTER_VREF | vals::Inm::HALF_VREF | vals::Inm::THREE_QUARTER_VREF | vals::Inm::VREF
));
w.set_brgen(matches!(
inmsel,
vals::Inm::QUARTER_VREF | vals::Inm::HALF_VREF | vals::Inm::THREE_QUARTER_VREF
));
}
w.set_en(true);
#[cfg(comp_u5)]
{
w.set_pwrmode(pwrmode);
w.set_winmode(winmode);
w.set_winout(winout);
}
});
}
fn configure(inp_channel: u8, config: Config) {
let inmsel = match config.inverting_input {
InvertingInput::OneQuarterVref => vals::Inm::QUARTER_VREF,
InvertingInput::HalfVref => vals::Inm::HALF_VREF,
InvertingInput::ThreeQuarterVref => vals::Inm::THREE_QUARTER_VREF,
InvertingInput::Vref => vals::Inm::VREF,
#[cfg(comp_u5)]
InvertingInput::Dac1 => vals::Inm::DAC1,
#[cfg(comp_u5)]
InvertingInput::Dac2 => vals::Inm::DAC2,
#[cfg(comp_v2)]
InvertingInput::Dac1 => vals::Inm::DACA,
#[cfg(comp_v2)]
InvertingInput::Dac2 => vals::Inm::DACB,
InvertingInput::InputPin => vals::Inm::INM1,
#[cfg(comp_v2)]
InvertingInput::InputPin2 => vals::Inm::INM2,
};
Self::configure_raw(inp_channel, inmsel, config);
}
fn configure_with_input_minus_pin(inp_channel: u8, inm_channel: u8, config: Config) {
let inmsel = vals::Inm::from_bits(0x06 + inm_channel);
Self::configure_raw(inp_channel, inmsel, config)
}
pub fn enable(&mut self) {
T::regs().csr().modify(|w| {
w.set_en(true);
});
}
pub fn disable(&mut self) {
T::regs().csr().modify(|w| {
w.set_en(false);
});
}
pub fn is_enabled(&self) -> bool {
T::regs().csr().read().en()
}
pub fn output_level(&self) -> bool {
T::regs().csr().read().value()
}
pub fn set_blanking_source(&mut self, source: BlankingSource) {
let blanksel = match source {
BlankingSource::None => vals::Blanking::NO_BLANKING,
BlankingSource::Blank1 => vals::Blanking::BLANK1,
BlankingSource::Blank2 => vals::Blanking::BLANK2,
BlankingSource::Blank3 => vals::Blanking::BLANK3,
};
T::regs().csr().modify(|w| {
w.set_blanksel(blanksel);
});
}
pub async fn wait_for_high(&mut self) {
self.enable();
if self.output_level() {
return;
}
self.wait_for_rising_edge().await;
}
pub async fn wait_for_low(&mut self) {
self.enable();
if !self.output_level() {
return;
}
self.wait_for_falling_edge().await;
}
pub async fn wait_for_rising_edge(&mut self) {
self.enable();
T::configure_exti(true, false);
poll_fn(|cx| {
T::state().waker.register(cx.waker());
if !T::is_exti_interrupt_enabled() {
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
pub async fn wait_for_falling_edge(&mut self) {
self.enable();
T::configure_exti(false, true);
poll_fn(|cx| {
T::state().waker.register(cx.waker());
if !T::is_exti_interrupt_enabled() {
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
pub async fn wait_for_any_edge(&mut self) {
self.enable();
T::configure_exti(true, true);
poll_fn(|cx| {
T::state().waker.register(cx.waker());
if !T::is_exti_interrupt_enabled() {
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
}
impl<'d, T: Instance> Drop for Comp<'d, T> {
fn drop(&mut self) {
T::regs().csr().modify(|w| {
w.set_en(false);
});
T::disable_exti_interrupt();
T::info().rcc.disable();
}
}
pub(crate) struct Info {
rcc: RccInfo,
}
pub(crate) trait SealedInstance {
fn info() -> &'static Info;
fn regs() -> crate::pac::comp::Comp;
fn state() -> &'static State;
fn exti_line() -> u8;
fn configure_exti(rising: bool, falling: bool);
fn enable_exti_interrupt();
fn disable_exti_interrupt();
fn is_exti_interrupt_enabled() -> bool;
fn clear_exti_pending();
}
pub(crate) trait SealedInputPlusPin<T: Instance> {
fn channel(&self) -> u8;
}
pub(crate) trait SealedInputMinusPin<T: Instance> {
fn channel(&self) -> u8;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + 'static {
type Interrupt: Interrupt;
}
#[allow(private_bounds)]
pub trait InputPlusPin<T: Instance>: SealedInputPlusPin<T> {}
#[allow(private_bounds)]
pub trait InputMinusPin<T: Instance>: SealedInputMinusPin<T> {}
macro_rules! impl_comp {
($inst:ident, $exti_line:expr) => {
impl SealedInstance for crate::peripherals::$inst {
fn info() -> &'static Info {
use crate::rcc::SealedRccPeripheral;
static INFO: Info = Info {
rcc: crate::peripherals::$inst::RCC_INFO,
};
&INFO
}
fn regs() -> crate::pac::comp::Comp {
crate::pac::$inst
}
fn state() -> &'static State {
static STATE: State = State::new();
&STATE
}
fn exti_line() -> u8 {
$exti_line
}
fn configure_exti(rising: bool, falling: bool) {
use crate::pac::EXTI;
let line = Self::exti_line() as usize;
critical_section::with(|_| {
EXTI.rtsr(0).modify(|w| w.set_line(line, rising));
EXTI.ftsr(0).modify(|w| w.set_line(line, falling));
Self::clear_exti_pending();
Self::enable_exti_interrupt();
});
}
fn enable_exti_interrupt() {
use crate::pac::EXTI;
let line = Self::exti_line() as usize;
#[cfg(any(
exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6
))]
EXTI.imr(0).modify(|w| w.set_line(line, true));
#[cfg(not(any(
exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6
)))]
EXTI.imr(0).modify(|w| w.set_line(line, true));
}
fn disable_exti_interrupt() {
use crate::pac::EXTI;
let line = Self::exti_line() as usize;
EXTI.imr(0).modify(|w| w.set_line(line, false));
}
fn is_exti_interrupt_enabled() -> bool {
use crate::pac::EXTI;
let line = Self::exti_line() as usize;
EXTI.imr(0).read().line(line)
}
fn clear_exti_pending() {
use crate::pac::EXTI;
let line = Self::exti_line() as usize;
#[cfg(not(any(
exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6
)))]
EXTI.pr(0).write(|w| w.set_line(line, true));
#[cfg(any(
exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6
))]
{
EXTI.rpr(0).write(|w| w.set_line(line, true));
EXTI.fpr(0).write(|w| w.set_line(line, true));
}
}
}
impl Instance for crate::peripherals::$inst {
type Interrupt = crate::_generated::peripheral_interrupts::$inst::WKUP;
}
};
}
#[cfg(comp_u5)]
foreach_peripheral! {
(comp, COMP1) => {
impl_comp!(COMP1, 17);
};
(comp, COMP2) => {
impl_comp!(COMP2, 18);
};
}
#[cfg(comp_v2)]
foreach_peripheral! {
(comp, COMP1) => {
impl_comp!(COMP1, 21);
};
(comp, COMP2) => {
impl_comp!(COMP2, 22);
};
(comp, COMP3) => {
impl_comp!(COMP3, 29);
};
(comp, COMP4) => {
impl_comp!(COMP4, 30);
};
(comp, COMP5) => {
impl_comp!(COMP5, 31);
};
(comp, COMP6) => {
impl_comp!(COMP6, 32);
};
(comp, COMP7) => {
impl_comp!(COMP7, 33);
};
}
#[allow(unused_macros)]
macro_rules! impl_comp_inp_pin {
($inst:ident, $pin:ident, $ch:expr) => {
impl crate::comp::InputPlusPin<crate::peripherals::$inst> for crate::peripherals::$pin {}
impl crate::comp::SealedInputPlusPin<crate::peripherals::$inst> for crate::peripherals::$pin {
fn channel(&self) -> u8 {
$ch
}
}
};
}
#[allow(unused_macros)]
macro_rules! impl_comp_inm_pin {
($inst:ident, $pin:ident, $ch:expr) => {
impl crate::comp::InputMinusPin<crate::peripherals::$inst> for crate::peripherals::$pin {}
impl crate::comp::SealedInputMinusPin<crate::peripherals::$inst> for crate::peripherals::$pin {
fn channel(&self) -> u8 {
$ch
}
}
};
}