From 840bb2952e68465ff5e58846db5a4f1ced76834b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Sat, 7 May 2022 00:46:36 +0200 Subject: [PATCH 1/3] Add qdec module --- embassy-nrf/src/chips/nrf52805.rs | 3 + embassy-nrf/src/chips/nrf52810.rs | 3 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52820.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/lib.rs | 2 + embassy-nrf/src/qdec.rs | 222 ++++++++++++++++++++++++++++++ examples/nrf/src/bin/qdec.rs | 29 ++++ 10 files changed, 274 insertions(+) create mode 100644 embassy-nrf/src/qdec.rs create mode 100644 examples/nrf/src/bin/qdec.rs diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 68989648..c917dcdd 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -122,6 +122,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // QDEC + QDEC, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index b3b3593b..922b683f 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -125,6 +125,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // QDEC + QDEC, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 7551492c..d23ab5b3 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -125,6 +125,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // QDEC + QDEC, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 136ef4ec..e94ddbb1 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -123,6 +123,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // QDEC + QDEC, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index f1216cf2..fec7e10d 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -135,6 +135,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // QDEC + QDEC, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 35cf4224..e09c7718 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -155,6 +155,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // QDEC + QDEC, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index d20abbfb..2e71e04b 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -27,6 +27,9 @@ embassy_hal_common::peripherals! { // QSPI QSPI, + // QDEC + QDEC, + // UARTE UARTE0, UARTE1, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 3b180902..46234b4b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -73,6 +73,8 @@ pub mod nvmc; 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")))] +pub mod qdec; #[cfg(feature = "nrf52840")] pub mod qspi; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs new file mode 100644 index 00000000..c124ba35 --- /dev/null +++ b/embassy-nrf/src/qdec.rs @@ -0,0 +1,222 @@ +//! Quadrature decoder interface + +use crate::gpio::sealed::Pin as _; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt; +use crate::pac; +use crate::peripherals::QDEC; + +use core::marker::PhantomData; +use core::task::Poll; +use embassy::interrupt::InterruptExt; +use embassy::util::Unborrow; +use embassy::waitqueue::AtomicWaker; +use embassy_hal_common::{drop::OnDrop, unborrow}; +use futures::future::poll_fn; + +/// 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!(a, b); + Self::new_inner(qdec, irq, a.degrade(), b.degrade(), 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!(a, b, led); + Self::new_inner( + qdec, + irq, + a.degrade(), + b.degrade(), + Some(led.degrade()), + config, + ) + } + + fn new_inner( + _t: impl Unborrow + 'd, + irq: impl Unborrow + 'd, + a: AnyPin, + b: 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()); + + 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 { + // In case the future is dropped, stop the task and reset events. + let on_drop = OnDrop::new(|| { + let t = Self::regs(); + t.tasks_stop.write(|w| unsafe { w.bits(1) }); + t.events_reportrdy.reset(); + }); + + let t = Self::regs(); + t.intenset.write(|w| w.reportrdy().set()); + unsafe { t.tasks_start.write(|w| w.bits(1)) }; + 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; + on_drop.defuse(); + 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, +} diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf/src/bin/qdec.rs new file mode 100644 index 00000000..9c670cea --- /dev/null +++ b/examples/nrf/src/bin/qdec.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy::executor::Spawner; +use embassy_nrf::{ + interrupt, + qdec::{self, Qdec}, + Peripherals, +}; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let irq = interrupt::take!(QDEC); + let config = qdec::Config::default(); + let mut rotary = Qdec::new(p.QDEC, irq, p.P1_13, p.P0_12, config); + // let mut rotary = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); + + info!("Turn rotary encoder!"); + let mut value = 0; + loop { + value += rotary.read().await; + info!("Value: {}", value); + } +} From 108a98136096f8b530266aa6687bdbbed4a6a382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 7 May 2022 01:15:01 +0200 Subject: [PATCH 2/3] Change example pins --- examples/nrf/src/bin/qdec.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf/src/bin/qdec.rs index 9c670cea..bf5b1146 100644 --- a/examples/nrf/src/bin/qdec.rs +++ b/examples/nrf/src/bin/qdec.rs @@ -17,13 +17,12 @@ use panic_probe as _; async fn main(_spawner: Spawner, p: Peripherals) { let irq = interrupt::take!(QDEC); let config = qdec::Config::default(); - let mut rotary = Qdec::new(p.QDEC, irq, p.P1_13, p.P0_12, config); - // let mut rotary = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); + let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); info!("Turn rotary encoder!"); let mut value = 0; loop { - value += rotary.read().await; + value += rotary_enc.read().await; info!("Value: {}", value); } } From 93cbd079ec1f5b8371fb4d52b00e0a404403f73d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Thu, 12 May 2022 15:35:32 +0200 Subject: [PATCH 3/3] Remove OnDrop handler, start sampling in new --- embassy-nrf/src/qdec.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index c124ba35..c2681538 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -11,7 +11,7 @@ use core::task::Poll; use embassy::interrupt::InterruptExt; use embassy::util::Unborrow; use embassy::waitqueue::AtomicWaker; -use embassy_hal_common::{drop::OnDrop, unborrow}; +use embassy_hal_common::unborrow; use futures::future::poll_fn; /// Quadrature decoder @@ -128,6 +128,9 @@ impl<'d> Qdec<'d> { // 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(); @@ -155,16 +158,8 @@ impl<'d> Qdec<'d> { /// let delta = q.read().await; /// ``` pub async fn read(&mut self) -> i16 { - // In case the future is dropped, stop the task and reset events. - let on_drop = OnDrop::new(|| { - let t = Self::regs(); - t.tasks_stop.write(|w| unsafe { w.bits(1) }); - t.events_reportrdy.reset(); - }); - let t = Self::regs(); t.intenset.write(|w| w.reportrdy().set()); - unsafe { t.tasks_start.write(|w| w.bits(1)) }; unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; let value = poll_fn(|cx| { @@ -178,7 +173,6 @@ impl<'d> Qdec<'d> { } }) .await; - on_drop.defuse(); value }