use core::{
fmt,
marker::PhantomData,
ops::Deref,
pin::Pin,
sync::atomic::{self, Ordering},
};
use as_slice::AsSlice;
use crate::{
pac::{
self,
dma2::{self, st::cr},
Interrupt, DMA1, DMA2, NVIC,
},
qspi,
rcc::{Enable, RccBus, Reset},
serial, spi, state,
};
pub struct DMA<I> {
pub handle: Handle<I, state::Disabled>,
pub streams: Streams<I>,
}
impl<I> DMA<I>
where
I: Instance + Enable + Reset,
{
pub fn new(instance: I) -> Self {
DMA {
handle: Handle::new(instance),
streams: Streams::new(),
}
}
}
pub struct Handle<I, State> {
dma: I,
_state: State,
}
impl<I> Handle<I, state::Disabled>
where
I: Instance + Reset + Enable,
{
fn new(instance: I) -> Self {
Self {
dma: instance,
_state: state::Disabled,
}
}
pub fn enable(self, apb: &mut <I as RccBus>::Bus) -> Handle<I, state::Enabled> {
I::reset(apb);
I::enable(apb);
Handle {
dma: self.dma,
_state: state::Enabled,
}
}
}
pub struct Transfer<T: Target, B, State> {
res: TransferResources<T, B>,
_state: State,
}
impl<T, B> Transfer<T, B, Ready>
where
T: Target,
B: 'static,
{
pub(crate) unsafe fn new<Word>(
handle: &Handle<T::Instance, state::Enabled>,
stream: T::Stream,
buffer: Pin<B>,
target: T,
address: u32,
direction: Direction,
) -> Self
where
B: Deref,
B::Target: Buffer<Word>,
Word: SupportedWordSize,
{
assert!(buffer.len() <= u16::max_value() as usize);
let nr = T::Stream::number();
handle.dma.st[nr].cr.modify(|_, w| w.en().disabled());
while handle.dma.st[nr].cr.read().en().is_enabled() {}
T::Stream::clear_status_flags(&handle.dma);
handle.dma.st[nr].par.write(|w| w.pa().bits(address));
let memory_address = buffer.as_ptr() as u32;
handle.dma.st[nr]
.m0ar
.write(|w| w.m0a().bits(memory_address));
handle.dma.st[nr]
.ndtr
.write(|w| w.ndt().bits(buffer.len() as u16));
handle.dma.st[nr].fcr.modify(|_, w| {
w
.feie()
.disabled()
.dmdis()
.enabled()
});
handle.dma.st[nr].cr.write(|w| {
let w = T::Channel::select(w);
let w = match direction {
Direction::MemoryToPeripheral => w.dir().memory_to_peripheral(),
Direction::PeripheralToMemory => w.dir().peripheral_to_memory(),
};
w
.mburst()
.single()
.pburst()
.single()
.dbm()
.disabled()
.pl()
.very_high()
.msize()
.variant(Word::msize())
.psize()
.variant(Word::psize())
.minc()
.incremented()
.pinc()
.fixed()
.circ()
.disabled()
.pfctrl()
.dma()
.tcie()
.disabled()
.htie()
.disabled()
.teie()
.disabled()
.dmeie()
.disabled()
});
Transfer {
res: TransferResources {
stream,
buffer,
target,
},
_state: Ready,
}
}
pub fn enable_interrupts(
&mut self,
handle: &Handle<T::Instance, state::Enabled>,
interrupts: Interrupts,
) {
handle.dma.st[T::Stream::number()].cr.modify(|_, w| {
let w = if interrupts.transfer_complete {
w.tcie().enabled()
} else {
w
};
let w = if interrupts.half_transfer {
w.htie().enabled()
} else {
w
};
let w = if interrupts.transfer_error {
w.teie().enabled()
} else {
w
};
let w = if interrupts.direct_mode_error {
w.dmeie().enabled()
} else {
w
};
w
});
unsafe { NVIC::unmask(T::INTERRUPT) };
}
pub fn start(self, handle: &Handle<T::Instance, state::Enabled>) -> Transfer<T, B, Started> {
T::Stream::clear_status_flags(&handle.dma);
atomic::fence(Ordering::SeqCst);
handle.dma.st[T::Stream::number()]
.cr
.modify(|_, w| w.en().enabled());
Transfer {
res: self.res,
_state: Started,
}
}
}
impl<T, B> Transfer<T, B, Started>
where
T: Target,
{
pub fn is_active(&self, handle: &Handle<T::Instance, state::Enabled>) -> bool {
handle.dma.st[T::Stream::number()]
.cr
.read()
.en()
.is_enabled()
}
pub fn cancel(&self, handle: &Handle<T::Instance, state::Enabled>) {
handle.dma.st[T::Stream::number()]
.cr
.write(|w| w.en().disabled());
}
pub fn wait(
self,
handle: &Handle<T::Instance, state::Enabled>,
) -> Result<TransferResources<T, B>, (TransferResources<T, B>, Error)> {
NVIC::mask(T::INTERRUPT);
while self.is_active(handle) {
if let Err(error) = Error::check::<T::Stream>(&handle.dma) {
return Err((self.res, error));
}
}
atomic::fence(Ordering::SeqCst);
if let Err(error) = Error::check::<T::Stream>(&handle.dma) {
return Err((self.res, error));
}
Ok(self.res)
}
}
pub struct TransferResources<T: Target, B> {
pub stream: T::Stream,
pub buffer: Pin<B>,
pub target: T,
}
impl<T, B> fmt::Debug for TransferResources<T, B>
where
T: Target,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TransferResources {{ .. }}")
}
}
pub(crate) enum Direction {
MemoryToPeripheral,
PeripheralToMemory,
}
pub trait Target {
type Instance: Deref<Target = dma2::RegisterBlock>;
type Stream: Stream;
type Channel: Channel;
const INTERRUPT: Interrupt;
}
macro_rules! impl_target {
(
$(
$ty:ty,
$instance:ty,
$stream:ident,
$channel:ty,
$interrupt:ident;
)*
) => {
$(
impl Target for $ty {
type Instance = $instance;
type Stream = $stream<$instance>;
type Channel = $channel;
const INTERRUPT: Interrupt = Interrupt::$interrupt;
}
)*
}
}
impl_target!(
spi::Rx<pac::SPI1>, DMA2, Stream0, Channel3, DMA2_STREAM0;
spi::Rx<pac::SPI2>, DMA1, Stream3, Channel0, DMA1_STREAM3;
spi::Rx<pac::SPI3>, DMA1, Stream0, Channel0, DMA1_STREAM0;
spi::Rx<pac::SPI4>, DMA2, Stream0, Channel4, DMA2_STREAM0;
spi::Rx<pac::SPI5>, DMA2, Stream3, Channel2, DMA2_STREAM3;
spi::Tx<pac::SPI1>, DMA2, Stream3, Channel3, DMA2_STREAM3;
spi::Tx<pac::SPI2>, DMA1, Stream4, Channel0, DMA1_STREAM4;
spi::Tx<pac::SPI3>, DMA1, Stream5, Channel0, DMA1_STREAM5;
spi::Tx<pac::SPI4>, DMA2, Stream1, Channel4, DMA2_STREAM1;
spi::Tx<pac::SPI5>, DMA2, Stream4, Channel2, DMA2_STREAM4;
serial::Rx<pac::USART1>, DMA2, Stream2, Channel4, DMA2_STREAM2;
serial::Rx<pac::USART2>, DMA1, Stream5, Channel4, DMA1_STREAM5;
serial::Rx<pac::USART3>, DMA1, Stream1, Channel4, DMA1_STREAM1;
serial::Rx<pac::UART4>, DMA1, Stream2, Channel4, DMA1_STREAM2;
serial::Rx<pac::UART5>, DMA1, Stream0, Channel4, DMA1_STREAM0;
serial::Rx<pac::USART6>, DMA2, Stream1, Channel5, DMA2_STREAM1;
serial::Rx<pac::UART7>, DMA1, Stream3, Channel5, DMA1_STREAM3;
serial::Rx<pac::UART8>, DMA1, Stream6, Channel5, DMA1_STREAM6;
serial::Tx<pac::USART1>, DMA2, Stream7, Channel4, DMA2_STREAM7;
serial::Tx<pac::USART2>, DMA1, Stream6, Channel4, DMA1_STREAM6;
serial::Tx<pac::USART3>, DMA1, Stream3, Channel4, DMA1_STREAM3;
serial::Tx<pac::UART4>, DMA1, Stream4, Channel4, DMA1_STREAM4;
serial::Tx<pac::UART5>, DMA1, Stream7, Channel4, DMA1_STREAM7;
serial::Tx<pac::USART6>, DMA2, Stream6, Channel5, DMA2_STREAM6;
serial::Tx<pac::UART7>, DMA1, Stream1, Channel5, DMA1_STREAM1;
serial::Tx<pac::UART8>, DMA1, Stream0, Channel5, DMA1_STREAM0;
qspi::RxTx<pac::QUADSPI>, DMA2, Stream7, Channel3, DMA2_STREAM7;
);
#[cfg(any(
feature = "stm32f745",
feature = "stm32f746",
feature = "stm32f756",
feature = "stm32f765",
feature = "stm32f767",
feature = "stm32f769",
feature = "stm32f777",
feature = "stm32f778",
feature = "stm32f779",
))]
impl_target!(
spi::Rx<pac::SPI6>, DMA2, Stream6, Channel1, DMA2_STREAM6;
spi::Tx<pac::SPI6>, DMA2, Stream5, Channel1, DMA2_STREAM5;
);
pub trait Stream {
fn number() -> usize;
fn clear_status_flags(dma: &dma2::RegisterBlock);
fn is_transfer_complete(dma: &dma2::RegisterBlock) -> bool;
fn is_half_transfer(dma: &dma2::RegisterBlock) -> bool;
fn is_transfer_error(dma: &dma2::RegisterBlock) -> bool;
fn is_direct_mode_error(dma: &dma2::RegisterBlock) -> bool;
fn is_fifo_error(dma: &dma2::RegisterBlock) -> bool;
}
macro_rules! impl_stream {
(
$(
$name:ident,
$name_lower:ident,
$number:expr,
$flag_reg:ident,
$feif:ident,
$dmeif:ident,
$teif:ident,
$htif:ident,
$tcif:ident,
$flag_clear_reg:ident,
($($flag_clear_field:ident,)*);
)*
) => {
pub struct Streams<I> {
$(pub $name_lower: $name<I>,)*
}
impl<I> Streams<I> {
fn new() -> Self {
Self {
$($name_lower: $name(PhantomData),)*
}
}
}
$(
pub struct $name<I>(PhantomData<I>);
impl<I> Stream for $name<I> {
fn number() -> usize { $number }
fn clear_status_flags(dma: &dma2::RegisterBlock) {
dma.$flag_clear_reg.write(|w|
w
$(.$flag_clear_field().clear())*
);
}
fn is_transfer_complete(dma: &dma2::RegisterBlock) -> bool {
dma.$flag_reg.read().$tcif().is_complete()
}
fn is_half_transfer(dma: &dma2::RegisterBlock) -> bool {
dma.$flag_reg.read().$htif().is_half()
}
fn is_transfer_error(dma: &dma2::RegisterBlock) -> bool {
dma.$flag_reg.read().$teif().is_error()
}
fn is_direct_mode_error(dma: &dma2::RegisterBlock) -> bool {
dma.$flag_reg.read().$dmeif().is_error()
}
fn is_fifo_error(dma: &dma2::RegisterBlock) -> bool {
dma.$flag_reg.read().$feif().is_error()
}
}
)*
}
}
impl_stream!(
Stream0, stream0, 0,
lisr, feif0, dmeif0, teif0, htif0, tcif0,
lifcr, (cfeif0, cdmeif0, cteif0, chtif0, ctcif0,);
Stream1, stream1, 1,
lisr, feif1, dmeif1, teif1, htif1, tcif1,
lifcr, (cfeif1, cdmeif1, cteif1, chtif1, ctcif1,);
Stream2, stream2, 2,
lisr, feif2, dmeif2, teif2, htif2, tcif2,
lifcr, (cfeif2, cdmeif2, cteif2, chtif2, ctcif2,);
Stream3, stream3, 3,
lisr, feif3, dmeif3, teif3, htif3, tcif3,
lifcr, (cfeif3, cdmeif3, cteif3, chtif3, ctcif3,);
Stream4, stream4, 4,
hisr, feif4, dmeif4, teif4, htif4, tcif4,
hifcr, (cfeif4, cdmeif4, cteif4, chtif4, ctcif4,);
Stream5, stream5, 5,
hisr, feif5, dmeif5, teif5, htif5, tcif5,
hifcr, (cfeif5, cdmeif5, cteif5, chtif5, ctcif5,);
Stream6, stream6, 6,
hisr, feif6, dmeif6, teif6, htif6, tcif6,
hifcr, (cfeif6, cdmeif6, cteif6, chtif6, ctcif6,);
Stream7, stream7, 7,
hisr, feif7, dmeif7, teif7, htif7, tcif7,
hifcr, (cfeif7, cdmeif7, cteif7, chtif7, ctcif7,);
);
pub trait Channel {
fn select(w: &mut dma2::st::cr::W) -> &mut dma2::st::cr::W;
}
macro_rules! impl_channel {
($($name:ident, $number:expr;)*) => {
$(
pub struct $name;
impl Channel for $name {
fn select<'r>(w: &'r mut dma2::st::cr::W)
-> &'r mut dma2::st::cr::W
{
w.chsel().bits($number)
}
}
)*
}
}
impl_channel!(
Channel0, 0;
Channel1, 1;
Channel2, 2;
Channel3, 3;
Channel4, 4;
Channel5, 5;
Channel6, 6;
Channel7, 7;
);
pub trait Instance {}
macro_rules! impl_instance {
($($name:ty;)*) => {
$(
impl Instance for $name {
}
)*
}
}
impl_instance!(
DMA1;
DMA2;
);
#[derive(Clone, Copy)]
pub struct Interrupts {
pub transfer_complete: bool,
pub half_transfer: bool,
pub transfer_error: bool,
pub direct_mode_error: bool,
}
impl Default for Interrupts {
fn default() -> Self {
Self {
transfer_complete: false,
half_transfer: false,
transfer_error: false,
direct_mode_error: false,
}
}
}
#[derive(Debug)]
pub enum Error {
Transfer,
DirectMode,
}
impl Error {
pub(crate) fn check<S>(dma: &dma2::RegisterBlock) -> Result<(), Self>
where
S: Stream,
{
if S::is_transfer_error(dma) {
return Err(Error::Transfer);
}
if S::is_direct_mode_error(dma) {
return Err(Error::DirectMode);
}
Ok(())
}
}
pub struct Ready;
pub struct Started;
pub(crate) trait Buffer<Word> {
fn as_ptr(&self) -> *const Word;
fn len(&self) -> usize;
}
impl<T, Word> Buffer<Word> for T
where
T: ?Sized + AsSlice<Element = Word>,
{
fn as_ptr(&self) -> *const Word {
self.as_slice().as_ptr()
}
fn len(&self) -> usize {
self.as_slice().len()
}
}
pub(crate) struct PtrBuffer<Word: SupportedWordSize> {
pub ptr: *const Word,
pub len: usize,
}
impl<Word> Deref for PtrBuffer<Word>
where
Word: SupportedWordSize,
{
type Target = Self;
fn deref(&self) -> &Self::Target {
self
}
}
impl<Word> Buffer<Word> for PtrBuffer<Word>
where
Word: SupportedWordSize,
{
fn as_ptr(&self) -> *const Word {
self.ptr
}
fn len(&self) -> usize {
self.len
}
}
pub trait SupportedWordSize: private::Sealed + Unpin + 'static {
fn msize() -> cr::MSIZE_A;
fn psize() -> cr::PSIZE_A;
}
impl private::Sealed for u8 {}
impl SupportedWordSize for u8 {
fn msize() -> cr::MSIZE_A {
cr::MSIZE_A::BITS8
}
fn psize() -> cr::PSIZE_A {
cr::MSIZE_A::BITS8
}
}
impl private::Sealed for u16 {}
impl SupportedWordSize for u16 {
fn msize() -> cr::MSIZE_A {
cr::MSIZE_A::BITS16
}
fn psize() -> cr::PSIZE_A {
cr::MSIZE_A::BITS16
}
}
mod private {
pub trait Sealed {}
}