aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/saadc.rs
diff options
context:
space:
mode:
authorRichard Dodd <[email protected]>2021-03-24 18:33:17 +0100
committerDario Nieuwenhuis <[email protected]>2021-03-29 00:58:58 +0200
commit53645d9d38bcb5904f1deba2cfa4e6719da2bc58 (patch)
treed7a7bbb0a28914dde25a2d101c7e021311473120 /embassy-nrf/src/saadc.rs
parenta08d7814420ed575ead6744f4254bfbb431bbc27 (diff)
nrf/saadc: initial implementation
Diffstat (limited to 'embassy-nrf/src/saadc.rs')
-rw-r--r--embassy-nrf/src/saadc.rs242
1 files changed, 242 insertions, 0 deletions
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs
new file mode 100644
index 000000000..b854ce780
--- /dev/null
+++ b/embassy-nrf/src/saadc.rs
@@ -0,0 +1,242 @@
1use core::future::Future;
2use core::marker::PhantomData;
3use core::pin::Pin;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6use embassy::traits;
7use embassy::util::{wake_on_interrupt, PeripheralBorrow};
8use embassy_extras::unborrow;
9use futures::future::poll_fn;
10use traits::spi::FullDuplex;
11
12use crate::gpio::Pin as GpioPin;
13use crate::interrupt::{self, Interrupt};
14use crate::{pac, peripherals, slice_in_ram_or};
15
16#[cfg(feature = "9160")]
17use pac::{saadc_ns as saadc, SAADC_NS as SAADC};
18
19#[cfg(not(feature = "9160"))]
20use pac::{saadc, SAADC};
21
22pub use saadc::{
23 ch::{
24 config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time},
25 pselp::PSELP_A as PositiveChannel,
26 },
27 oversample::OVERSAMPLE_A as Oversample,
28 resolution::VAL_A as Resolution,
29};
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[non_exhaustive]
34pub enum Error {}
35
36/// One-shot saadc. Continuous sample mode TODO.
37pub struct OneShot<'d, T: PositivePin> {
38 peri: peripherals::SAADC,
39 positive_pin: T,
40 irq: interrupt::SAADC,
41 phantom: PhantomData<(&'d mut peripherals::SAADC, &'d mut T)>,
42}
43
44/// Used to configure the SAADC peripheral.
45///
46/// See the `Default` impl for suitable default values.
47pub struct Config {
48 /// Output resolution in bits.
49 pub resolution: Resolution,
50 /// Average 2^`oversample` input samples before transferring the result into memory.
51 pub oversample: Oversample,
52 /// Reference voltage of the SAADC input.
53 pub reference: Reference,
54 /// Gain used to control the effective input range of the SAADC.
55 pub gain: Gain,
56 /// Positive channel resistor control.
57 pub resistor: Resistor,
58 /// Acquisition time in microseconds.
59 pub time: Time,
60}
61
62impl Default for Config {
63 fn default() -> Self {
64 Self {
65 resolution: Resolution::_14BIT,
66 oversample: Oversample::OVER8X,
67 reference: Reference::VDD1_4,
68 gain: Gain::GAIN1_4,
69 resistor: Resistor::BYPASS,
70 time: Time::_20US,
71 }
72 }
73}
74
75impl<'d, T: PositivePin> OneShot<'d, T> {
76 pub fn new(
77 saadc: impl PeripheralBorrow<Target = peripherals::SAADC> + 'd,
78 irq: impl PeripheralBorrow<Target = interrupt::SAADC> + 'd,
79 positive_pin: impl PeripheralBorrow<Target = T> + 'd,
80 config: Config,
81 ) -> Self {
82 unborrow!(saadc, irq, positive_pin);
83
84 let r = unsafe { &*SAADC::ptr() };
85
86 let Config {
87 resolution,
88 oversample,
89 reference,
90 gain,
91 resistor,
92 time,
93 } = config;
94
95 // Configure pins
96 r.enable.write(|w| w.enable().enabled());
97 r.resolution.write(|w| w.val().variant(resolution));
98 r.oversample.write(|w| w.oversample().variant(oversample));
99
100 r.ch[0].config.write(|w| {
101 w.refsel().variant(reference);
102 w.gain().variant(gain);
103 w.tacq().variant(time);
104 w.mode().se();
105 w.resp().variant(resistor);
106 w.resn().bypass();
107 if !matches!(oversample, Oversample::BYPASS) {
108 w.burst().enabled();
109 } else {
110 w.burst().disabled();
111 }
112 w
113 });
114
115 // Set positive channel
116 r.ch[0]
117 .pselp
118 .write(|w| w.pselp().variant(positive_pin.channel()));
119
120 // Disable all events interrupts
121 r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
122
123 Self {
124 peri: saadc,
125 positive_pin,
126 irq,
127 phantom: PhantomData,
128 }
129 }
130
131 fn regs(&self) -> &saadc::RegisterBlock {
132 unsafe { &*SAADC::ptr() }
133 }
134}
135
136impl<'d, T: PositivePin> Drop for OneShot<'d, T> {
137 fn drop(&mut self) {
138 let r = self.regs();
139 r.enable.write(|w| w.enable().disabled());
140 }
141}
142
143pub trait Sample {
144 type SampleFuture<'a>: Future<Output = i16> + 'a
145 where
146 Self: 'a;
147
148 fn sample<'a>(self: Pin<&'a mut Self>) -> Self::SampleFuture<'a>;
149}
150
151impl<'d, T: PositivePin> Sample for OneShot<'d, T> {
152 #[rustfmt::skip]
153 type SampleFuture<'a> where Self: 'a = impl Future<Output = i16> + 'a;
154
155 fn sample<'a>(self: Pin<&'a mut Self>) -> Self::SampleFuture<'a> {
156 async move {
157 let this = unsafe { self.get_unchecked_mut() };
158 let r = this.regs();
159
160 // Set up the DMA
161 let mut val: i16 = 0;
162 r.result
163 .ptr
164 .write(|w| unsafe { w.ptr().bits(((&mut val) as *mut _) as u32) });
165 r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) });
166
167 // Reset and enable the end event
168 r.events_end.reset();
169 r.intenset.write(|w| w.end().set());
170
171 // Don't reorder the ADC start event before the previous writes. Hopefully this
172 // wouldn't happen anyway.
173 compiler_fence(Ordering::SeqCst);
174
175 r.tasks_start.write(|w| unsafe { w.bits(1) });
176 r.tasks_sample.write(|w| unsafe { w.bits(1) });
177
178 // Wait for 'end' event.
179 poll_fn(|cx| {
180 let r = this.regs();
181
182 if r.events_end.read().bits() != 0 {
183 r.events_end.reset();
184 return Poll::Ready(());
185 }
186
187 wake_on_interrupt(&mut this.irq, cx.waker());
188
189 Poll::Pending
190 })
191 .await;
192
193 // The DMA wrote the sampled value to `val`.
194 val
195 }
196 }
197}
198
199/// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal.
200///
201/// Currently negative is always shorted to ground (0V).
202pub trait PositivePin {
203 fn channel(&self) -> PositiveChannel;
204}
205
206macro_rules! positive_pin_mappings {
207 ( $($ch:ident => $pin:ident,)*) => {
208 $(
209 impl PositivePin for crate::peripherals::$pin {
210 fn channel(&self) -> PositiveChannel {
211 PositiveChannel::$ch
212 }
213 }
214 )*
215 };
216}
217
218// TODO the variant names are unchecked
219// the pins are copied from nrf hal
220#[cfg(feature = "9160")]
221positive_pin_mappings! {
222 ANALOGINPUT0 => P0_13,
223 ANALOGINPUT1 => P0_14,
224 ANALOGINPUT2 => P0_15,
225 ANALOGINPUT3 => P0_16,
226 ANALOGINPUT4 => P0_17,
227 ANALOGINPUT5 => P0_18,
228 ANALOGINPUT6 => P0_19,
229 ANALOGINPUT7 => P0_20,
230}
231
232#[cfg(not(feature = "9160"))]
233positive_pin_mappings! {
234 ANALOGINPUT0 => P0_02,
235 ANALOGINPUT1 => P0_03,
236 ANALOGINPUT2 => P0_04,
237 ANALOGINPUT3 => P0_05,
238 ANALOGINPUT4 => P0_28,
239 ANALOGINPUT5 => P0_29,
240 ANALOGINPUT6 => P0_30,
241 ANALOGINPUT7 => P0_31,
242}