mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
rp: Add multichannel ADC
This commit is contained in:
parent
5f9bc6def7
commit
bf36bec9bb
@ -221,16 +221,26 @@ impl<'d> Adc<'d, Async> {
|
||||
|
||||
async fn read_many_inner<W: dma::Word>(
|
||||
&mut self,
|
||||
ch: &mut Channel<'_>,
|
||||
channels: impl Iterator<Item = u8>,
|
||||
buf: &mut [W],
|
||||
fcs_err: bool,
|
||||
div: u16,
|
||||
dma: impl Peripheral<P = impl dma::Channel>,
|
||||
) -> Result<(), Error> {
|
||||
let mut rrobin = 0_u8;
|
||||
for c in channels {
|
||||
rrobin |= 1 << c;
|
||||
}
|
||||
let first_ch = rrobin.trailing_zeros() as u8;
|
||||
if rrobin.count_ones() == 1 {
|
||||
rrobin = 0;
|
||||
}
|
||||
|
||||
let r = Self::regs();
|
||||
// clear previous errors and set channel
|
||||
r.cs().modify(|w| {
|
||||
w.set_ainsel(ch.channel());
|
||||
w.set_ainsel(first_ch);
|
||||
w.set_rrobin(rrobin);
|
||||
w.set_err_sticky(true); // clear previous errors
|
||||
w.set_start_many(false);
|
||||
});
|
||||
@ -283,7 +293,49 @@ impl<'d> Adc<'d, Async> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample multiple values from multiple channels using DMA.
|
||||
/// Samples are stored in an interleaved fashion inside the buffer.
|
||||
/// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)`
|
||||
/// Any `div` value of less than 96 will have the same effect as setting it to 0
|
||||
#[inline]
|
||||
pub async fn read_many_multichannel<S: AdcSample>(
|
||||
&mut self,
|
||||
ch: &mut [Channel<'_>],
|
||||
buf: &mut [S],
|
||||
div: u16,
|
||||
dma: impl Peripheral<P = impl dma::Channel>,
|
||||
) -> Result<(), Error> {
|
||||
self.read_many_inner(ch.iter().map(|c| c.channel()), buf, false, div, dma)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Sample multiple values from multiple channels using DMA, with errors inlined in samples.
|
||||
/// Samples are stored in an interleaved fashion inside the buffer.
|
||||
/// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)`
|
||||
/// Any `div` value of less than 96 will have the same effect as setting it to 0
|
||||
#[inline]
|
||||
pub async fn read_many_multichannel_raw(
|
||||
&mut self,
|
||||
ch: &mut [Channel<'_>],
|
||||
buf: &mut [Sample],
|
||||
div: u16,
|
||||
dma: impl Peripheral<P = impl dma::Channel>,
|
||||
) {
|
||||
// errors are reported in individual samples
|
||||
let _ = self
|
||||
.read_many_inner(
|
||||
ch.iter().map(|c| c.channel()),
|
||||
unsafe { mem::transmute::<_, &mut [u16]>(buf) },
|
||||
true,
|
||||
div,
|
||||
dma,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Sample multiple values from a channel using DMA.
|
||||
/// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)`
|
||||
/// Any `div` value of less than 96 will have the same effect as setting it to 0
|
||||
#[inline]
|
||||
pub async fn read_many<S: AdcSample>(
|
||||
&mut self,
|
||||
@ -292,10 +344,13 @@ impl<'d> Adc<'d, Async> {
|
||||
div: u16,
|
||||
dma: impl Peripheral<P = impl dma::Channel>,
|
||||
) -> Result<(), Error> {
|
||||
self.read_many_inner(ch, buf, false, div, dma).await
|
||||
self.read_many_inner([ch.channel()].into_iter(), buf, false, div, dma)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Sample multiple values from a channel using DMA with errors inlined in samples.
|
||||
/// Sample multiple values from a channel using DMA, with errors inlined in samples.
|
||||
/// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)`
|
||||
/// Any `div` value of less than 96 will have the same effect as setting it to 0
|
||||
#[inline]
|
||||
pub async fn read_many_raw(
|
||||
&mut self,
|
||||
@ -306,7 +361,13 @@ impl<'d> Adc<'d, Async> {
|
||||
) {
|
||||
// errors are reported in individual samples
|
||||
let _ = self
|
||||
.read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, div, dma)
|
||||
.read_many_inner(
|
||||
[ch.channel()].into_iter(),
|
||||
unsafe { mem::transmute::<_, &mut [u16]>(buf) },
|
||||
true,
|
||||
div,
|
||||
dma,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
54
examples/rp/src/bin/adc_dma.rs
Normal file
54
examples/rp/src/bin/adc_dma.rs
Normal file
@ -0,0 +1,54 @@
|
||||
//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
|
||||
//! For multichannel, the samples are interleaved in the buffer:
|
||||
//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
ADC_IRQ_FIFO => InterruptHandler;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Here we go!");
|
||||
|
||||
let mut adc = Adc::new(p.ADC, Irqs, Config::default());
|
||||
let mut dma = p.DMA_CH0;
|
||||
let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
|
||||
let mut pins = [
|
||||
Channel::new_pin(p.PIN_27, Pull::Down),
|
||||
Channel::new_pin(p.PIN_28, Pull::None),
|
||||
Channel::new_pin(p.PIN_29, Pull::Up),
|
||||
Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
|
||||
];
|
||||
|
||||
const BLOCK_SIZE: usize = 100;
|
||||
const NUM_CHANNELS: usize = 4;
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
// Read 100 samples from a single channel
|
||||
let mut buf = [0_u16; BLOCK_SIZE];
|
||||
let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
|
||||
adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
|
||||
info!("single: {:?} ...etc", buf[..8]);
|
||||
|
||||
// Read 100 samples from 4 channels interleaved
|
||||
let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
|
||||
let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
|
||||
adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
|
||||
.await
|
||||
.unwrap();
|
||||
info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
@ -130,6 +130,19 @@ async fn main(_spawner: Spawner) {
|
||||
defmt::assert!(temp.iter().all(|t| *t > 0.0));
|
||||
defmt::assert!(temp.iter().all(|t| *t < 60.0));
|
||||
}
|
||||
{
|
||||
let mut multi = [0u16; 2];
|
||||
let mut channels = [
|
||||
Channel::new_pin(&mut p.PIN_29, Pull::Up),
|
||||
Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR),
|
||||
];
|
||||
adc.read_many_multichannel(&mut channels, &mut multi, 1, &mut p.DMA_CH0)
|
||||
.await
|
||||
.unwrap();
|
||||
defmt::assert!(multi[0] > 3_000);
|
||||
let temp = convert_to_celsius(multi[1]);
|
||||
defmt::assert!(temp > 0.0 && temp < 60.0);
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
|
Loading…
Reference in New Issue
Block a user