nrf/qdec: make available on all chips, use Instance trait, switch to new interrupt binding.

This commit is contained in:
Dario Nieuwenhuis 2023-03-05 21:28:13 +01:00
parent f8f1d3bcf0
commit c66b28e759
11 changed files with 108 additions and 38 deletions

View File

@ -140,6 +140,8 @@ impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
impl_qdec!(QDEC, QDEC, QDEC);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View File

@ -150,6 +150,8 @@ impl_pwm!(PWM0, PWM0, PWM0);
impl_pdm!(PDM, PDM, PDM); impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View File

@ -152,6 +152,8 @@ impl_pwm!(PWM0, PWM0, PWM0);
impl_pdm!(PDM, PDM, PDM); impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View File

@ -153,6 +153,8 @@ impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);
impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER3, TIMER3, TIMER3, extended);
impl_qdec!(QDEC, QDEC, QDEC);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -173,6 +173,8 @@ impl_pwm!(PWM2, PWM2, PWM2);
impl_pdm!(PDM, PDM, PDM); impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View File

@ -199,6 +199,8 @@ impl_pwm!(PWM3, PWM3, PWM3);
impl_pdm!(PDM, PDM, PDM); impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View File

@ -210,6 +210,8 @@ impl_qspi!(QSPI, QSPI, QSPI);
impl_pdm!(PDM, PDM, PDM); impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -37,7 +37,7 @@ pub mod pac {
pdm0_ns as pdm, pdm0_ns as pdm,
power_ns as power, power_ns as power,
pwm0_ns as pwm0, pwm0_ns as pwm0,
qdec0_ns as qdec0, qdec0_ns as qdec,
qspi_ns as qspi, qspi_ns as qspi,
regulators_ns as regulators, regulators_ns as regulators,
reset_ns as reset, reset_ns as reset,
@ -256,6 +256,10 @@ embassy_hal_common::peripherals! {
// PDM // PDM
PDM0, PDM0,
// QDEC
QDEC0,
QDEC1,
// GPIOTE // GPIOTE
GPIOTE_CH0, GPIOTE_CH0,
GPIOTE_CH1, GPIOTE_CH1,
@ -403,6 +407,9 @@ impl_qspi!(QSPI, QSPI, QSPI);
impl_pdm!(PDM0, PDM0, PDM0); impl_pdm!(PDM0, PDM0, PDM0);
impl_qdec!(QDEC0, QDEC0, QDEC0);
impl_qdec!(QDEC1, QDEC1, QDEC1);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
#[cfg(feature = "nfc-pins-as-gpio")] #[cfg(feature = "nfc-pins-as-gpio")]

View File

@ -57,7 +57,7 @@ pub mod pdm;
pub mod ppi; pub mod ppi;
#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod pwm; 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; pub mod qdec;
#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))]
pub mod qspi; pub mod qspi;

View File

@ -1,20 +1,22 @@
//! Quadrature decoder (QDEC) driver. //! Quadrature decoder (QDEC) driver.
#![macro_use]
use core::future::poll_fn; use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::InterruptExt; use crate::interrupt::InterruptExt;
use crate::peripherals::QDEC; use crate::{interrupt, Peripheral};
use crate::{interrupt, pac, Peripheral};
/// Quadrature decoder driver. /// Quadrature decoder driver.
pub struct Qdec<'d> { pub struct Qdec<'d, T: Instance> {
_p: PeripheralRef<'d, QDEC>, _p: PeripheralRef<'d, T>,
} }
/// QDEC config /// QDEC config
@ -44,44 +46,52 @@ impl Default for Config {
} }
} }
static WAKER: AtomicWaker = AtomicWaker::new(); /// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<'d> Qdec<'d> { impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> {
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. /// Create a new QDEC.
pub fn new( pub fn new(
qdec: impl Peripheral<P = QDEC> + 'd, qdec: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd, _irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd, a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd, b: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
) -> Self { ) -> Self {
into_ref!(a, b); into_ref!(qdec, a, b);
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) Self::new_inner(qdec, a.map_into(), b.map_into(), None, config)
} }
/// Create a new QDEC, with a pin for LED output. /// Create a new QDEC, with a pin for LED output.
pub fn new_with_led( pub fn new_with_led(
qdec: impl Peripheral<P = QDEC> + 'd, qdec: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd, _irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd, a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd, b: impl Peripheral<P = impl GpioPin> + 'd,
led: impl Peripheral<P = impl GpioPin> + 'd, led: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
) -> Self { ) -> Self {
into_ref!(a, b, led); into_ref!(qdec, a, b, led);
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config) Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config)
} }
fn new_inner( fn new_inner(
p: impl Peripheral<P = QDEC> + 'd, p: PeripheralRef<'d, T>,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
a: PeripheralRef<'d, AnyPin>, a: PeripheralRef<'d, AnyPin>,
b: PeripheralRef<'d, AnyPin>, b: PeripheralRef<'d, AnyPin>,
led: Option<PeripheralRef<'d, AnyPin>>, led: Option<PeripheralRef<'d, AnyPin>>,
config: Config, config: Config,
) -> Self { ) -> Self {
into_ref!(p, irq); let r = T::regs();
let r = Self::regs();
// Select pins. // Select pins.
a.conf().write(|w| w.input().connect().pull().pullup()); a.conf().write(|w| w.input().connect().pull().pullup());
@ -124,20 +134,15 @@ impl<'d> Qdec<'d> {
SamplePeriod::_131ms => w.sampleper()._131ms(), SamplePeriod::_131ms => w.sampleper()._131ms(),
}); });
unsafe { T::Interrupt::steal() }.unpend();
unsafe { T::Interrupt::steal() }.enable();
// Enable peripheral // Enable peripheral
r.enable.write(|w| w.enable().set_bit()); r.enable.write(|w| w.enable().set_bit());
// Start sampling // Start sampling
unsafe { r.tasks_start.write(|w| w.bits(1)) }; 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 } Self { _p: p }
} }
@ -155,12 +160,12 @@ impl<'d> Qdec<'d> {
/// let delta = q.read().await; /// let delta = q.read().await;
/// ``` /// ```
pub async fn read(&mut self) -> i16 { pub async fn read(&mut self) -> i16 {
let t = Self::regs(); let t = T::regs();
t.intenset.write(|w| w.reportrdy().set()); t.intenset.write(|w| w.reportrdy().set());
unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; unsafe { t.tasks_readclracc.write(|w| w.bits(1)) };
let value = poll_fn(|cx| { let value = poll_fn(|cx| {
WAKER.register(cx.waker()); T::state().waker.register(cx.waker());
if t.events_reportrdy.read().bits() == 0 { if t.events_reportrdy.read().bits() == 0 {
return Poll::Pending; return Poll::Pending;
} else { } else {
@ -172,10 +177,6 @@ impl<'d> Qdec<'d> {
.await; .await;
value value
} }
fn regs() -> &'static pac::qdec::RegisterBlock {
unsafe { &*pac::QDEC::ptr() }
}
} }
/// Sample period /// Sample period
@ -236,3 +237,48 @@ pub enum LedPolarity {
/// Active low (a low output turns on the LED). /// Active low (a low output turns on the LED).
ActiveLow, 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<P = Self> + 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;
}
};
}

View File

@ -4,16 +4,19 @@
use defmt::info; use defmt::info;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_nrf::interrupt;
use embassy_nrf::qdec::{self, Qdec}; use embassy_nrf::qdec::{self, Qdec};
use embassy_nrf::{bind_interrupts, peripherals};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
QDEC => qdec::InterruptHandler<peripherals::QDEC>;
});
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default()); let p = embassy_nrf::init(Default::default());
let irq = interrupt::take!(QDEC);
let config = qdec::Config::default(); 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!"); info!("Turn rotary encoder!");
let mut value = 0; let mut value = 0;