320 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
#[cfg(bdma)]
 | 
						|
pub(crate) mod bdma;
 | 
						|
#[cfg(dma)]
 | 
						|
pub(crate) mod dma;
 | 
						|
#[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};
 | 
						|
 | 
						|
#[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))]
 | 
						|
pub enum WordSize {
 | 
						|
    OneByte,
 | 
						|
    TwoBytes,
 | 
						|
    FourBytes,
 | 
						|
}
 | 
						|
 | 
						|
impl WordSize {
 | 
						|
    pub fn bytes(&self) -> usize {
 | 
						|
        match self {
 | 
						|
            Self::OneByte => 1,
 | 
						|
            Self::TwoBytes => 2,
 | 
						|
            Self::FourBytes => 4,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
pub trait Word: sealed::Word {
 | 
						|
    fn bits() -> WordSize;
 | 
						|
}
 | 
						|
 | 
						|
impl sealed::Word for u8 {}
 | 
						|
impl Word for u8 {
 | 
						|
    fn bits() -> WordSize {
 | 
						|
        WordSize::OneByte
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl sealed::Word for u16 {}
 | 
						|
impl Word for u16 {
 | 
						|
    fn bits() -> WordSize {
 | 
						|
        WordSize::TwoBytes
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl 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 struct TransferOptions {
 | 
						|
    /// Peripheral burst transfer configuration
 | 
						|
    pub pburst: Burst,
 | 
						|
    /// Memory burst transfer configuration
 | 
						|
    pub mburst: Burst,
 | 
						|
    /// Flow control configuration
 | 
						|
    pub flow_ctrl: FlowControl,
 | 
						|
}
 | 
						|
 | 
						|
impl Default for TransferOptions {
 | 
						|
    fn default() -> Self {
 | 
						|
        Self {
 | 
						|
            pburst: Burst::Single,
 | 
						|
            mburst: Burst::Single,
 | 
						|
            flow_ctrl: FlowControl::Dma,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
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)
 | 
						|
    }
 | 
						|
 | 
						|
    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) {
 | 
						|
    unsafe { mem::transmute(slice) }
 | 
						|
}
 | 
						|
 | 
						|
#[allow(unused)]
 | 
						|
pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) {
 | 
						|
    unsafe { mem::transmute(slice) }
 | 
						|
}
 |