aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhuntc <[email protected]>2021-10-07 18:00:03 +1100
committerhuntc <[email protected]>2021-10-09 11:25:18 +1100
commitcef6158c31794795f16099e3df4c9049b976dae6 (patch)
tree28768a13d23793287785da0eda6c39d8a48f9156
parent009b77c1b9874cccb9b2f81876f41e9c3d53f3a5 (diff)
Extend SAADC one shot support
One-shot mode now permits the sampling of differential pins, and the sampling of multiple pins simultaneously. A new ChannelConfig structure has been introduced so that multiple channels can be configured individually. Further, the `sample` method now accepts a buffer into which samples are written. Along the way, I've reset some default configuration to align with Nordic's settings in their nrfx saadc driver. Specifically, the channel gain defaults to 6 (from 4) and the time defaults to 10us (from 20us).
-rw-r--r--README.md2
-rw-r--r--embassy-nrf/src/saadc.rs175
-rw-r--r--examples/nrf/src/bin/saadc.rs10
3 files changed, 144 insertions, 43 deletions
diff --git a/README.md b/README.md
index 13bcf6e5b..f8898dcd6 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,8 @@ The `embassy-nrf` crate contains implementations for nRF 52 series SoCs.
35- `uarte`: UARTE driver implementing `AsyncBufRead` and `AsyncWrite`. 35- `uarte`: UARTE driver implementing `AsyncBufRead` and `AsyncWrite`.
36- `qspi`: QSPI driver implementing `Flash`. 36- `qspi`: QSPI driver implementing `Flash`.
37- `gpiote`: GPIOTE driver. Allows `await`ing GPIO pin changes. Great for reading buttons or receiving interrupts from external chips. 37- `gpiote`: GPIOTE driver. Allows `await`ing GPIO pin changes. Great for reading buttons or receiving interrupts from external chips.
38- `saadc`: SAADC driver. Provides a full implementation of the one-shot sampling for analog channels.
39
38- `rtc`: RTC driver implementing `Clock` and `Alarm`, for use with `embassy::executor`. 40- `rtc`: RTC driver implementing `Clock` and `Alarm`, for use with `embassy::executor`.
39 41
40## Examples 42## Examples
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs
index b6e8f4e44..12c302d58 100644
--- a/embassy-nrf/src/saadc.rs
+++ b/embassy-nrf/src/saadc.rs
@@ -1,3 +1,4 @@
1use core::convert::TryInto;
1use core::marker::PhantomData; 2use core::marker::PhantomData;
2use core::sync::atomic::{compiler_fence, Ordering}; 3use core::sync::atomic::{compiler_fence, Ordering};
3use core::task::Poll; 4use core::task::Poll;
@@ -15,6 +16,7 @@ use pac::{saadc, SAADC};
15pub use saadc::{ 16pub use saadc::{
16 ch::{ 17 ch::{
17 config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time}, 18 config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time},
19 pseln::PSELN_A as NegativeChannel,
18 pselp::PSELP_A as PositiveChannel, 20 pselp::PSELP_A as PositiveChannel,
19 }, 21 },
20 oversample::OVERSAMPLE_A as Oversample, 22 oversample::OVERSAMPLE_A as Oversample,
@@ -27,7 +29,7 @@ pub use saadc::{
27pub enum Error {} 29pub enum Error {}
28 30
29/// One-shot saadc. Continuous sample mode TODO. 31/// One-shot saadc. Continuous sample mode TODO.
30pub struct OneShot<'d> { 32pub struct OneShot<'d, const N: usize> {
31 phantom: PhantomData<&'d mut peripherals::SAADC>, 33 phantom: PhantomData<&'d mut peripherals::SAADC>,
32} 34}
33 35
@@ -36,11 +38,29 @@ static WAKER: AtomicWaker = AtomicWaker::new();
36/// Used to configure the SAADC peripheral. 38/// Used to configure the SAADC peripheral.
37/// 39///
38/// See the `Default` impl for suitable default values. 40/// See the `Default` impl for suitable default values.
41#[non_exhaustive]
39pub struct Config { 42pub struct Config {
40 /// Output resolution in bits. 43 /// Output resolution in bits.
41 pub resolution: Resolution, 44 pub resolution: Resolution,
42 /// Average 2^`oversample` input samples before transferring the result into memory. 45 /// Average 2^`oversample` input samples before transferring the result into memory.
43 pub oversample: Oversample, 46 pub oversample: Oversample,
47}
48
49impl Default for Config {
50 /// Default configuration for single channel sampling.
51 fn default() -> Self {
52 Self {
53 resolution: Resolution::_14BIT,
54 oversample: Oversample::BYPASS,
55 }
56 }
57}
58
59/// Used to configure an individual SAADC peripheral channel.
60///
61/// See the `Default` impl for suitable default values.
62#[non_exhaustive]
63pub struct ChannelConfig {
44 /// Reference voltage of the SAADC input. 64 /// Reference voltage of the SAADC input.
45 pub reference: Reference, 65 pub reference: Reference,
46 /// Gain used to control the effective input range of the SAADC. 66 /// Gain used to control the effective input range of the SAADC.
@@ -49,26 +69,48 @@ pub struct Config {
49 pub resistor: Resistor, 69 pub resistor: Resistor,
50 /// Acquisition time in microseconds. 70 /// Acquisition time in microseconds.
51 pub time: Time, 71 pub time: Time,
72 /// Positive channel to sample
73 p_channel: PositiveChannel,
74 /// An optional negative channel to sample
75 n_channel: Option<NegativeChannel>,
52} 76}
53 77
54impl Default for Config { 78impl ChannelConfig {
55 fn default() -> Self { 79 /// Default configuration for single ended channel sampling.
80 pub fn single_ended(pin: impl Unborrow<Target = impl PositivePin>) -> Self {
81 unborrow!(pin);
82 Self {
83 reference: Reference::INTERNAL,
84 gain: Gain::GAIN1_6,
85 resistor: Resistor::BYPASS,
86 time: Time::_10US,
87 p_channel: pin.channel(),
88 n_channel: None,
89 }
90 }
91 /// Default configuration for differential channel sampling.
92 pub fn differential(
93 ppin: impl Unborrow<Target = impl PositivePin>,
94 npin: impl Unborrow<Target = impl NegativePin>,
95 ) -> Self {
96 unborrow!(ppin, npin);
56 Self { 97 Self {
57 resolution: Resolution::_14BIT,
58 oversample: Oversample::OVER8X,
59 reference: Reference::VDD1_4, 98 reference: Reference::VDD1_4,
60 gain: Gain::GAIN1_4, 99 gain: Gain::GAIN1_6,
61 resistor: Resistor::BYPASS, 100 resistor: Resistor::BYPASS,
62 time: Time::_20US, 101 time: Time::_10US,
102 p_channel: ppin.channel(),
103 n_channel: Some(npin.channel()),
63 } 104 }
64 } 105 }
65} 106}
66 107
67impl<'d> OneShot<'d> { 108impl<'d, const N: usize> OneShot<'d, N> {
68 pub fn new( 109 pub fn new(
69 _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd, 110 _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd,
70 irq: impl Unborrow<Target = interrupt::SAADC> + 'd, 111 irq: impl Unborrow<Target = interrupt::SAADC> + 'd,
71 config: Config, 112 config: Config,
113 channel_configs: [ChannelConfig; N],
72 ) -> Self { 114 ) -> Self {
73 unborrow!(irq); 115 unborrow!(irq);
74 116
@@ -77,31 +119,37 @@ impl<'d> OneShot<'d> {
77 let Config { 119 let Config {
78 resolution, 120 resolution,
79 oversample, 121 oversample,
80 reference,
81 gain,
82 resistor,
83 time,
84 } = config; 122 } = config;
85 123
86 // Configure pins 124 // Configure channels
87 r.enable.write(|w| w.enable().enabled()); 125 r.enable.write(|w| w.enable().enabled());
88 r.resolution.write(|w| w.val().variant(resolution)); 126 r.resolution.write(|w| w.val().variant(resolution));
89 r.oversample.write(|w| w.oversample().variant(oversample)); 127 r.oversample.write(|w| w.oversample().variant(oversample));
90 128
91 r.ch[0].config.write(|w| { 129 for (i, cc) in channel_configs.iter().enumerate() {
92 w.refsel().variant(reference); 130 r.ch[i].pselp.write(|w| w.pselp().variant(cc.p_channel));
93 w.gain().variant(gain); 131 if let Some(npin) = cc.n_channel.as_ref() {
94 w.tacq().variant(time); 132 r.ch[i].pseln.write(|w| w.pseln().variant(*npin));
95 w.mode().se();
96 w.resp().variant(resistor);
97 w.resn().bypass();
98 if !matches!(oversample, Oversample::BYPASS) {
99 w.burst().enabled();
100 } else {
101 w.burst().disabled();
102 } 133 }
103 w 134 r.ch[i].config.write(|w| {
104 }); 135 w.refsel().variant(cc.reference);
136 w.gain().variant(cc.gain);
137 w.tacq().variant(cc.time);
138 if cc.n_channel.is_none() {
139 w.mode().se();
140 } else {
141 w.mode().diff();
142 }
143 w.resp().variant(cc.resistor);
144 w.resn().bypass();
145 if !matches!(oversample, Oversample::BYPASS) {
146 w.burst().enabled();
147 } else {
148 w.burst().disabled();
149 }
150 w
151 });
152 }
105 153
106 // Disable all events interrupts 154 // Disable all events interrupts
107 r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); 155 r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
@@ -128,18 +176,16 @@ impl<'d> OneShot<'d> {
128 unsafe { &*SAADC::ptr() } 176 unsafe { &*SAADC::ptr() }
129 } 177 }
130 178
131 pub async fn sample(&mut self, pin: &mut impl PositivePin) -> i16 { 179 pub async fn sample(&mut self, buf: &mut [i16; N]) {
132 let r = Self::regs(); 180 let r = Self::regs();
133 181
134 // Set positive channel
135 r.ch[0].pselp.write(|w| w.pselp().variant(pin.channel()));
136
137 // Set up the DMA 182 // Set up the DMA
138 let mut val: i16 = 0;
139 r.result 183 r.result
140 .ptr 184 .ptr
141 .write(|w| unsafe { w.ptr().bits(((&mut val) as *mut _) as u32) }); 185 .write(|w| unsafe { w.ptr().bits(buf.as_mut_ptr() as u32) });
142 r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); 186 r.result
187 .maxcnt
188 .write(|w| unsafe { w.maxcnt().bits(N.try_into().unwrap()) });
143 189
144 // Reset and enable the end event 190 // Reset and enable the end event
145 r.events_end.reset(); 191 r.events_end.reset();
@@ -166,13 +212,10 @@ impl<'d> OneShot<'d> {
166 Poll::Pending 212 Poll::Pending
167 }) 213 })
168 .await; 214 .await;
169
170 // The DMA wrote the sampled value to `val`.
171 val
172 } 215 }
173} 216}
174 217
175impl<'d> Drop for OneShot<'d> { 218impl<'d, const N: usize> Drop for OneShot<'d, N> {
176 fn drop(&mut self) { 219 fn drop(&mut self) {
177 let r = Self::regs(); 220 let r = Self::regs();
178 r.enable.write(|w| w.enable().disabled()); 221 r.enable.write(|w| w.enable().disabled());
@@ -180,8 +223,6 @@ impl<'d> Drop for OneShot<'d> {
180} 223}
181 224
182/// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal. 225/// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal.
183///
184/// Currently negative is always shorted to ground (0V).
185pub trait PositivePin { 226pub trait PositivePin {
186 fn channel(&self) -> PositiveChannel; 227 fn channel(&self) -> PositiveChannel;
187} 228}
@@ -198,6 +239,38 @@ macro_rules! positive_pin_mappings {
198 }; 239 };
199} 240}
200 241
242/// A pin that can be used as the negative end of a ADC differential in the SAADC periperhal.
243pub trait NegativePin {
244 fn channel(&self) -> NegativeChannel;
245}
246
247macro_rules! negative_pin_mappings {
248 ( $($ch:ident => $pin:ident,)*) => {
249 $(
250 impl NegativePin for crate::peripherals::$pin {
251 fn channel(&self) -> NegativeChannel {
252 NegativeChannel::$ch
253 }
254 }
255 )*
256 };
257}
258
259/// Represents an unconnected pin
260pub struct NotConnected {}
261
262impl PositivePin for NotConnected {
263 fn channel(&self) -> PositiveChannel {
264 PositiveChannel::NC
265 }
266}
267
268impl NegativePin for NotConnected {
269 fn channel(&self) -> NegativeChannel {
270 NegativeChannel::NC
271 }
272}
273
201// TODO the variant names are unchecked 274// TODO the variant names are unchecked
202// the pins are copied from nrf hal 275// the pins are copied from nrf hal
203#[cfg(feature = "9160")] 276#[cfg(feature = "9160")]
@@ -223,3 +296,27 @@ positive_pin_mappings! {
223 ANALOGINPUT6 => P0_30, 296 ANALOGINPUT6 => P0_30,
224 ANALOGINPUT7 => P0_31, 297 ANALOGINPUT7 => P0_31,
225} 298}
299
300#[cfg(feature = "9160")]
301negative_pin_mappings! {
302 ANALOGINPUT0 => P0_13,
303 ANALOGINPUT1 => P0_14,
304 ANALOGINPUT2 => P0_15,
305 ANALOGINPUT3 => P0_16,
306 ANALOGINPUT4 => P0_17,
307 ANALOGINPUT5 => P0_18,
308 ANALOGINPUT6 => P0_19,
309 ANALOGINPUT7 => P0_20,
310}
311
312#[cfg(not(feature = "9160"))]
313negative_pin_mappings! {
314 ANALOGINPUT0 => P0_02,
315 ANALOGINPUT1 => P0_03,
316 ANALOGINPUT2 => P0_04,
317 ANALOGINPUT3 => P0_05,
318 ANALOGINPUT4 => P0_28,
319 ANALOGINPUT5 => P0_29,
320 ANALOGINPUT6 => P0_30,
321 ANALOGINPUT7 => P0_31,
322}
diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf/src/bin/saadc.rs
index c4d23360e..d12717c04 100644
--- a/examples/nrf/src/bin/saadc.rs
+++ b/examples/nrf/src/bin/saadc.rs
@@ -7,18 +7,20 @@ mod example_common;
7use defmt::panic; 7use defmt::panic;
8use embassy::executor::Spawner; 8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer}; 9use embassy::time::{Duration, Timer};
10use embassy_nrf::saadc::{Config, OneShot}; 10use embassy_nrf::saadc::{ChannelConfig, Config, OneShot};
11use embassy_nrf::{interrupt, Peripherals}; 11use embassy_nrf::{interrupt, Peripherals};
12use example_common::*; 12use example_common::*;
13 13
14#[embassy::main] 14#[embassy::main]
15async fn main(_spawner: Spawner, mut p: Peripherals) { 15async fn main(_spawner: Spawner, mut p: Peripherals) {
16 let config = Config::default(); 16 let config = Config::default();
17 let mut saadc = OneShot::new(p.SAADC, interrupt::take!(SAADC), config); 17 let channel_config = ChannelConfig::single_ended(&mut p.P0_02);
18 let mut saadc = OneShot::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]);
18 19
19 loop { 20 loop {
20 let sample = saadc.sample(&mut p.P0_02).await; 21 let mut buf = [0; 1];
21 info!("sample: {=i16}", sample); 22 saadc.sample(&mut buf).await;
23 info!("sample: {=i16}", &buf[0]);
22 Timer::after(Duration::from_millis(100)).await; 24 Timer::after(Duration::from_millis(100)).await;
23 } 25 }
24} 26}