mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
new PR, taking Dirbao's advice to make the DMA impl in a separate struct that consumes Adc<T> to make RingBufferedAdc<T>. Handling overrun similar to RingBufferedUart
This commit is contained in:
parent
ab3cc7226a
commit
f64dd8228b
@ -764,7 +764,7 @@ fn main() {
|
||||
|
||||
#[rustfmt::skip]
|
||||
let signals: HashMap<_, _> = [
|
||||
// (kind, signal) => trait
|
||||
// (kind, signal) => trait
|
||||
(("ucpd", "CC1"), quote!(crate::ucpd::Cc1Pin)),
|
||||
(("ucpd", "CC2"), quote!(crate::ucpd::Cc2Pin)),
|
||||
(("usart", "TX"), quote!(crate::usart::TxPin)),
|
||||
@ -1178,6 +1178,10 @@ fn main() {
|
||||
|
||||
let signals: HashMap<_, _> = [
|
||||
// (kind, signal) => trait
|
||||
(("adc", "ADC"), quote!(crate::adc::RxDma)),
|
||||
(("adc", "ADC1"), quote!(crate::adc::RxDma)),
|
||||
(("adc", "ADC2"), quote!(crate::adc::RxDma)),
|
||||
(("adc", "ADC3"), quote!(crate::adc::RxDma)),
|
||||
(("ucpd", "RX"), quote!(crate::ucpd::RxDma)),
|
||||
(("ucpd", "TX"), quote!(crate::ucpd::TxDma)),
|
||||
(("usart", "RX"), quote!(crate::usart::RxDma)),
|
||||
|
@ -28,6 +28,8 @@ pub use crate::pac::adc::vals::Res as Resolution;
|
||||
pub use crate::pac::adc::vals::SampleTime;
|
||||
use crate::peripherals;
|
||||
|
||||
dma_trait!(RxDma, Instance);
|
||||
|
||||
/// Analog to Digital driver.
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
#[allow(unused)]
|
||||
|
376
embassy-stm32/src/adc/ringbuffered_v2.rs
Normal file
376
embassy-stm32/src/adc/ringbuffered_v2.rs
Normal file
@ -0,0 +1,376 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
use embassy_hal_internal::{into_ref, Peripheral};
|
||||
use stm32_metapac::adc::vals::SampleTime;
|
||||
|
||||
use crate::adc::{Adc, AdcChannel, Instance, RxDma};
|
||||
use crate::dma::ringbuffer::OverrunError;
|
||||
use crate::dma::{Priority, ReadableRingBuffer, TransferOptions};
|
||||
use crate::pac::adc::vals;
|
||||
use crate::rcc;
|
||||
|
||||
fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
|
||||
r.sr().modify(|regs| {
|
||||
regs.set_eoc(false);
|
||||
regs.set_ovr(false);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Sequence {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Ten,
|
||||
Eleven,
|
||||
Twelve,
|
||||
Thirteen,
|
||||
Fourteen,
|
||||
Fifteen,
|
||||
Sixteen,
|
||||
}
|
||||
|
||||
impl From<Sequence> for u8 {
|
||||
fn from(s: Sequence) -> u8 {
|
||||
match s {
|
||||
Sequence::One => 0,
|
||||
Sequence::Two => 1,
|
||||
Sequence::Three => 2,
|
||||
Sequence::Four => 3,
|
||||
Sequence::Five => 4,
|
||||
Sequence::Six => 5,
|
||||
Sequence::Seven => 6,
|
||||
Sequence::Eight => 7,
|
||||
Sequence::Nine => 8,
|
||||
Sequence::Ten => 9,
|
||||
Sequence::Eleven => 10,
|
||||
Sequence::Twelve => 11,
|
||||
Sequence::Thirteen => 12,
|
||||
Sequence::Fourteen => 13,
|
||||
Sequence::Fifteen => 14,
|
||||
Sequence::Sixteen => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Sequence {
|
||||
fn from(val: u8) -> Self {
|
||||
match val {
|
||||
0 => Sequence::One,
|
||||
1 => Sequence::Two,
|
||||
2 => Sequence::Three,
|
||||
3 => Sequence::Four,
|
||||
4 => Sequence::Five,
|
||||
5 => Sequence::Six,
|
||||
6 => Sequence::Seven,
|
||||
7 => Sequence::Eight,
|
||||
8 => Sequence::Nine,
|
||||
9 => Sequence::Ten,
|
||||
10 => Sequence::Eleven,
|
||||
11 => Sequence::Twelve,
|
||||
12 => Sequence::Thirteen,
|
||||
13 => Sequence::Fourteen,
|
||||
14 => Sequence::Fifteen,
|
||||
15 => Sequence::Sixteen,
|
||||
_ => panic!("Invalid sequence number"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RingBufferedAdc<'d, T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
ring_buf: ReadableRingBuffer<'d, u16>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn into_ring_buffered(
|
||||
self,
|
||||
dma: impl Peripheral<P = impl RxDma<T>> + 'd,
|
||||
dma_buf: &'d mut [u16],
|
||||
) -> RingBufferedAdc<'d, T> {
|
||||
assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
|
||||
into_ref!(dma);
|
||||
|
||||
let opts: crate::dma::TransferOptions = TransferOptions {
|
||||
half_transfer_ir: true,
|
||||
priority: Priority::VeryHigh,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Safety: we forget the struct before this function returns.
|
||||
let rx_src = T::regs().dr().as_ptr() as *mut u16;
|
||||
let request = dma.request();
|
||||
|
||||
let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) };
|
||||
|
||||
// Don't disable the clock
|
||||
mem::forget(self);
|
||||
|
||||
RingBufferedAdc {
|
||||
_phantom: PhantomData,
|
||||
ring_buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RingBufferedAdc<'d, T> {
|
||||
fn is_on() -> bool {
|
||||
T::regs().cr2().read().adon()
|
||||
}
|
||||
|
||||
fn stop_adc() {
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(false);
|
||||
});
|
||||
}
|
||||
|
||||
fn start_adc() {
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(true);
|
||||
});
|
||||
}
|
||||
|
||||
/// Sets the channel sample time
|
||||
///
|
||||
/// ## SAFETY:
|
||||
/// - ADON == 0 i.e ADC must not be enabled when this is called.
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
if ch <= 9 {
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) {
|
||||
let ch_iter = ch.iter();
|
||||
for idx in ch_iter {
|
||||
unsafe {
|
||||
Self::set_channel_sample_time(*idx, sample_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sample_sequence(
|
||||
&mut self,
|
||||
sequence: Sequence,
|
||||
channel: &mut impl AdcChannel<T>,
|
||||
sample_time: SampleTime,
|
||||
) {
|
||||
let was_on = Self::is_on();
|
||||
if !was_on {
|
||||
Self::start_adc();
|
||||
}
|
||||
|
||||
//Check the sequence is long enough
|
||||
T::regs().sqr1().modify(|r| {
|
||||
let prev: Sequence = r.l().into();
|
||||
if prev < sequence {
|
||||
let new_l: Sequence = sequence;
|
||||
trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8);
|
||||
r.set_l(sequence.into())
|
||||
} else {
|
||||
r.set_l(prev.into())
|
||||
}
|
||||
});
|
||||
|
||||
//Set this GPIO as an analog input.
|
||||
channel.setup();
|
||||
|
||||
//Set the channel in the right sequence field.
|
||||
match sequence {
|
||||
Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())),
|
||||
Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())),
|
||||
Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())),
|
||||
Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())),
|
||||
Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())),
|
||||
Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())),
|
||||
Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(6, channel.channel())),
|
||||
Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(7, channel.channel())),
|
||||
Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(8, channel.channel())),
|
||||
Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(9, channel.channel())),
|
||||
Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(10, channel.channel())),
|
||||
Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(11, channel.channel())),
|
||||
Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(12, channel.channel())),
|
||||
Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(13, channel.channel())),
|
||||
Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(14, channel.channel())),
|
||||
Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(15, channel.channel())),
|
||||
};
|
||||
|
||||
if !was_on {
|
||||
Self::stop_adc();
|
||||
}
|
||||
|
||||
self.set_channels_sample_time(&[channel.channel()], sample_time);
|
||||
|
||||
Self::start_adc();
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> Result<(), OverrunError> {
|
||||
self.ring_buf.clear();
|
||||
|
||||
self.setup_adc();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> {
|
||||
self.teardown_adc();
|
||||
Err(err)
|
||||
}
|
||||
|
||||
pub fn teardown_adc(&mut self) {
|
||||
// Stop the DMA transfer
|
||||
self.ring_buf.request_stop();
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// Stop ADC
|
||||
r.cr2().modify(|reg| {
|
||||
// Stop ADC
|
||||
reg.set_swstart(false);
|
||||
// Stop DMA
|
||||
reg.set_dma(false);
|
||||
});
|
||||
|
||||
r.cr1().modify(|w| {
|
||||
// Disable interrupt for end of conversion
|
||||
w.set_eocie(false);
|
||||
// Disable interrupt for overrun
|
||||
w.set_ovrie(false);
|
||||
});
|
||||
|
||||
clear_interrupt_flags(r);
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn setup_adc(&mut self) {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
self.ring_buf.start();
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
//Enable ADC
|
||||
let was_on = Self::is_on();
|
||||
if !was_on {
|
||||
r.cr2().modify(|reg| {
|
||||
reg.set_adon(false);
|
||||
reg.set_swstart(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all interrupts
|
||||
r.sr().modify(|regs| {
|
||||
regs.set_eoc(false);
|
||||
regs.set_ovr(false);
|
||||
regs.set_strt(false);
|
||||
});
|
||||
|
||||
r.cr1().modify(|w| {
|
||||
// Enable interrupt for end of conversion
|
||||
w.set_eocie(true);
|
||||
// Enable interrupt for overrun
|
||||
w.set_ovrie(true);
|
||||
// Scanning converisons of multiple channels
|
||||
w.set_scan(true);
|
||||
// Continuous conversion mode
|
||||
w.set_discen(false);
|
||||
});
|
||||
|
||||
r.cr2().modify(|w| {
|
||||
// Enable DMA mode
|
||||
w.set_dma(true);
|
||||
// Enable continuous conversions
|
||||
w.set_cont(vals::Cont::CONTINUOUS);
|
||||
// DMA requests are issues as long as DMA=1 and data are converted.
|
||||
w.set_dds(vals::Dds::CONTINUOUS);
|
||||
// EOC flag is set at the end of each conversion.
|
||||
w.set_eocs(vals::Eocs::EACHCONVERSION);
|
||||
});
|
||||
|
||||
//Being ADC conversions
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(true);
|
||||
reg.set_swstart(true);
|
||||
});
|
||||
|
||||
super::blocking_delay_us(3);
|
||||
}
|
||||
|
||||
/// Read bytes that are readily available in the ring buffer.
|
||||
/// If no bytes are currently available in the buffer the call waits until the some
|
||||
/// bytes are available (at least one byte and at most half the buffer size)
|
||||
///
|
||||
/// Background receive is started if `start()` has not been previously called.
|
||||
///
|
||||
/// Receive in the background is terminated if an error is returned.
|
||||
/// It must then manually be started again by calling `start()` or by re-calling `read()`.
|
||||
pub async fn read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
|
||||
let r = T::regs();
|
||||
|
||||
// Start background receive if it was not already started
|
||||
if !r.cr2().read().dma() {
|
||||
self.start()?;
|
||||
}
|
||||
|
||||
// Clear overrun flag if set.
|
||||
if r.sr().read().ovr() {
|
||||
r.sr().modify(|regs| {
|
||||
regs.set_ovr(false);
|
||||
// regs.set_eoc(false);
|
||||
});
|
||||
// return self.stop(OverrunError);
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.ring_buf.read(buf) {
|
||||
Ok((0, _)) => {}
|
||||
Ok((len, _)) => {
|
||||
return Ok(len);
|
||||
}
|
||||
Err(_) => {
|
||||
return self.stop(OverrunError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_exact<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
|
||||
let r = T::regs();
|
||||
|
||||
// Start background receive if it was not already started
|
||||
if !r.cr2().read().dma() {
|
||||
self.start()?;
|
||||
}
|
||||
|
||||
// Clear overrun flag if set.
|
||||
if r.sr().read().ovr() {
|
||||
r.sr().modify(|regs| {
|
||||
regs.set_ovr(false);
|
||||
// regs.set_eoc(false);
|
||||
});
|
||||
// return self.stop(OverrunError);
|
||||
}
|
||||
match self.ring_buf.read_exact(buf).await {
|
||||
Ok(len) => Ok(len),
|
||||
Err(_) => self.stop(OverrunError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.teardown_adc();
|
||||
rcc::disable::<T>();
|
||||
}
|
||||
}
|
@ -6,6 +6,9 @@ use crate::peripherals::ADC1;
|
||||
use crate::time::Hertz;
|
||||
use crate::{rcc, Peripheral};
|
||||
|
||||
mod ringbuffered_v2;
|
||||
pub use ringbuffered_v2::{RingBufferedAdc, Sequence};
|
||||
|
||||
/// Default VREF voltage used for sample conversion to millivolts.
|
||||
pub const VREF_DEFAULT_MV: u32 = 3300;
|
||||
/// VREF voltage used for factory calibration of VREFINTCAL register.
|
||||
|
59
examples/stm32f4/src/bin/adc_dma.rs
Normal file
59
examples/stm32f4/src/bin/adc_dma.rs
Normal file
@ -0,0 +1,59 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use cortex_m::singleton;
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::adc::RingBufferedAdc;
|
||||
use embassy_stm32::adc::{Adc, SampleTime, Sequence};
|
||||
use embassy_time::{Instant, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
const ADC_BUF_SIZE: usize = 1024;
|
||||
let mut p = embassy_stm32::init(Default::default());
|
||||
|
||||
let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
|
||||
|
||||
let adc = Adc::new(p.ADC1);
|
||||
|
||||
let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data);
|
||||
|
||||
adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
|
||||
adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112);
|
||||
adc.set_sample_sequence(Sequence::Three, &mut p.PA1, SampleTime::CYCLES112);
|
||||
adc.set_sample_sequence(Sequence::Four, &mut p.PA3, SampleTime::CYCLES112);
|
||||
|
||||
// Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around
|
||||
// to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of
|
||||
// what channel is at what index is lost. The buffer must be cleared and reset. This *is* handled here, but allowing this to happen will cause
|
||||
// a reduction of performance as each time the buffer is reset, the adc & dma buffer must be restarted.
|
||||
|
||||
// An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most
|
||||
// frequently.
|
||||
let mut tic = Instant::now();
|
||||
let mut buffer1: [u16; 256] = [0u16; 256];
|
||||
let _ = adc.start();
|
||||
loop {
|
||||
match adc.read(&mut buffer1).await {
|
||||
Ok(_data) => {
|
||||
let toc = Instant::now();
|
||||
info!(
|
||||
"\n adc1: {} dt = {}, n = {}",
|
||||
buffer1[0..16],
|
||||
(toc - tic).as_micros(),
|
||||
_data
|
||||
);
|
||||
tic = toc;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error: {:?}", e);
|
||||
buffer1 = [0u16; 256];
|
||||
let _ = adc.start();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Timer::after_micros(300).await;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user