aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenrik Alsér <[email protected]>2024-06-22 21:05:17 +0000
committerGitHub <[email protected]>2024-06-22 21:05:17 +0000
commitcfe8561550e10d145eb6ef14423a49f78d7ac38e (patch)
tree56aaeb51dd78e6a24851efa2374d971a494db8d9
parent95d0cae897cc3557b0df4ea8cb73efb3a272b450 (diff)
parent0888183666df0df389453aeeb0c87d841ac50522 (diff)
Merge pull request #3044 from kalkyl/adc-multi
rp: Add multichannel ADC
-rw-r--r--embassy-rp/src/adc.rs73
-rw-r--r--examples/rp/src/bin/adc_dma.rs54
-rw-r--r--tests/rp/src/bin/adc.rs13
3 files changed, 135 insertions, 5 deletions
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs
index 101c5b71f..eb1cc9a66 100644
--- a/embassy-rp/src/adc.rs
+++ b/embassy-rp/src/adc.rs
@@ -219,18 +219,30 @@ impl<'d> Adc<'d, Async> {
219 } 219 }
220 } 220 }
221 221
222 // Note for refactoring: we don't require the actual Channels here, just the channel numbers.
223 // The public api is responsible for asserting ownership of the actual Channels.
222 async fn read_many_inner<W: dma::Word>( 224 async fn read_many_inner<W: dma::Word>(
223 &mut self, 225 &mut self,
224 ch: &mut Channel<'_>, 226 channels: impl Iterator<Item = u8>,
225 buf: &mut [W], 227 buf: &mut [W],
226 fcs_err: bool, 228 fcs_err: bool,
227 div: u16, 229 div: u16,
228 dma: impl Peripheral<P = impl dma::Channel>, 230 dma: impl Peripheral<P = impl dma::Channel>,
229 ) -> Result<(), Error> { 231 ) -> Result<(), Error> {
232 let mut rrobin = 0_u8;
233 for c in channels {
234 rrobin |= 1 << c;
235 }
236 let first_ch = rrobin.trailing_zeros() as u8;
237 if rrobin.count_ones() == 1 {
238 rrobin = 0;
239 }
240
230 let r = Self::regs(); 241 let r = Self::regs();
231 // clear previous errors and set channel 242 // clear previous errors and set channel
232 r.cs().modify(|w| { 243 r.cs().modify(|w| {
233 w.set_ainsel(ch.channel()); 244 w.set_ainsel(first_ch);
245 w.set_rrobin(rrobin);
234 w.set_err_sticky(true); // clear previous errors 246 w.set_err_sticky(true); // clear previous errors
235 w.set_start_many(false); 247 w.set_start_many(false);
236 }); 248 });
@@ -283,7 +295,49 @@ impl<'d> Adc<'d, Async> {
283 } 295 }
284 } 296 }
285 297
298 /// Sample multiple values from multiple channels using DMA.
299 /// Samples are stored in an interleaved fashion inside the buffer.
300 /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)`
301 /// Any `div` value of less than 96 will have the same effect as setting it to 0
302 #[inline]
303 pub async fn read_many_multichannel<S: AdcSample>(
304 &mut self,
305 ch: &mut [Channel<'_>],
306 buf: &mut [S],
307 div: u16,
308 dma: impl Peripheral<P = impl dma::Channel>,
309 ) -> Result<(), Error> {
310 self.read_many_inner(ch.iter().map(|c| c.channel()), buf, false, div, dma)
311 .await
312 }
313
314 /// Sample multiple values from multiple channels using DMA, with errors inlined in samples.
315 /// Samples are stored in an interleaved fashion inside the buffer.
316 /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)`
317 /// Any `div` value of less than 96 will have the same effect as setting it to 0
318 #[inline]
319 pub async fn read_many_multichannel_raw(
320 &mut self,
321 ch: &mut [Channel<'_>],
322 buf: &mut [Sample],
323 div: u16,
324 dma: impl Peripheral<P = impl dma::Channel>,
325 ) {
326 // errors are reported in individual samples
327 let _ = self
328 .read_many_inner(
329 ch.iter().map(|c| c.channel()),
330 unsafe { mem::transmute::<_, &mut [u16]>(buf) },
331 true,
332 div,
333 dma,
334 )
335 .await;
336 }
337
286 /// Sample multiple values from a channel using DMA. 338 /// Sample multiple values from a channel using DMA.
339 /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)`
340 /// Any `div` value of less than 96 will have the same effect as setting it to 0
287 #[inline] 341 #[inline]
288 pub async fn read_many<S: AdcSample>( 342 pub async fn read_many<S: AdcSample>(
289 &mut self, 343 &mut self,
@@ -292,10 +346,13 @@ impl<'d> Adc<'d, Async> {
292 div: u16, 346 div: u16,
293 dma: impl Peripheral<P = impl dma::Channel>, 347 dma: impl Peripheral<P = impl dma::Channel>,
294 ) -> Result<(), Error> { 348 ) -> Result<(), Error> {
295 self.read_many_inner(ch, buf, false, div, dma).await 349 self.read_many_inner([ch.channel()].into_iter(), buf, false, div, dma)
350 .await
296 } 351 }
297 352
298 /// Sample multiple values from a channel using DMA with errors inlined in samples. 353 /// Sample multiple values from a channel using DMA, with errors inlined in samples.
354 /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)`
355 /// Any `div` value of less than 96 will have the same effect as setting it to 0
299 #[inline] 356 #[inline]
300 pub async fn read_many_raw( 357 pub async fn read_many_raw(
301 &mut self, 358 &mut self,
@@ -306,7 +363,13 @@ impl<'d> Adc<'d, Async> {
306 ) { 363 ) {
307 // errors are reported in individual samples 364 // errors are reported in individual samples
308 let _ = self 365 let _ = self
309 .read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, div, dma) 366 .read_many_inner(
367 [ch.channel()].into_iter(),
368 unsafe { mem::transmute::<_, &mut [u16]>(buf) },
369 true,
370 div,
371 dma,
372 )
310 .await; 373 .await;
311 } 374 }
312} 375}
diff --git a/examples/rp/src/bin/adc_dma.rs b/examples/rp/src/bin/adc_dma.rs
new file mode 100644
index 000000000..f755cf5bf
--- /dev/null
+++ b/examples/rp/src/bin/adc_dma.rs
@@ -0,0 +1,54 @@
1//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
2//! For multichannel, the samples are interleaved in the buffer:
3//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::Pull;
12use embassy_time::{Duration, Ticker};
13use {defmt_rtt as _, panic_probe as _};
14
15bind_interrupts!(struct Irqs {
16 ADC_IRQ_FIFO => InterruptHandler;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = embassy_rp::init(Default::default());
22 info!("Here we go!");
23
24 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
25 let mut dma = p.DMA_CH0;
26 let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
27 let mut pins = [
28 Channel::new_pin(p.PIN_27, Pull::Down),
29 Channel::new_pin(p.PIN_28, Pull::None),
30 Channel::new_pin(p.PIN_29, Pull::Up),
31 Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
32 ];
33
34 const BLOCK_SIZE: usize = 100;
35 const NUM_CHANNELS: usize = 4;
36 let mut ticker = Ticker::every(Duration::from_secs(1));
37 loop {
38 // Read 100 samples from a single channel
39 let mut buf = [0_u16; BLOCK_SIZE];
40 let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
41 adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
42 info!("single: {:?} ...etc", buf[..8]);
43
44 // Read 100 samples from 4 channels interleaved
45 let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
46 let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
47 adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
48 .await
49 .unwrap();
50 info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
51
52 ticker.next().await;
53 }
54}
diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs
index 29eda95bf..65c246472 100644
--- a/tests/rp/src/bin/adc.rs
+++ b/tests/rp/src/bin/adc.rs
@@ -130,6 +130,19 @@ async fn main(_spawner: Spawner) {
130 defmt::assert!(temp.iter().all(|t| *t > 0.0)); 130 defmt::assert!(temp.iter().all(|t| *t > 0.0));
131 defmt::assert!(temp.iter().all(|t| *t < 60.0)); 131 defmt::assert!(temp.iter().all(|t| *t < 60.0));
132 } 132 }
133 {
134 let mut multi = [0u16; 2];
135 let mut channels = [
136 Channel::new_pin(&mut p.PIN_26, Pull::Up),
137 Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR),
138 ];
139 adc.read_many_multichannel(&mut channels, &mut multi, 1, &mut p.DMA_CH0)
140 .await
141 .unwrap();
142 defmt::assert!(multi[0] > 3_000);
143 let temp = convert_to_celsius(multi[1]);
144 defmt::assert!(temp > 0.0 && temp < 60.0);
145 }
133 146
134 info!("Test OK"); 147 info!("Test OK");
135 cortex_m::asm::bkpt(); 148 cortex_m::asm::bkpt();