aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/src/adc.rs115
-rw-r--r--examples/rp/src/bin/adc.rs2
-rw-r--r--tests/rp/src/bin/adc.rs55
3 files changed, 165 insertions, 7 deletions
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs
index 2824d893c..bac455743 100644
--- a/embassy-rp/src/adc.rs
+++ b/embassy-rp/src/adc.rs
@@ -1,5 +1,6 @@
1use core::future::poll_fn; 1use core::future::poll_fn;
2use core::marker::PhantomData; 2use core::marker::PhantomData;
3use core::mem;
3use core::sync::atomic::{compiler_fence, Ordering}; 4use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll; 5use core::task::Poll;
5 6
@@ -11,7 +12,7 @@ use crate::gpio::{self, AnyPin, Pull};
11use crate::interrupt::typelevel::Binding; 12use crate::interrupt::typelevel::Binding;
12use crate::interrupt::InterruptExt; 13use crate::interrupt::InterruptExt;
13use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; 14use crate::peripherals::{ADC, ADC_TEMP_SENSOR};
14use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; 15use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt};
15 16
16static WAKER: AtomicWaker = AtomicWaker::new(); 17static WAKER: AtomicWaker = AtomicWaker::new();
17 18
@@ -48,7 +49,7 @@ impl<'p> Channel<'p> {
48 Self(Source::Pin(pin.map_into())) 49 Self(Source::Pin(pin.map_into()))
49 } 50 }
50 51
51 pub fn new_sensor(s: impl Peripheral<P = ADC_TEMP_SENSOR> + 'p) -> Self { 52 pub fn new_temp_sensor(s: impl Peripheral<P = ADC_TEMP_SENSOR> + 'p) -> Self {
52 let r = pac::ADC; 53 let r = pac::ADC;
53 r.cs().write_set(|w| w.set_ts_en(true)); 54 r.cs().write_set(|w| w.set_ts_en(true));
54 Self(Source::TempSensor(s.into_ref())) 55 Self(Source::TempSensor(s.into_ref()))
@@ -82,6 +83,21 @@ impl<'p> Drop for Source<'p> {
82 } 83 }
83} 84}
84 85
86#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88#[repr(transparent)]
89pub struct Sample(u16);
90
91impl Sample {
92 pub fn good(&self) -> bool {
93 self.0 < 0x8000
94 }
95
96 pub fn value(&self) -> u16 {
97 self.0 & !0x8000
98 }
99}
100
85#[derive(Debug, Eq, PartialEq, Copy, Clone)] 101#[derive(Debug, Eq, PartialEq, Copy, Clone)]
86#[cfg_attr(feature = "defmt", derive(defmt::Format))] 102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
87pub enum Error { 103pub enum Error {
@@ -191,6 +207,91 @@ impl<'d> Adc<'d, Async> {
191 false => Ok(r.result().read().result().into()), 207 false => Ok(r.result().read().result().into()),
192 } 208 }
193 } 209 }
210
211 async fn read_many_inner<W: dma::Word>(
212 &mut self,
213 ch: &mut Channel<'_>,
214 buf: &mut [W],
215 fcs_err: bool,
216 dma: impl Peripheral<P = impl dma::Channel>,
217 ) -> Result<(), Error> {
218 let r = Self::regs();
219 // clear previous errors and set channel
220 r.cs().modify(|w| {
221 w.set_ainsel(ch.channel());
222 w.set_err_sticky(true); // clear previous errors
223 w.set_start_many(false);
224 });
225 // wait for previous conversions and drain fifo. an earlier batch read may have
226 // been cancelled, leaving the adc running.
227 while !r.cs().read().ready() {}
228 while !r.fcs().read().empty() {
229 r.fifo().read();
230 }
231
232 // set up fifo for dma
233 r.fcs().write(|w| {
234 w.set_thresh(1);
235 w.set_dreq_en(true);
236 w.set_shift(mem::size_of::<W>() == 1);
237 w.set_en(true);
238 w.set_err(fcs_err);
239 });
240
241 // reset dma config on drop, regardless of whether it was a future being cancelled
242 // or the method returning normally.
243 struct ResetDmaConfig;
244 impl Drop for ResetDmaConfig {
245 fn drop(&mut self) {
246 pac::ADC.cs().write_clear(|w| w.set_start_many(true));
247 while !pac::ADC.cs().read().ready() {}
248 pac::ADC.fcs().write_clear(|w| {
249 w.set_dreq_en(true);
250 w.set_shift(true);
251 w.set_en(true);
252 });
253 }
254 }
255 let auto_reset = ResetDmaConfig;
256
257 let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) };
258 // start conversions and wait for dma to finish. we can't report errors early
259 // because there's no interrupt to signal them, and inspecting every element
260 // of the fifo is too costly to do here.
261 r.cs().write_set(|w| w.set_start_many(true));
262 dma.await;
263 mem::drop(auto_reset);
264 // we can't report errors before the conversions have ended since no interrupt
265 // exists to report them early, and since they're exceedingly rare we probably don't
266 // want to anyway.
267 match r.cs().read().err_sticky() {
268 false => Ok(()),
269 true => Err(Error::ConversionFailed),
270 }
271 }
272
273 #[inline]
274 pub async fn read_many<S: AdcSample>(
275 &mut self,
276 ch: &mut Channel<'_>,
277 buf: &mut [S],
278 dma: impl Peripheral<P = impl dma::Channel>,
279 ) -> Result<(), Error> {
280 self.read_many_inner(ch, buf, false, dma).await
281 }
282
283 #[inline]
284 pub async fn read_many_raw(
285 &mut self,
286 ch: &mut Channel<'_>,
287 buf: &mut [Sample],
288 dma: impl Peripheral<P = impl dma::Channel>,
289 ) {
290 // errors are reported in individual samples
291 let _ = self
292 .read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, dma)
293 .await;
294 }
194} 295}
195 296
196impl<'d> Adc<'d, Blocking> { 297impl<'d> Adc<'d, Blocking> {
@@ -214,9 +315,19 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for Inter
214} 315}
215 316
216mod sealed { 317mod sealed {
318 pub trait AdcSample: crate::dma::Word {}
319
217 pub trait AdcChannel {} 320 pub trait AdcChannel {}
218} 321}
219 322
323pub trait AdcSample: sealed::AdcSample {}
324
325impl sealed::AdcSample for u16 {}
326impl AdcSample for u16 {}
327
328impl sealed::AdcSample for u8 {}
329impl AdcSample for u8 {}
330
220pub trait AdcChannel: sealed::AdcChannel {} 331pub trait AdcChannel: sealed::AdcChannel {}
221pub trait AdcPin: AdcChannel + gpio::Pin {} 332pub trait AdcPin: AdcChannel + gpio::Pin {}
222 333
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs
index c58695512..02bc493b6 100644
--- a/examples/rp/src/bin/adc.rs
+++ b/examples/rp/src/bin/adc.rs
@@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) {
25 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None); 25 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
26 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None); 26 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
27 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None); 27 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
28 let mut ts = Channel::new_sensor(p.ADC_TEMP_SENSOR); 28 let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
29 29
30 loop { 30 loop {
31 let level = adc.read(&mut p26).await.unwrap(); 31 let level = adc.read(&mut p26).await.unwrap();
diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs
index 9006ce8cc..d6d58f0c0 100644
--- a/tests/rp/src/bin/adc.rs
+++ b/tests/rp/src/bin/adc.rs
@@ -6,7 +6,7 @@ mod common;
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; 9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample};
10use embassy_rp::bind_interrupts; 10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::Pull; 11use embassy_rp::gpio::Pull;
12use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
@@ -71,10 +71,57 @@ async fn main(_spawner: Spawner) {
71 defmt::assert!(low < none); 71 defmt::assert!(low < none);
72 defmt::assert!(none < up); 72 defmt::assert!(none < up);
73 } 73 }
74 {
75 let temp = convert_to_celsius(
76 adc.read(&mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR))
77 .await
78 .unwrap(),
79 );
80 defmt::assert!(temp > 0.0);
81 defmt::assert!(temp < 60.0);
82 }
74 83
75 let temp = convert_to_celsius(adc.read(&mut Channel::new_sensor(p.ADC_TEMP_SENSOR)).await.unwrap()); 84 // run a bunch of conversions. we'll only check gp29 and the temp
76 defmt::assert!(temp > 0.0); 85 // sensor here for brevity, if those two work the rest will too.
77 defmt::assert!(temp < 60.0); 86 {
87 // gp29 is connected to vsys through a 200k/100k divider,
88 // adding pulls should change the value
89 let mut low = [0u16; 16];
90 let mut none = [0u8; 16];
91 let mut up = [Sample::default(); 16];
92 adc.read_many(
93 &mut Channel::new_pin(&mut p.PIN_29, Pull::Down),
94 &mut low,
95 &mut p.DMA_CH0,
96 )
97 .await
98 .unwrap();
99 adc.read_many(
100 &mut Channel::new_pin(&mut p.PIN_29, Pull::None),
101 &mut none,
102 &mut p.DMA_CH0,
103 )
104 .await
105 .unwrap();
106 adc.read_many_raw(&mut Channel::new_pin(&mut p.PIN_29, Pull::Up), &mut up, &mut p.DMA_CH0)
107 .await;
108 defmt::assert!(low.iter().zip(none.iter()).all(|(l, n)| *l >> 4 < *n as u16));
109 defmt::assert!(up.iter().all(|s| s.good()));
110 defmt::assert!(none.iter().zip(up.iter()).all(|(n, u)| (*n as u16) < u.value()));
111 }
112 {
113 let mut temp = [0u16; 16];
114 adc.read_many(
115 &mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR),
116 &mut temp,
117 &mut p.DMA_CH0,
118 )
119 .await
120 .unwrap();
121 let temp = temp.map(convert_to_celsius);
122 defmt::assert!(temp.iter().all(|t| *t > 0.0));
123 defmt::assert!(temp.iter().all(|t| *t < 60.0));
124 }
78 125
79 info!("Test OK"); 126 info!("Test OK");
80 cortex_m::asm::bkpt(); 127 cortex_m::asm::bkpt();