diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 3c74a2a63..185cda430 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -140,6 +140,8 @@ impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index c6ccb6a0e..1e3c054a4 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -150,6 +150,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 7d1bce1c0..3bb44171e 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -152,6 +152,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 81b07f32c..b28778f33 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -153,6 +153,8 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index ce19a18e8..00dc9fd8f 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -173,6 +173,8 @@ impl_pwm!(PWM2, PWM2, PWM2); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 08b82021d..345608c9d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -199,6 +199,8 @@ impl_pwm!(PWM3, PWM3, PWM3); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4e8b6d9ee..630f01aa9 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -210,6 +210,8 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 050612b1c..34f96800f 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -37,7 +37,7 @@ pub mod pac { pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, - qdec0_ns as qdec0, + qdec0_ns as qdec, qspi_ns as qspi, regulators_ns as regulators, reset_ns as reset, @@ -256,6 +256,10 @@ embassy_hal_common::peripherals! { // PDM PDM0, + // QDEC + QDEC0, + QDEC1, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -403,6 +407,9 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM0, PDM0, PDM0); +impl_qdec!(QDEC0, QDEC0, QDEC0); +impl_qdec!(QDEC1, QDEC1, QDEC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 17aa5ad15..feefa2486 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -57,7 +57,7 @@ pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; -#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] +#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))] pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index c01babca3..4d2a09198 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,20 +1,22 @@ //! Quadrature decoder (QDEC) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; 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, Peripheral}; +use crate::{interrupt, Peripheral}; /// Quadrature decoder driver. -pub struct Qdec<'d> { - _p: PeripheralRef<'d, QDEC>, +pub struct Qdec<'d, T: Instance> { + _p: PeripheralRef<'d, T>, } /// QDEC config @@ -44,44 +46,52 @@ impl Default for Config { } } -static WAKER: AtomicWaker = AtomicWaker::new(); +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} -impl<'d> Qdec<'d> { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.reportrdy().clear()); + T::state().waker.wake(); + } +} + +impl<'d, T: Instance> Qdec<'d, T> { /// Create a new QDEC. pub fn new( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) + into_ref!(qdec, a, b); + Self::new_inner(qdec, a.map_into(), b.map_into(), None, config) } /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, led: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b, led); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config) + into_ref!(qdec, a, b, led); + Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config) } fn new_inner( - p: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + p: PeripheralRef<'d, T>, a: PeripheralRef<'d, AnyPin>, b: PeripheralRef<'d, AnyPin>, led: Option>, config: Config, ) -> Self { - into_ref!(p, irq); - let r = Self::regs(); + let r = T::regs(); // Select pins. a.conf().write(|w| w.input().connect().pull().pullup()); @@ -124,20 +134,15 @@ impl<'d> Qdec<'d> { SamplePeriod::_131ms => w.sampleper()._131ms(), }); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + // 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 { _p: p } } @@ -155,12 +160,12 @@ impl<'d> Qdec<'d> { /// let delta = q.read().await; /// ``` pub async fn read(&mut self) -> i16 { - let t = Self::regs(); + let t = T::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()); + T::state().waker.register(cx.waker()); if t.events_reportrdy.read().bits() == 0 { return Poll::Pending; } else { @@ -172,10 +177,6 @@ impl<'d> Qdec<'d> { .await; value } - - fn regs() -> &'static pac::qdec::RegisterBlock { - unsafe { &*pac::QDEC::ptr() } - } } /// Sample period @@ -236,3 +237,48 @@ pub enum LedPolarity { /// Active low (a low output turns on the LED). ActiveLow, } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::qdec::RegisterBlock; + fn state() -> &'static State; + } +} + +/// qdec peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_qdec { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::qdec::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::qdec::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::qdec::sealed::State { + static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new(); + &STATE + } + } + impl crate::qdec::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs index 600bba07a..59783d312 100644 --- a/examples/nrf52840/src/bin/qdec.rs +++ b/examples/nrf52840/src/bin/qdec.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::qdec::{self, Qdec}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + QDEC => qdec::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(QDEC); let config = qdec::Config::default(); - let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); + let mut rotary_enc = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config); info!("Turn rotary encoder!"); let mut value = 0;