embassy_nrf/
qdec.rs

1//! Quadrature decoder (QDEC) driver.
2
3#![macro_use]
4
5use core::future::poll_fn;
6use core::marker::PhantomData;
7use core::task::Poll;
8
9use embassy_hal_internal::{Peri, PeripheralType};
10use embassy_sync::waitqueue::AtomicWaker;
11
12use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};
13use crate::interrupt::typelevel::Interrupt;
14use crate::pac::gpio::vals as gpiovals;
15use crate::pac::qdec::vals;
16use crate::{interrupt, pac};
17
18/// Quadrature decoder driver.
19pub struct Qdec<'d, T: Instance> {
20    _p: Peri<'d, T>,
21}
22
23/// QDEC config
24#[non_exhaustive]
25pub struct Config {
26    /// Number of samples
27    pub num_samples: NumSamples,
28    /// Sample period
29    pub period: SamplePeriod,
30    /// Set LED output pin polarity
31    pub led_polarity: LedPolarity,
32    /// Enable/disable input debounce filters
33    pub debounce: bool,
34    /// Time period the LED is switched ON prior to sampling (0..511 us).
35    pub led_pre_usecs: u16,
36}
37
38impl Default for Config {
39    fn default() -> Self {
40        Self {
41            num_samples: NumSamples::_1smpl,
42            period: SamplePeriod::_256us,
43            led_polarity: LedPolarity::ActiveHigh,
44            debounce: true,
45            led_pre_usecs: 0,
46        }
47    }
48}
49
50/// Interrupt handler.
51pub struct InterruptHandler<T: Instance> {
52    _phantom: PhantomData<T>,
53}
54
55impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
56    unsafe fn on_interrupt() {
57        T::regs().intenclr().write(|w| w.set_reportrdy(true));
58        T::state().waker.wake();
59    }
60}
61
62impl<'d, T: Instance> Qdec<'d, T> {
63    /// Create a new QDEC.
64    pub fn new(
65        qdec: Peri<'d, T>,
66        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
67        a: Peri<'d, impl GpioPin>,
68        b: Peri<'d, impl GpioPin>,
69        config: Config,
70    ) -> Self {
71        Self::new_inner(qdec, a.into(), b.into(), None, config)
72    }
73
74    /// Create a new QDEC, with a pin for LED output.
75    pub fn new_with_led(
76        qdec: Peri<'d, T>,
77        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
78        a: Peri<'d, impl GpioPin>,
79        b: Peri<'d, impl GpioPin>,
80        led: Peri<'d, impl GpioPin>,
81        config: Config,
82    ) -> Self {
83        Self::new_inner(qdec, a.into(), b.into(), Some(led.into()), config)
84    }
85
86    fn new_inner(
87        p: Peri<'d, T>,
88        a: Peri<'d, AnyPin>,
89        b: Peri<'d, AnyPin>,
90        led: Option<Peri<'d, AnyPin>>,
91        config: Config,
92    ) -> Self {
93        let r = T::regs();
94
95        // Select pins.
96        a.conf().write(|w| {
97            w.set_input(gpiovals::Input::CONNECT);
98            w.set_pull(gpiovals::Pull::PULLUP);
99        });
100        b.conf().write(|w| {
101            w.set_input(gpiovals::Input::CONNECT);
102            w.set_pull(gpiovals::Pull::PULLUP);
103        });
104        r.psel().a().write_value(a.psel_bits());
105        r.psel().b().write_value(b.psel_bits());
106        if let Some(led_pin) = &led {
107            led_pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT));
108            r.psel().led().write_value(led_pin.psel_bits());
109        }
110
111        // Enables/disable input debounce filters
112        r.dbfen().write(|w| match config.debounce {
113            true => w.set_dbfen(true),
114            false => w.set_dbfen(false),
115        });
116
117        // Set LED output pin polarity
118        r.ledpol().write(|w| match config.led_polarity {
119            LedPolarity::ActiveHigh => w.set_ledpol(vals::Ledpol::ACTIVE_HIGH),
120            LedPolarity::ActiveLow => w.set_ledpol(vals::Ledpol::ACTIVE_LOW),
121        });
122
123        // Set time period the LED is switched ON prior to sampling (0..511 us).
124        r.ledpre().write(|w| w.set_ledpre(config.led_pre_usecs.min(511)));
125
126        // Set sample period
127        r.sampleper().write(|w| match config.period {
128            SamplePeriod::_128us => w.set_sampleper(vals::Sampleper::_128US),
129            SamplePeriod::_256us => w.set_sampleper(vals::Sampleper::_256US),
130            SamplePeriod::_512us => w.set_sampleper(vals::Sampleper::_512US),
131            SamplePeriod::_1024us => w.set_sampleper(vals::Sampleper::_1024US),
132            SamplePeriod::_2048us => w.set_sampleper(vals::Sampleper::_2048US),
133            SamplePeriod::_4096us => w.set_sampleper(vals::Sampleper::_4096US),
134            SamplePeriod::_8192us => w.set_sampleper(vals::Sampleper::_8192US),
135            SamplePeriod::_16384us => w.set_sampleper(vals::Sampleper::_16384US),
136            SamplePeriod::_32ms => w.set_sampleper(vals::Sampleper::_32MS),
137            SamplePeriod::_65ms => w.set_sampleper(vals::Sampleper::_65MS),
138            SamplePeriod::_131ms => w.set_sampleper(vals::Sampleper::_131MS),
139        });
140
141        T::Interrupt::unpend();
142        unsafe { T::Interrupt::enable() };
143
144        // Enable peripheral
145        r.enable().write(|w| w.set_enable(true));
146
147        // Start sampling
148        r.tasks_start().write_value(1);
149
150        Self { _p: p }
151    }
152
153    /// Perform an asynchronous read of the decoder.
154    /// The returned future can be awaited to obtain the number of steps.
155    ///
156    /// If the future is dropped, the read is cancelled.
157    ///
158    /// # Example
159    ///
160    /// ```no_run
161    /// use embassy_nrf::qdec::{self, Qdec};
162    /// use embassy_nrf::{bind_interrupts, peripherals};
163    ///
164    /// bind_interrupts!(struct Irqs {
165    ///     QDEC => qdec::InterruptHandler<peripherals::QDEC>;
166    /// });
167    ///
168    /// # async {
169    /// # let p: embassy_nrf::Peripherals = todo!();
170    /// let config = qdec::Config::default();
171    /// let mut q = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config);
172    /// let delta = q.read().await;
173    /// # };
174    /// ```
175    pub async fn read(&mut self) -> i16 {
176        let t = T::regs();
177        t.intenset().write(|w| w.set_reportrdy(true));
178        t.tasks_readclracc().write_value(1);
179
180        poll_fn(|cx| {
181            T::state().waker.register(cx.waker());
182            if t.events_reportrdy().read() == 0 {
183                Poll::Pending
184            } else {
185                t.events_reportrdy().write_value(0);
186                let acc = t.accread().read();
187                Poll::Ready(acc as i16)
188            }
189        })
190        .await
191    }
192}
193
194/// Sample period
195#[derive(Debug, Eq, PartialEq, Clone, Copy)]
196pub enum SamplePeriod {
197    /// 128 us
198    _128us,
199    /// 256 us
200    _256us,
201    /// 512 us
202    _512us,
203    /// 1024 us
204    _1024us,
205    /// 2048 us
206    _2048us,
207    /// 4096 us
208    _4096us,
209    /// 8192 us
210    _8192us,
211    /// 16384 us
212    _16384us,
213    /// 32 ms
214    _32ms,
215    /// 65 ms
216    _65ms,
217    /// 131 ms
218    _131ms,
219}
220
221/// Number of samples taken.
222#[derive(Debug, Eq, PartialEq, Clone, Copy)]
223pub enum NumSamples {
224    /// 10 samples
225    _10smpl,
226    /// 40 samples
227    _40smpl,
228    /// 80 samples
229    _80smpl,
230    /// 120 samples
231    _120smpl,
232    /// 160 samples
233    _160smpl,
234    /// 200 samples
235    _200smpl,
236    /// 240 samples
237    _240smpl,
238    /// 280 samples
239    _280smpl,
240    /// 1 sample
241    _1smpl,
242}
243
244/// LED polarity
245#[derive(Debug, Eq, PartialEq, Clone, Copy)]
246pub enum LedPolarity {
247    /// Active high (a high output turns on the LED).
248    ActiveHigh,
249    /// Active low (a low output turns on the LED).
250    ActiveLow,
251}
252
253/// Peripheral static state
254pub(crate) struct State {
255    waker: AtomicWaker,
256}
257
258impl State {
259    pub(crate) const fn new() -> Self {
260        Self {
261            waker: AtomicWaker::new(),
262        }
263    }
264}
265
266pub(crate) trait SealedInstance {
267    fn regs() -> pac::qdec::Qdec;
268    fn state() -> &'static State;
269}
270
271/// qdec peripheral instance.
272#[allow(private_bounds)]
273pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
274    /// Interrupt for this peripheral.
275    type Interrupt: interrupt::typelevel::Interrupt;
276}
277
278macro_rules! impl_qdec {
279    ($type:ident, $pac_type:ident, $irq:ident) => {
280        impl crate::qdec::SealedInstance for peripherals::$type {
281            fn regs() -> pac::qdec::Qdec {
282                pac::$pac_type
283            }
284            fn state() -> &'static crate::qdec::State {
285                static STATE: crate::qdec::State = crate::qdec::State::new();
286                &STATE
287            }
288        }
289        impl crate::qdec::Instance for peripherals::$type {
290            type Interrupt = crate::interrupt::typelevel::$irq;
291        }
292    };
293}