#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::gpio::vals as gpiovals;
use crate::pac::qdec::vals;
use crate::{interrupt, pac};
pub struct Qdec<'d> {
r: pac::qdec::Qdec,
state: &'static State,
_phantom: PhantomData<&'d ()>,
}
#[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,
}
}
}
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
T::regs().intenclr().write(|w| w.set_reportrdy(true));
T::state().waker.wake();
}
}
impl<'d> Qdec<'d> {
pub fn new<T: Instance>(
qdec: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: Peri<'d, impl GpioPin>,
b: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
Self::new_inner(qdec, a.into(), b.into(), None, config)
}
pub fn new_with_led<T: Instance>(
qdec: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: Peri<'d, impl GpioPin>,
b: Peri<'d, impl GpioPin>,
led: Peri<'d, impl GpioPin>,
config: Config,
) -> Self {
Self::new_inner(qdec, a.into(), b.into(), Some(led.into()), config)
}
fn new_inner<T: Instance>(
_p: Peri<'d, T>,
a: Peri<'d, AnyPin>,
b: Peri<'d, AnyPin>,
led: Option<Peri<'d, AnyPin>>,
config: Config,
) -> Self {
let r = T::regs();
a.conf().write(|w| {
w.set_input(gpiovals::Input::CONNECT);
w.set_pull(gpiovals::Pull::PULLUP);
});
b.conf().write(|w| {
w.set_input(gpiovals::Input::CONNECT);
w.set_pull(gpiovals::Pull::PULLUP);
});
r.psel().a().write_value(a.psel_bits());
r.psel().b().write_value(b.psel_bits());
if let Some(led_pin) = &led {
led_pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT));
r.psel().led().write_value(led_pin.psel_bits());
}
r.dbfen().write(|w| match config.debounce {
true => w.set_dbfen(true),
false => w.set_dbfen(false),
});
r.ledpol().write(|w| match config.led_polarity {
LedPolarity::ActiveHigh => w.set_ledpol(vals::Ledpol::ACTIVE_HIGH),
LedPolarity::ActiveLow => w.set_ledpol(vals::Ledpol::ACTIVE_LOW),
});
r.ledpre().write(|w| w.set_ledpre(config.led_pre_usecs.min(511)));
r.sampleper().write(|w| match config.period {
SamplePeriod::_128us => w.set_sampleper(vals::Sampleper::_128US),
SamplePeriod::_256us => w.set_sampleper(vals::Sampleper::_256US),
SamplePeriod::_512us => w.set_sampleper(vals::Sampleper::_512US),
SamplePeriod::_1024us => w.set_sampleper(vals::Sampleper::_1024US),
SamplePeriod::_2048us => w.set_sampleper(vals::Sampleper::_2048US),
SamplePeriod::_4096us => w.set_sampleper(vals::Sampleper::_4096US),
SamplePeriod::_8192us => w.set_sampleper(vals::Sampleper::_8192US),
SamplePeriod::_16384us => w.set_sampleper(vals::Sampleper::_16384US),
SamplePeriod::_32ms => w.set_sampleper(vals::Sampleper::_32MS),
SamplePeriod::_65ms => w.set_sampleper(vals::Sampleper::_65MS),
SamplePeriod::_131ms => w.set_sampleper(vals::Sampleper::_131MS),
});
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
r.enable().write(|w| w.set_enable(true));
r.tasks_start().write_value(1);
Self {
r: T::regs(),
state: T::state(),
_phantom: PhantomData,
}
}
pub async fn read(&mut self) -> i16 {
self.r.intenset().write(|w| w.set_reportrdy(true));
self.r.tasks_readclracc().write_value(1);
let state = self.state;
let r = self.r;
poll_fn(move |cx| {
state.waker.register(cx.waker());
if r.events_reportrdy().read() == 0 {
Poll::Pending
} else {
r.events_reportrdy().write_value(0);
let acc = r.accread().read();
Poll::Ready(acc as i16)
}
})
.await
}
}
#[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,
}
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> pac::qdec::Qdec;
fn state() -> &'static State;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_qdec {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::qdec::SealedInstance for peripherals::$type {
fn regs() -> pac::qdec::Qdec {
pac::$pac_type
}
fn state() -> &'static crate::qdec::State {
static STATE: crate::qdec::State = crate::qdec::State::new();
&STATE
}
}
impl crate::qdec::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}