aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/chips/nrf52805.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs3
-rw-r--r--embassy-nrf/src/lib.rs2
-rw-r--r--embassy-nrf/src/qdec.rs222
-rw-r--r--examples/nrf/src/bin/qdec.rs29
10 files changed, 274 insertions, 0 deletions
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index 689896485..c917dcdd0 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -122,6 +122,9 @@ embassy_hal_common::peripherals! {
122 122
123 // TEMP 123 // TEMP
124 TEMP, 124 TEMP,
125
126 // QDEC
127 QDEC,
125} 128}
126 129
127impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 130impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index b3b3593bb..922b683f9 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -125,6 +125,9 @@ embassy_hal_common::peripherals! {
125 125
126 // TEMP 126 // TEMP
127 TEMP, 127 TEMP,
128
129 // QDEC
130 QDEC,
128} 131}
129 132
130impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 133impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 7551492c3..d23ab5b39 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -125,6 +125,9 @@ embassy_hal_common::peripherals! {
125 125
126 // TEMP 126 // TEMP
127 TEMP, 127 TEMP,
128
129 // QDEC
130 QDEC,
128} 131}
129 132
130impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 133impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index 136ef4ec9..e94ddbb14 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -123,6 +123,9 @@ embassy_hal_common::peripherals! {
123 123
124 // TEMP 124 // TEMP
125 TEMP, 125 TEMP,
126
127 // QDEC
128 QDEC,
126} 129}
127 130
128#[cfg(feature = "nightly")] 131#[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index f1216cf21..fec7e10de 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -135,6 +135,9 @@ embassy_hal_common::peripherals! {
135 135
136 // TEMP 136 // TEMP
137 TEMP, 137 TEMP,
138
139 // QDEC
140 QDEC,
138} 141}
139 142
140impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 143impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 35cf4224d..e09c77187 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -155,6 +155,9 @@ embassy_hal_common::peripherals! {
155 155
156 // TEMP 156 // TEMP
157 TEMP, 157 TEMP,
158
159 // QDEC
160 QDEC,
158} 161}
159 162
160#[cfg(feature = "nightly")] 163#[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index d20abbfbd..2e71e04b0 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -27,6 +27,9 @@ embassy_hal_common::peripherals! {
27 // QSPI 27 // QSPI
28 QSPI, 28 QSPI,
29 29
30 // QDEC
31 QDEC,
32
30 // UARTE 33 // UARTE
31 UARTE0, 34 UARTE0,
32 UARTE1, 35 UARTE1,
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 3b1809023..46234b4b5 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -73,6 +73,8 @@ pub mod nvmc;
73pub mod ppi; 73pub mod ppi;
74#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] 74#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
75pub mod pwm; 75pub mod pwm;
76#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))]
77pub mod qdec;
76#[cfg(feature = "nrf52840")] 78#[cfg(feature = "nrf52840")]
77pub mod qspi; 79pub mod qspi;
78#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] 80#[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 000000000..c124ba35e
--- /dev/null
+++ b/embassy-nrf/src/qdec.rs
@@ -0,0 +1,222 @@
1//! Quadrature decoder interface
2
3use crate::gpio::sealed::Pin as _;
4use crate::gpio::{AnyPin, Pin as GpioPin};
5use crate::interrupt;
6use crate::pac;
7use crate::peripherals::QDEC;
8
9use core::marker::PhantomData;
10use core::task::Poll;
11use embassy::interrupt::InterruptExt;
12use embassy::util::Unborrow;
13use embassy::waitqueue::AtomicWaker;
14use embassy_hal_common::{drop::OnDrop, unborrow};
15use futures::future::poll_fn;
16
17/// Quadrature decoder
18pub struct Qdec<'d> {
19 phantom: PhantomData<&'d QDEC>,
20}
21
22#[non_exhaustive]
23pub struct Config {
24 pub num_samples: NumSamples,
25 pub period: SamplePeriod,
26 pub led_polarity: LedPolarity,
27 pub debounce: bool,
28 pub led_pre_usecs: u16,
29}
30
31impl Default for Config {
32 fn default() -> Self {
33 Self {
34 num_samples: NumSamples::_1smpl,
35 period: SamplePeriod::_256us,
36 led_polarity: LedPolarity::ActiveHigh,
37 debounce: true,
38 led_pre_usecs: 0,
39 }
40 }
41}
42
43static WAKER: AtomicWaker = AtomicWaker::new();
44
45impl<'d> Qdec<'d> {
46 pub fn new(
47 qdec: impl Unborrow<Target = QDEC> + 'd,
48 irq: impl Unborrow<Target = interrupt::QDEC> + 'd,
49 a: impl Unborrow<Target = impl GpioPin> + 'd,
50 b: impl Unborrow<Target = impl GpioPin> + 'd,
51 config: Config,
52 ) -> Self {
53 unborrow!(a, b);
54 Self::new_inner(qdec, irq, a.degrade(), b.degrade(), None, config)
55 }
56
57 pub fn new_with_led(
58 qdec: impl Unborrow<Target = QDEC> + 'd,
59 irq: impl Unborrow<Target = interrupt::QDEC> + 'd,
60 a: impl Unborrow<Target = impl GpioPin> + 'd,
61 b: impl Unborrow<Target = impl GpioPin> + 'd,
62 led: impl Unborrow<Target = impl GpioPin> + 'd,
63 config: Config,
64 ) -> Self {
65 unborrow!(a, b, led);
66 Self::new_inner(
67 qdec,
68 irq,
69 a.degrade(),
70 b.degrade(),
71 Some(led.degrade()),
72 config,
73 )
74 }
75
76 fn new_inner(
77 _t: impl Unborrow<Target = QDEC> + 'd,
78 irq: impl Unborrow<Target = interrupt::QDEC> + 'd,
79 a: AnyPin,
80 b: AnyPin,
81 led: Option<AnyPin>,
82 config: Config,
83 ) -> Self {
84 unborrow!(irq);
85 let r = Self::regs();
86
87 // Select pins.
88 a.conf().write(|w| w.input().connect().pull().pullup());
89 b.conf().write(|w| w.input().connect().pull().pullup());
90 r.psel.a.write(|w| unsafe { w.bits(a.psel_bits()) });
91 r.psel.b.write(|w| unsafe { w.bits(b.psel_bits()) });
92 if let Some(led_pin) = &led {
93 led_pin.conf().write(|w| w.dir().output());
94 r.psel.led.write(|w| unsafe { w.bits(led_pin.psel_bits()) });
95 }
96
97 // Enables/disable input debounce filters
98 r.dbfen.write(|w| match config.debounce {
99 true => w.dbfen().enabled(),
100 false => w.dbfen().disabled(),
101 });
102
103 // Set LED output pin polarity
104 r.ledpol.write(|w| match config.led_polarity {
105 LedPolarity::ActiveHigh => w.ledpol().active_high(),
106 LedPolarity::ActiveLow => w.ledpol().active_low(),
107 });
108
109 // Set time period the LED is switched ON prior to sampling (0..511 us).
110 r.ledpre
111 .write(|w| unsafe { w.ledpre().bits(config.led_pre_usecs.min(511)) });
112
113 // Set sample period
114 r.sampleper.write(|w| match config.period {
115 SamplePeriod::_128us => w.sampleper()._128us(),
116 SamplePeriod::_256us => w.sampleper()._256us(),
117 SamplePeriod::_512us => w.sampleper()._512us(),
118 SamplePeriod::_1024us => w.sampleper()._1024us(),
119 SamplePeriod::_2048us => w.sampleper()._2048us(),
120 SamplePeriod::_4096us => w.sampleper()._4096us(),
121 SamplePeriod::_8192us => w.sampleper()._8192us(),
122 SamplePeriod::_16384us => w.sampleper()._16384us(),
123 SamplePeriod::_32ms => w.sampleper()._32ms(),
124 SamplePeriod::_65ms => w.sampleper()._65ms(),
125 SamplePeriod::_131ms => w.sampleper()._131ms(),
126 });
127
128 // Enable peripheral
129 r.enable.write(|w| w.enable().set_bit());
130
131 irq.disable();
132 irq.set_handler(|_| {
133 let r = Self::regs();
134 r.intenclr.write(|w| w.reportrdy().clear());
135 WAKER.wake();
136 });
137 irq.enable();
138
139 Self {
140 phantom: PhantomData,
141 }
142 }
143
144 /// Perform an asynchronous read of the decoder.
145 /// The returned future can be awaited to obtain the number of steps.
146 ///
147 /// If the future is dropped, the read is cancelled.
148 ///
149 /// # Example
150 ///
151 /// ```no_run
152 /// let irq = interrupt::take!(QDEC);
153 /// let config = qdec::Config::default();
154 /// let mut q = Qdec::new(p.QDEC, p.P0_31, p.P0_30, config);
155 /// let delta = q.read().await;
156 /// ```
157 pub async fn read(&mut self) -> i16 {
158 // In case the future is dropped, stop the task and reset events.
159 let on_drop = OnDrop::new(|| {
160 let t = Self::regs();
161 t.tasks_stop.write(|w| unsafe { w.bits(1) });
162 t.events_reportrdy.reset();
163 });
164
165 let t = Self::regs();
166 t.intenset.write(|w| w.reportrdy().set());
167 unsafe { t.tasks_start.write(|w| w.bits(1)) };
168 unsafe { t.tasks_readclracc.write(|w| w.bits(1)) };
169
170 let value = poll_fn(|cx| {
171 WAKER.register(cx.waker());
172 if t.events_reportrdy.read().bits() == 0 {
173 return Poll::Pending;
174 } else {
175 t.events_reportrdy.reset();
176 let acc = t.accread.read().bits();
177 Poll::Ready(acc as i16)
178 }
179 })
180 .await;
181 on_drop.defuse();
182 value
183 }
184
185 fn regs() -> &'static pac::qdec::RegisterBlock {
186 unsafe { &*pac::QDEC::ptr() }
187 }
188}
189
190#[derive(Debug, Eq, PartialEq, Clone, Copy)]
191pub enum SamplePeriod {
192 _128us,
193 _256us,
194 _512us,
195 _1024us,
196 _2048us,
197 _4096us,
198 _8192us,
199 _16384us,
200 _32ms,
201 _65ms,
202 _131ms,
203}
204
205#[derive(Debug, Eq, PartialEq, Clone, Copy)]
206pub enum NumSamples {
207 _10smpl,
208 _40smpl,
209 _80smpl,
210 _120smpl,
211 _160smpl,
212 _200smpl,
213 _240smpl,
214 _280smpl,
215 _1smpl,
216}
217
218#[derive(Debug, Eq, PartialEq, Clone, Copy)]
219pub enum LedPolarity {
220 ActiveHigh,
221 ActiveLow,
222}
diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf/src/bin/qdec.rs
new file mode 100644
index 000000000..9c670cea0
--- /dev/null
+++ b/examples/nrf/src/bin/qdec.rs
@@ -0,0 +1,29 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::info;
6use embassy::executor::Spawner;
7use embassy_nrf::{
8 interrupt,
9 qdec::{self, Qdec},
10 Peripherals,
11};
12
13use defmt_rtt as _; // global logger
14use panic_probe as _;
15
16#[embassy::main]
17async fn main(_spawner: Spawner, p: Peripherals) {
18 let irq = interrupt::take!(QDEC);
19 let config = qdec::Config::default();
20 let mut rotary = Qdec::new(p.QDEC, irq, p.P1_13, p.P0_12, config);
21 // let mut rotary = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config);
22
23 info!("Turn rotary encoder!");
24 let mut value = 0;
25 loop {
26 value += rotary.read().await;
27 info!("Value: {}", value);
28 }
29}