mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 00:02:28 +00:00
Merge pull request #3044 from kalkyl/adc-multi
rp: Add multichannel ADC
This commit is contained in:
commit
cfe8561550
@ -219,18 +219,30 @@ impl<'d> Adc<'d, Async> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note for refactoring: we don't require the actual Channels here, just the channel numbers.
|
||||||
|
// The public api is responsible for asserting ownership of the actual Channels.
|
||||||
async fn read_many_inner<W: dma::Word>(
|
async fn read_many_inner<W: dma::Word>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ch: &mut Channel<'_>,
|
channels: impl Iterator<Item = u8>,
|
||||||
buf: &mut [W],
|
buf: &mut [W],
|
||||||
fcs_err: bool,
|
fcs_err: bool,
|
||||||
div: u16,
|
div: u16,
|
||||||
dma: impl Peripheral<P = impl dma::Channel>,
|
dma: impl Peripheral<P = impl dma::Channel>,
|
||||||
) -> Result<(), Error> {
|
) -> 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();
|
let r = Self::regs();
|
||||||
// clear previous errors and set channel
|
// clear previous errors and set channel
|
||||||
r.cs().modify(|w| {
|
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_err_sticky(true); // clear previous errors
|
||||||
w.set_start_many(false);
|
w.set_start_many(false);
|
||||||
});
|
});
|
||||||
@ -283,7 +295,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.
|
/// 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]
|
#[inline]
|
||||||
pub async fn read_many<S: AdcSample>(
|
pub async fn read_many<S: AdcSample>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -292,10 +346,13 @@ impl<'d> Adc<'d, Async> {
|
|||||||
div: u16,
|
div: u16,
|
||||||
dma: impl Peripheral<P = impl dma::Channel>,
|
dma: impl Peripheral<P = impl dma::Channel>,
|
||||||
) -> Result<(), Error> {
|
) -> 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]
|
#[inline]
|
||||||
pub async fn read_many_raw(
|
pub async fn read_many_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -306,7 +363,13 @@ impl<'d> Adc<'d, Async> {
|
|||||||
) {
|
) {
|
||||||
// errors are reported in individual samples
|
// errors are reported in individual samples
|
||||||
let _ = self
|
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;
|
.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 > 0.0));
|
||||||
defmt::assert!(temp.iter().all(|t| *t < 60.0));
|
defmt::assert!(temp.iter().all(|t| *t < 60.0));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
let mut multi = [0u16; 2];
|
||||||
|
let mut channels = [
|
||||||
|
Channel::new_pin(&mut p.PIN_26, 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");
|
info!("Test OK");
|
||||||
cortex_m::asm::bkpt();
|
cortex_m::asm::bkpt();
|
||||||
|
Loading…
Reference in New Issue
Block a user