embassy/embassy-nrf/src/qdec.rs

239 lines
6.7 KiB
Rust
Raw Normal View History

2023-02-01 00:48:33 +01:00
//! Quadrature decoder (QDEC) driver.
2022-05-07 00:46:36 +02:00
use core::future::poll_fn;
2022-05-07 00:46:36 +02:00
use core::task::Poll;
2022-06-12 22:15:44 +02:00
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
2022-05-07 00:46:36 +02:00
2022-06-12 22:15:44 +02:00
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};
2022-06-12 22:15:44 +02:00
2023-02-01 00:48:33 +01:00
/// Quadrature decoder driver.
2022-05-07 00:46:36 +02:00
pub struct Qdec<'d> {
_p: PeripheralRef<'d, QDEC>,
2022-05-07 00:46:36 +02:00
}
2023-02-01 00:48:33 +01:00
/// QDEC config
2022-05-07 00:46:36 +02:00
#[non_exhaustive]
pub struct Config {
2023-02-01 00:48:33 +01:00
/// Number of samples
2022-05-07 00:46:36 +02:00
pub num_samples: NumSamples,
2023-02-01 00:48:33 +01:00
/// Sample period
2022-05-07 00:46:36 +02:00
pub period: SamplePeriod,
2023-02-01 00:48:33 +01:00
/// Set LED output pin polarity
2022-05-07 00:46:36 +02:00
pub led_polarity: LedPolarity,
2023-02-01 00:48:33 +01:00
/// Enable/disable input debounce filters
2022-05-07 00:46:36 +02:00
pub debounce: bool,
2023-02-01 00:48:33 +01:00
/// Time period the LED is switched ON prior to sampling (0..511 us).
2022-05-07 00:46:36 +02:00
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> {
2023-02-01 00:48:33 +01:00
/// Create a new QDEC.
2022-05-07 00:46:36 +02:00
pub fn new(
qdec: impl Peripheral<P = QDEC> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd,
2022-05-07 00:46:36 +02:00
config: Config,
) -> Self {
2022-07-23 14:27:45 +02:00
into_ref!(a, b);
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config)
2022-05-07 00:46:36 +02:00
}
2023-02-01 00:48:33 +01:00
/// Create a new QDEC, with a pin for LED output.
2022-05-07 00:46:36 +02:00
pub fn new_with_led(
qdec: impl Peripheral<P = QDEC> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd,
led: impl Peripheral<P = impl GpioPin> + 'd,
2022-05-07 00:46:36 +02:00
config: Config,
) -> Self {
2022-07-23 14:27:45 +02:00
into_ref!(a, b, led);
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config)
2022-05-07 00:46:36 +02:00
}
fn new_inner(
p: impl Peripheral<P = QDEC> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
a: PeripheralRef<'d, AnyPin>,
b: PeripheralRef<'d, AnyPin>,
led: Option<PeripheralRef<'d, AnyPin>>,
2022-05-07 00:46:36 +02:00
config: Config,
) -> Self {
into_ref!(p, irq);
2022-05-07 00:46:36 +02:00
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());
// Start sampling
unsafe { r.tasks_start.write(|w| w.bits(1)) };
2022-05-07 00:46:36 +02:00
irq.disable();
irq.set_handler(|_| {
let r = Self::regs();
r.intenclr.write(|w| w.reportrdy().clear());
WAKER.wake();
});
irq.enable();
Self { _p: p }
2022-05-07 00:46:36 +02:00
}
/// 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 {
let t = Self::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());
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;
value
}
fn regs() -> &'static pac::qdec::RegisterBlock {
unsafe { &*pac::QDEC::ptr() }
}
}
2023-02-01 00:48:33 +01:00
/// Sample period
2022-05-07 00:46:36 +02:00
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum SamplePeriod {
2023-02-01 00:48:33 +01:00
/// 128 us
2022-05-07 00:46:36 +02:00
_128us,
2023-02-01 00:48:33 +01:00
/// 256 us
2022-05-07 00:46:36 +02:00
_256us,
2023-02-01 00:48:33 +01:00
/// 512 us
2022-05-07 00:46:36 +02:00
_512us,
2023-02-01 00:48:33 +01:00
/// 1024 us
2022-05-07 00:46:36 +02:00
_1024us,
2023-02-01 00:48:33 +01:00
/// 2048 us
2022-05-07 00:46:36 +02:00
_2048us,
2023-02-01 00:48:33 +01:00
/// 4096 us
2022-05-07 00:46:36 +02:00
_4096us,
2023-02-01 00:48:33 +01:00
/// 8192 us
2022-05-07 00:46:36 +02:00
_8192us,
2023-02-01 00:48:33 +01:00
/// 16384 us
2022-05-07 00:46:36 +02:00
_16384us,
2023-02-01 00:48:33 +01:00
/// 32 ms
2022-05-07 00:46:36 +02:00
_32ms,
2023-02-01 00:48:33 +01:00
/// 65 ms
2022-05-07 00:46:36 +02:00
_65ms,
2023-02-01 00:48:33 +01:00
/// 131 ms
2022-05-07 00:46:36 +02:00
_131ms,
}
2023-02-01 00:48:33 +01:00
/// Number of samples taken.
2022-05-07 00:46:36 +02:00
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum NumSamples {
2023-02-01 00:48:33 +01:00
/// 10 samples
2022-05-07 00:46:36 +02:00
_10smpl,
2023-02-01 00:48:33 +01:00
/// 40 samples
2022-05-07 00:46:36 +02:00
_40smpl,
2023-02-01 00:48:33 +01:00
/// 80 samples
2022-05-07 00:46:36 +02:00
_80smpl,
2023-02-01 00:48:33 +01:00
/// 120 samples
2022-05-07 00:46:36 +02:00
_120smpl,
2023-02-01 00:48:33 +01:00
/// 160 samples
2022-05-07 00:46:36 +02:00
_160smpl,
2023-02-01 00:48:33 +01:00
/// 200 samples
2022-05-07 00:46:36 +02:00
_200smpl,
2023-02-01 00:48:33 +01:00
/// 240 samples
2022-05-07 00:46:36 +02:00
_240smpl,
2023-02-01 00:48:33 +01:00
/// 280 samples
2022-05-07 00:46:36 +02:00
_280smpl,
2023-02-01 00:48:33 +01:00
/// 1 sample
2022-05-07 00:46:36 +02:00
_1smpl,
}
2023-02-01 00:48:33 +01:00
/// LED polarity
2022-05-07 00:46:36 +02:00
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum LedPolarity {
2023-02-01 00:48:33 +01:00
/// Active high (a high output turns on the LED).
2022-05-07 00:46:36 +02:00
ActiveHigh,
2023-02-01 00:48:33 +01:00
/// Active low (a low output turns on the LED).
2022-05-07 00:46:36 +02:00
ActiveLow,
}