stm32/dma: refactor.
This commit is contained in:
@ -1,124 +1,39 @@
|
||||
#[cfg(bdma)]
|
||||
pub(crate) mod bdma;
|
||||
#[cfg(dma)]
|
||||
pub(crate) mod dma;
|
||||
#[cfg(dma)]
|
||||
pub use dma::*;
|
||||
|
||||
// stm32h7 has both dma and bdma. In that case, we export dma as "main" dma,
|
||||
// and bdma as "secondary", under `embassy_stm32::dma::bdma`.
|
||||
#[cfg(all(bdma, dma))]
|
||||
pub mod bdma;
|
||||
|
||||
#[cfg(all(bdma, not(dma)))]
|
||||
pub(crate) mod bdma;
|
||||
#[cfg(all(bdma, not(dma)))]
|
||||
pub use bdma::*;
|
||||
|
||||
#[cfg(gpdma)]
|
||||
pub(crate) mod gpdma;
|
||||
#[cfg(gpdma)]
|
||||
pub use gpdma::*;
|
||||
|
||||
#[cfg(dmamux)]
|
||||
mod dmamux;
|
||||
#[cfg(gpdma)]
|
||||
mod gpdma;
|
||||
|
||||
use core::future::Future;
|
||||
use core::mem;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
#[cfg(any(dma, bdma))]
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_hal_common::{impl_peripheral, into_ref};
|
||||
use embassy_hal_common::impl_peripheral;
|
||||
|
||||
#[cfg(dmamux)]
|
||||
pub use self::dmamux::*;
|
||||
use crate::Peripheral;
|
||||
|
||||
#[cfg(feature = "unstable-pac")]
|
||||
pub mod low_level {
|
||||
pub use super::transfers::*;
|
||||
}
|
||||
|
||||
pub(crate) use transfers::*;
|
||||
|
||||
#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))]
|
||||
pub type Request = u8;
|
||||
#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))]
|
||||
pub type Request = ();
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Word {}
|
||||
|
||||
pub trait Channel {
|
||||
/// Starts this channel for writing a stream of words.
|
||||
///
|
||||
/// Safety:
|
||||
/// - `buf` must point to a valid buffer for DMA reading.
|
||||
/// - `buf` must be alive for the entire duration of the DMA transfer.
|
||||
/// - `reg_addr` must be a valid peripheral register address to write to.
|
||||
unsafe fn start_write<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
buf: *const [W],
|
||||
reg_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
/// Starts this channel for writing a word repeatedly.
|
||||
///
|
||||
/// Safety:
|
||||
/// - `reg_addr` must be a valid peripheral register address to write to.
|
||||
unsafe fn start_write_repeated<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
repeated: *const W,
|
||||
count: usize,
|
||||
reg_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
/// Starts this channel for reading a stream of words.
|
||||
///
|
||||
/// Safety:
|
||||
/// - `buf` must point to a valid buffer for DMA writing.
|
||||
/// - `buf` must be alive for the entire duration of the DMA transfer.
|
||||
/// - `reg_addr` must be a valid peripheral register address to read from.
|
||||
unsafe fn start_read<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
reg_addr: *const W,
|
||||
buf: *mut [W],
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
/// DMA double-buffered mode is unsafe as UB can happen when the hardware writes to a buffer currently owned by the software
|
||||
/// more information can be found here: https://github.com/embassy-rs/embassy/issues/702
|
||||
/// This feature is now used solely for the purposes of implementing giant DMA transfers required for DCMI
|
||||
unsafe fn start_double_buffered_read<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
reg_addr: *const W,
|
||||
buffer0: *mut W,
|
||||
buffer1: *mut W,
|
||||
buffer_len: usize,
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
unsafe fn set_buffer0<W: super::Word>(&mut self, buffer: *mut W);
|
||||
|
||||
unsafe fn set_buffer1<W: super::Word>(&mut self, buffer: *mut W);
|
||||
|
||||
unsafe fn is_buffer0_accessible(&mut self) -> bool;
|
||||
|
||||
/// Requests the channel to stop.
|
||||
/// NOTE: The channel does not immediately stop, you have to wait
|
||||
/// for `is_running() = false`.
|
||||
fn request_stop(&mut self);
|
||||
|
||||
/// Returns whether this channel is running or stopped.
|
||||
///
|
||||
/// The channel stops running when it either completes or is manually stopped.
|
||||
fn is_running(&self) -> bool;
|
||||
|
||||
/// Returns the total number of remaining transfers.
|
||||
fn remaining_transfers(&mut self) -> u16;
|
||||
|
||||
/// Sets the waker that is called when this channel stops (either completed or manually stopped)
|
||||
fn set_waker(&mut self, waker: &Waker);
|
||||
|
||||
/// This is called when this channel triggers an interrupt.
|
||||
/// Note: Because some channels share an interrupt, this function might be
|
||||
/// called for a channel that didn't trigger an interrupt.
|
||||
fn on_irq();
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
enum Dir {
|
||||
MemoryToPeripheral,
|
||||
PeripheralToMemory,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -139,191 +54,39 @@ impl WordSize {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Word: sealed::Word {
|
||||
mod word_sealed {
|
||||
pub trait Word {}
|
||||
}
|
||||
|
||||
pub trait Word: word_sealed::Word {
|
||||
fn bits() -> WordSize;
|
||||
}
|
||||
|
||||
impl sealed::Word for u8 {}
|
||||
impl word_sealed::Word for u8 {}
|
||||
impl Word for u8 {
|
||||
fn bits() -> WordSize {
|
||||
WordSize::OneByte
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::Word for u16 {}
|
||||
impl word_sealed::Word for u16 {}
|
||||
impl Word for u16 {
|
||||
fn bits() -> WordSize {
|
||||
WordSize::TwoBytes
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::Word for u32 {}
|
||||
impl word_sealed::Word for u32 {}
|
||||
impl Word for u32 {
|
||||
fn bits() -> WordSize {
|
||||
WordSize::FourBytes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Burst {
|
||||
/// Single transfer
|
||||
Single,
|
||||
/// Incremental burst of 4 beats
|
||||
Incr4,
|
||||
/// Incremental burst of 8 beats
|
||||
Incr8,
|
||||
/// Incremental burst of 16 beats
|
||||
Incr16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FlowControl {
|
||||
/// Flow control by DMA
|
||||
Dma,
|
||||
/// Flow control by peripheral
|
||||
Peripheral,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FifoThreshold {
|
||||
/// 1/4 full FIFO
|
||||
Quarter,
|
||||
/// 1/2 full FIFO
|
||||
Half,
|
||||
/// 3/4 full FIFO
|
||||
ThreeQuarters,
|
||||
/// Full FIFO
|
||||
Full,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct TransferOptions {
|
||||
/// Peripheral burst transfer configuration
|
||||
pub pburst: Burst,
|
||||
/// Memory burst transfer configuration
|
||||
pub mburst: Burst,
|
||||
/// Flow control configuration
|
||||
pub flow_ctrl: FlowControl,
|
||||
/// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
|
||||
pub fifo_threshold: Option<FifoThreshold>,
|
||||
}
|
||||
|
||||
impl Default for TransferOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pburst: Burst::Single,
|
||||
mburst: Burst::Single,
|
||||
flow_ctrl: FlowControl::Dma,
|
||||
fifo_threshold: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod transfers {
|
||||
use embassy_hal_common::PeripheralRef;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn read<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
reg_addr: *mut W,
|
||||
buf: &'a mut [W],
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
|
||||
into_ref!(channel);
|
||||
|
||||
unsafe { channel.start_read::<W>(request, reg_addr, buf, Default::default()) };
|
||||
|
||||
Transfer::new(channel)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn write<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
buf: &'a [W],
|
||||
reg_addr: *mut W,
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
|
||||
into_ref!(channel);
|
||||
|
||||
unsafe { channel.start_write::<W>(request, buf, reg_addr, Default::default()) };
|
||||
|
||||
Transfer::new(channel)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn write_repeated<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
repeated: *const W,
|
||||
count: usize,
|
||||
reg_addr: *mut W,
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
into_ref!(channel);
|
||||
|
||||
unsafe { channel.start_write_repeated::<W>(request, repeated, count, reg_addr, Default::default()) };
|
||||
|
||||
Transfer::new(channel)
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub(crate) struct Transfer<'a, C: Channel> {
|
||||
channel: PeripheralRef<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub(crate) fn new(channel: impl Peripheral<P = C> + 'a) -> Self {
|
||||
into_ref!(channel);
|
||||
Self { channel }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||
fn drop(&mut self) {
|
||||
self.channel.request_stop();
|
||||
while self.channel.is_running() {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.channel.set_waker(cx.waker());
|
||||
if self.channel.is_running() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||
|
||||
pub struct NoDma;
|
||||
|
||||
impl_peripheral!(NoDma);
|
||||
|
||||
// safety: must be called only once at startup
|
||||
pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) {
|
||||
#[cfg(bdma)]
|
||||
bdma::init(bdma_priority);
|
||||
#[cfg(dma)]
|
||||
dma::init(dma_priority);
|
||||
#[cfg(dmamux)]
|
||||
dmamux::init();
|
||||
#[cfg(gpdma)]
|
||||
gpdma::init();
|
||||
}
|
||||
|
||||
// TODO: replace transmutes with core::ptr::metadata once it's stable
|
||||
#[allow(unused)]
|
||||
pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) {
|
||||
@ -334,3 +97,19 @@ pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) {
|
||||
pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) {
|
||||
unsafe { mem::transmute(slice) }
|
||||
}
|
||||
|
||||
// safety: must be called only once at startup
|
||||
pub(crate) unsafe fn init(
|
||||
#[cfg(bdma)] bdma_priority: Priority,
|
||||
#[cfg(dma)] dma_priority: Priority,
|
||||
#[cfg(gpdma)] gpdma_priority: Priority,
|
||||
) {
|
||||
#[cfg(bdma)]
|
||||
bdma::init(bdma_priority);
|
||||
#[cfg(dma)]
|
||||
dma::init(dma_priority);
|
||||
#[cfg(gpdma)]
|
||||
gpdma::init(gpdma_priority);
|
||||
#[cfg(dmamux)]
|
||||
dmamux::init();
|
||||
}
|
||||
|
Reference in New Issue
Block a user