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