diff options
| author | kalkyl <[email protected]> | 2024-06-05 09:42:08 +0200 |
|---|---|---|
| committer | kalkyl <[email protected]> | 2024-06-05 09:42:08 +0200 |
| commit | bf36bec9bb85caea613687ab9e0c1652f414b95c (patch) | |
| tree | 80a8f818648742dabff97fd2c27053e7211e1077 | |
| parent | 5f9bc6def7ea8698a6ce45d8e12e1d1bd8cce876 (diff) | |
rp: Add multichannel ADC
| -rw-r--r-- | embassy-rp/src/adc.rs | 71 | ||||
| -rw-r--r-- | examples/rp/src/bin/adc_dma.rs | 54 | ||||
| -rw-r--r-- | tests/rp/src/bin/adc.rs | 13 |
3 files changed, 133 insertions, 5 deletions
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 101c5b71f..be453d08e 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs | |||
| @@ -221,16 +221,26 @@ impl<'d> Adc<'d, Async> { | |||
| 221 | 221 | ||
| 222 | async fn read_many_inner<W: dma::Word>( | 222 | async fn read_many_inner<W: dma::Word>( |
| 223 | &mut self, | 223 | &mut self, |
| 224 | ch: &mut Channel<'_>, | 224 | channels: impl Iterator<Item = u8>, |
| 225 | buf: &mut [W], | 225 | buf: &mut [W], |
| 226 | fcs_err: bool, | 226 | fcs_err: bool, |
| 227 | div: u16, | 227 | div: u16, |
| 228 | dma: impl Peripheral<P = impl dma::Channel>, | 228 | dma: impl Peripheral<P = impl dma::Channel>, |
| 229 | ) -> Result<(), Error> { | 229 | ) -> Result<(), Error> { |
| 230 | let mut rrobin = 0_u8; | ||
| 231 | for c in channels { | ||
| 232 | rrobin |= 1 << c; | ||
| 233 | } | ||
| 234 | let first_ch = rrobin.trailing_zeros() as u8; | ||
| 235 | if rrobin.count_ones() == 1 { | ||
| 236 | rrobin = 0; | ||
| 237 | } | ||
| 238 | |||
| 230 | let r = Self::regs(); | 239 | let r = Self::regs(); |
| 231 | // clear previous errors and set channel | 240 | // clear previous errors and set channel |
| 232 | r.cs().modify(|w| { | 241 | r.cs().modify(|w| { |
| 233 | w.set_ainsel(ch.channel()); | 242 | w.set_ainsel(first_ch); |
| 243 | w.set_rrobin(rrobin); | ||
| 234 | w.set_err_sticky(true); // clear previous errors | 244 | w.set_err_sticky(true); // clear previous errors |
| 235 | w.set_start_many(false); | 245 | w.set_start_many(false); |
| 236 | }); | 246 | }); |
| @@ -283,7 +293,49 @@ impl<'d> Adc<'d, Async> { | |||
| 283 | } | 293 | } |
| 284 | } | 294 | } |
| 285 | 295 | ||
| 296 | /// Sample multiple values from multiple channels using DMA. | ||
| 297 | /// Samples are stored in an interleaved fashion inside the buffer. | ||
| 298 | /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)` | ||
| 299 | /// Any `div` value of less than 96 will have the same effect as setting it to 0 | ||
| 300 | #[inline] | ||
| 301 | pub async fn read_many_multichannel<S: AdcSample>( | ||
| 302 | &mut self, | ||
| 303 | ch: &mut [Channel<'_>], | ||
| 304 | buf: &mut [S], | ||
| 305 | div: u16, | ||
| 306 | dma: impl Peripheral<P = impl dma::Channel>, | ||
| 307 | ) -> Result<(), Error> { | ||
| 308 | self.read_many_inner(ch.iter().map(|c| c.channel()), buf, false, div, dma) | ||
| 309 | .await | ||
| 310 | } | ||
| 311 | |||
| 312 | /// Sample multiple values from multiple channels using DMA, with errors inlined in samples. | ||
| 313 | /// Samples are stored in an interleaved fashion inside the buffer. | ||
| 314 | /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)` | ||
| 315 | /// Any `div` value of less than 96 will have the same effect as setting it to 0 | ||
| 316 | #[inline] | ||
| 317 | pub async fn read_many_multichannel_raw( | ||
| 318 | &mut self, | ||
| 319 | ch: &mut [Channel<'_>], | ||
| 320 | buf: &mut [Sample], | ||
| 321 | div: u16, | ||
| 322 | dma: impl Peripheral<P = impl dma::Channel>, | ||
| 323 | ) { | ||
| 324 | // errors are reported in individual samples | ||
| 325 | let _ = self | ||
| 326 | .read_many_inner( | ||
| 327 | ch.iter().map(|c| c.channel()), | ||
| 328 | unsafe { mem::transmute::<_, &mut [u16]>(buf) }, | ||
| 329 | true, | ||
| 330 | div, | ||
| 331 | dma, | ||
| 332 | ) | ||
| 333 | .await; | ||
| 334 | } | ||
| 335 | |||
| 286 | /// Sample multiple values from a channel using DMA. | 336 | /// Sample multiple values from a channel using DMA. |
| 337 | /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)` | ||
| 338 | /// Any `div` value of less than 96 will have the same effect as setting it to 0 | ||
| 287 | #[inline] | 339 | #[inline] |
| 288 | pub async fn read_many<S: AdcSample>( | 340 | pub async fn read_many<S: AdcSample>( |
| 289 | &mut self, | 341 | &mut self, |
| @@ -292,10 +344,13 @@ impl<'d> Adc<'d, Async> { | |||
| 292 | div: u16, | 344 | div: u16, |
| 293 | dma: impl Peripheral<P = impl dma::Channel>, | 345 | dma: impl Peripheral<P = impl dma::Channel>, |
| 294 | ) -> Result<(), Error> { | 346 | ) -> Result<(), Error> { |
| 295 | self.read_many_inner(ch, buf, false, div, dma).await | 347 | self.read_many_inner([ch.channel()].into_iter(), buf, false, div, dma) |
| 348 | .await | ||
| 296 | } | 349 | } |
| 297 | 350 | ||
| 298 | /// Sample multiple values from a channel using DMA with errors inlined in samples. | 351 | /// Sample multiple values from a channel using DMA, with errors inlined in samples. |
| 352 | /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)` | ||
| 353 | /// Any `div` value of less than 96 will have the same effect as setting it to 0 | ||
| 299 | #[inline] | 354 | #[inline] |
| 300 | pub async fn read_many_raw( | 355 | pub async fn read_many_raw( |
| 301 | &mut self, | 356 | &mut self, |
| @@ -306,7 +361,13 @@ impl<'d> Adc<'d, Async> { | |||
| 306 | ) { | 361 | ) { |
| 307 | // errors are reported in individual samples | 362 | // errors are reported in individual samples |
| 308 | let _ = self | 363 | let _ = self |
| 309 | .read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, div, dma) | 364 | .read_many_inner( |
| 365 | [ch.channel()].into_iter(), | ||
| 366 | unsafe { mem::transmute::<_, &mut [u16]>(buf) }, | ||
| 367 | true, | ||
| 368 | div, | ||
| 369 | dma, | ||
| 370 | ) | ||
| 310 | .await; | 371 | .await; |
| 311 | } | 372 | } |
| 312 | } | 373 | } |
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 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::gpio::Pull; | ||
| 12 | use embassy_time::{Duration, Ticker}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | ADC_IRQ_FIFO => InterruptHandler; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async 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..0f13e626f 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_29, 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(); |
