//! Quadrature decoder interface use core::marker::PhantomData; use core::task::Poll; use embassy::waitqueue::AtomicWaker; use embassy_hal_common::{unborrow, Unborrowed}; use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::InterruptExt; use crate::peripherals::QDEC; use crate::{interrupt, pac, Unborrow}; /// Quadrature decoder pub struct Qdec<'d> { phantom: PhantomData<&'d QDEC>, } #[non_exhaustive] pub struct Config { pub num_samples: NumSamples, pub period: SamplePeriod, pub led_polarity: LedPolarity, pub debounce: bool, pub led_pre_usecs: u16, } impl Default for Config { fn default() -> Self { Self { num_samples: NumSamples::_1smpl, period: SamplePeriod::_256us, led_polarity: LedPolarity::ActiveHigh, debounce: true, led_pre_usecs: 0, } } } static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Qdec<'d> { pub fn new( qdec: impl Unborrow + 'd, irq: impl Unborrow + 'd, a: impl Unborrow + 'd, b: impl Unborrow + 'd, config: Config, ) -> Self { unborrow_and_degrade!(a, b); Self::new_inner(qdec, irq, a, b, None, config) } pub fn new_with_led( qdec: impl Unborrow + 'd, irq: impl Unborrow + 'd, a: impl Unborrow + 'd, b: impl Unborrow + 'd, led: impl Unborrow + 'd, config: Config, ) -> Self { unborrow_and_degrade!(a, b, led); Self::new_inner(qdec, irq, a, b, Some(led), config) } fn new_inner( _t: impl Unborrow + 'd, irq: impl Unborrow + 'd, a: Unborrowed<'d, AnyPin>, b: Unborrowed<'d, AnyPin>, led: Option>, config: Config, ) -> Self { unborrow!(irq); let r = Self::regs(); // Select pins. a.conf().write(|w| w.input().connect().pull().pullup()); b.conf().write(|w| w.input().connect().pull().pullup()); r.psel.a.write(|w| unsafe { w.bits(a.psel_bits()) }); r.psel.b.write(|w| unsafe { w.bits(b.psel_bits()) }); if let Some(led_pin) = &led { led_pin.conf().write(|w| w.dir().output()); r.psel.led.write(|w| unsafe { w.bits(led_pin.psel_bits()) }); } // Enables/disable input debounce filters r.dbfen.write(|w| match config.debounce { true => w.dbfen().enabled(), false => w.dbfen().disabled(), }); // Set LED output pin polarity r.ledpol.write(|w| match config.led_polarity { LedPolarity::ActiveHigh => w.ledpol().active_high(), LedPolarity::ActiveLow => w.ledpol().active_low(), }); // Set time period the LED is switched ON prior to sampling (0..511 us). r.ledpre .write(|w| unsafe { w.ledpre().bits(config.led_pre_usecs.min(511)) }); // Set sample period r.sampleper.write(|w| match config.period { SamplePeriod::_128us => w.sampleper()._128us(), SamplePeriod::_256us => w.sampleper()._256us(), SamplePeriod::_512us => w.sampleper()._512us(), SamplePeriod::_1024us => w.sampleper()._1024us(), SamplePeriod::_2048us => w.sampleper()._2048us(), SamplePeriod::_4096us => w.sampleper()._4096us(), SamplePeriod::_8192us => w.sampleper()._8192us(), SamplePeriod::_16384us => w.sampleper()._16384us(), SamplePeriod::_32ms => w.sampleper()._32ms(), SamplePeriod::_65ms => w.sampleper()._65ms(), SamplePeriod::_131ms => w.sampleper()._131ms(), }); // Enable peripheral r.enable.write(|w| w.enable().set_bit()); // Start sampling unsafe { r.tasks_start.write(|w| w.bits(1)) }; irq.disable(); irq.set_handler(|_| { let r = Self::regs(); r.intenclr.write(|w| w.reportrdy().clear()); WAKER.wake(); }); irq.enable(); Self { phantom: PhantomData } } /// Perform an asynchronous read of the decoder. /// The returned future can be awaited to obtain the number of steps. /// /// If the future is dropped, the read is cancelled. /// /// # Example /// /// ```no_run /// let irq = interrupt::take!(QDEC); /// let config = qdec::Config::default(); /// let mut q = Qdec::new(p.QDEC, p.P0_31, p.P0_30, config); /// let delta = q.read().await; /// ``` pub async fn read(&mut self) -> i16 { let t = Self::regs(); t.intenset.write(|w| w.reportrdy().set()); unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; let value = poll_fn(|cx| { WAKER.register(cx.waker()); if t.events_reportrdy.read().bits() == 0 { return Poll::Pending; } else { t.events_reportrdy.reset(); let acc = t.accread.read().bits(); Poll::Ready(acc as i16) } }) .await; value } fn regs() -> &'static pac::qdec::RegisterBlock { unsafe { &*pac::QDEC::ptr() } } } #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SamplePeriod { _128us, _256us, _512us, _1024us, _2048us, _4096us, _8192us, _16384us, _32ms, _65ms, _131ms, } #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum NumSamples { _10smpl, _40smpl, _80smpl, _120smpl, _160smpl, _200smpl, _240smpl, _280smpl, _1smpl, } #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum LedPolarity { ActiveHigh, ActiveLow, }