use core::{future::poll_fn, marker::PhantomData, task::Poll};
use embassy_hal_internal::{Peri, interrupt::InterruptExt as _};
use crate::{
event_link::IcuInterrupt as _,
gpio::{Flex, WithOpenDrain},
interrupt,
interrupt::typelevel::{Handler as InterruptHandler, Interrupt as InterruptType},
pac::gpt::{
regs::{Gtdnsr, Gtupsr},
vals::{Ccr, Nfcs},
},
pwm::{ChanA, ChanB, Divider, Instance, PwmPin},
};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
pub divider: Divider,
pub debounce: Debounce,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, PartialEq)]
pub enum Debounce {
Off,
Min1,
Min4,
Min16,
Min64,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Copy, Clone, PartialEq)]
pub enum Direction {
#[allow(missing_docs)]
Up,
#[allow(missing_docs)]
Down,
#[allow(missing_docs)]
Unknown,
}
#[allow(private_bounds)]
pub struct Qdec<'d, I: Instance, Int: InterruptType> {
_instance: PhantomData<&'d I>,
_interrupt: PhantomData<Int>,
_pin_a: Flex<'d, WithOpenDrain>,
_pin_b: Flex<'d, WithOpenDrain>,
}
#[allow(private_bounds)]
pub struct QdecInterruptHandler<I: Instance> {
_phantom: PhantomData<I>,
}
impl From<Debounce> for Nfcs {
fn from(value: Debounce) -> Self {
match value {
Debounce::Off => Self::Pclkd1,
Debounce::Min1 => Self::Pclkd1,
Debounce::Min4 => Self::Pclkd4,
Debounce::Min16 => Self::Pclkd16,
Debounce::Min64 => Self::Pclkd64,
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
divider: Divider::Div1024,
debounce: Debounce::Min64,
}
}
}
impl<I: Instance, Int: InterruptType> InterruptHandler<Int> for QdecInterruptHandler<I> {
unsafe fn on_interrupt() {
Int::IRQ.icu_unpend();
I::cmpa_waker().wake()
}
}
#[allow(private_bounds)]
impl<'d, I: Instance, Int: InterruptType> Qdec<'d, I, Int> {
pub fn new<A: PwmPin<I, ChanA>, B: PwmPin<I, ChanB>>(
peri: Peri<'d, I>,
pin_a: Peri<'d, A>,
pin_b: Peri<'d, B>,
config: Config,
irq: impl interrupt::typelevel::Binding<Int, QdecInterruptHandler<I>>,
) -> Self {
let _ = peri;
let _ = irq;
let pwm = I::regs();
pwm.gticasr().modify(|r| {
r.set_ascarbl(true);
r.set_ascarbh(false);
r.set_ascafbl(true);
r.set_ascafbh(false);
});
pwm.gtcr().modify(|w| w.set_tpcs(config.divider.into()));
pwm.gtpr().write_value(0xFFFF);
pwm.gtupsr().write_value(Gtupsr(0x6900));
pwm.gtdnsr().write_value(Gtdnsr(0x9600));
let debounce_enable = config.debounce != Debounce::Off;
pwm.gtior().modify(|r| {
r.set_nfaen(debounce_enable);
r.set_nfcsa(config.debounce.into());
r.set_nfben(debounce_enable);
r.set_nfcsb(config.debounce.into());
});
pwm.gtcnt().write_value(0);
pwm.gtccra().write_value(0);
pwm.gtccrc().write_value(0);
pwm.gtber().modify(|w| w.set_ccra(Ccr::SingleBuffer));
pin_a.set_pfunc();
let pin_a = Flex::new(pin_a);
pin_b.set_pfunc();
let pin_b = Flex::new(pin_b);
unsafe {
Int::IRQ.enable();
Int::IRQ.icu_enable(I::CAPTURE_COMP_A_EVENT)
};
pwm.gtcr().modify(|w| w.set_cst(true));
pwm.gtst().modify(|r| r.set_tcfa(false));
Self {
_instance: PhantomData,
_interrupt: PhantomData,
_pin_a: pin_a,
_pin_b: pin_b,
}
}
pub fn count(&self) -> u32 {
let pwm = I::regs();
pwm.gtcnt().read()
}
pub async fn read(&self) -> Direction {
let pwm = I::regs();
poll_fn(|ctx| {
I::cmpa_waker().register(ctx.waker());
if pwm.gtst().read().tcfa() {
pwm.gtst().modify(|r| r.set_tcfa(false));
let count = pwm.gtcnt().read();
let prev_count = pwm.gtccra().read();
if count > prev_count {
Poll::Ready(Direction::Up)
} else if count < prev_count {
Poll::Ready(Direction::Down)
} else {
Poll::Ready(Direction::Unknown)
}
} else {
Poll::Pending
}
})
.await
}
pub fn reset(&mut self) {
let pwm = I::regs();
critical_section::with(|_| {
pwm.gtcnt().write_value(0);
pwm.gtccra().write_value(0);
pwm.gtccrc().write_value(0);
});
}
}
impl<'d, I: Instance, Int: InterruptType> Drop for Qdec<'d, I, Int> {
fn drop(&mut self) {
Int::IRQ.disable();
Int::IRQ.icu_disable();
let pwm = I::regs();
pwm.gtcr().modify(|w| w.set_cst(false));
}
}