use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr;
use crate::adc::Adc;
use crate::pac;
use crate::rcu::{Enable, Rcu};
use crate::spi::{self, Spi};
use crate::usart::{self, Usart};
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum Prio {
Low,
Medium,
High,
UltraHigh,
}
pub(crate) enum Dir {
P2M,
M2P,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum Width {
Bits8,
Bits16,
Bits32,
}
pub struct Channel<const N: u8> {
_marker: PhantomData<()>,
}
impl<const N: u8> Channel<N> {
#[allow(clippy::unused_self)]
fn reg(&self) -> &pac::dma::RegisterBlock {
unsafe { &*pac::Dma::ptr() }
}
}
pub(crate) trait ChannelOps {
fn configure(&mut self, dir: Dir, width: Width, prio: Prio);
fn set_paddr(&mut self, addr: u32);
fn set_maddr(&mut self, addr: u32);
fn set_cnt(&mut self, cnt: u16);
fn cnt(&self) -> u16;
fn set_enabled(&mut self, enabled: bool);
fn is_complete(&self) -> bool;
fn is_error(&self) -> bool;
fn clear_flags(&mut self);
}
macro_rules! channels {
($($N:literal),+ $(,)?) => {
paste::paste! { $(
impl ChannelOps for Channel<$N> {
#[inline]
fn configure(&mut self, dir: Dir, width: Width, prio: Prio) {
self.reg().[<ch $N ctl>]().write(|w| {
let w = w.pnaga()
.fixed()
.mnaga()
.increment()
.m2m()
.disabled()
.cmen()
.disabled()
.errie()
.disabled()
.htfie()
.disabled()
.ftfie()
.disabled();
let w = match dir {
Dir::P2M => w.dir().from_peripheral(),
Dir::M2P => w.dir().from_memory(),
};
let w = match width {
Width::Bits8 => w.mwidth().bits8().pwidth().bits8(),
Width::Bits16 => w.mwidth().bits16().pwidth().bits16(),
Width::Bits32 => w.mwidth().bits32().pwidth().bits32(),
};
match prio {
Prio::Low => w.prio().low(),
Prio::Medium => w.prio().medium(),
Prio::High => w.prio().high(),
Prio::UltraHigh => w.prio().very_high(),
}
});
}
#[inline]
fn set_paddr(&mut self, addr: u32) {
self.reg().[<ch $N paddr>]().write(|w| unsafe { w.bits(addr) });
}
#[inline]
fn set_maddr(&mut self, addr: u32) {
self.reg().[<ch $N maddr>]().write(|w| unsafe { w.bits(addr) });
}
#[inline]
fn set_cnt(&mut self, cnt: u16) {
self.reg().[<ch $N cnt>]().write(|w| w.cnt().bits(cnt));
}
#[inline]
fn cnt(&self) -> u16 {
self.reg().[<ch $N cnt>]().read().cnt().bits()
}
#[inline]
fn set_enabled(&mut self, enabled: bool) {
self.reg().[<ch $N ctl>]().modify(|_, w| w.chen().bit(enabled));
}
#[inline]
fn is_complete(&self) -> bool {
self.reg().intf().read().[<ftfif $N>]().bit_is_set()
}
#[inline]
fn is_error(&self) -> bool {
self.reg().intf().read().[<errif $N>]().bit_is_set()
}
#[inline]
fn clear_flags(&mut self) {
self.reg().intc().write(|w| w.[<gifc $N>]().set_bit());
}
}
)+ }
};
}
channels!(0, 1, 2, 3, 4);
pub trait DmaWord {
const WIDTH: Width;
}
impl DmaWord for u8 {
const WIDTH: Width = Width::Bits8;
}
impl DmaWord for u16 {
const WIDTH: Width = Width::Bits16;
}
impl DmaWord for u32 {
const WIDTH: Width = Width::Bits32;
}
pub trait DmaPeriph<const N: u8> {
type Word: DmaWord;
fn addr(&self) -> u32;
fn enable_dma(&mut self);
fn disable_dma(&mut self);
}
pub trait DmaSrc<const N: u8>: DmaPeriph<N> {}
pub trait DmaDst<const N: u8>: DmaPeriph<N> {}
macro_rules! dma_map {
($($Trait:ident<$N:literal> for $Ty:ty, $W:ty, $Periph:ty, $reg:ident,
$ctl:ident, $den:ident, [$($gen:ident),*];)+) => {
$(
impl<$($gen),*> DmaPeriph<$N> for $Ty {
type Word = $W;
fn addr(&self) -> u32 {
unsafe { (*<$Periph>::ptr()).$reg().as_ptr() as u32 }
}
fn enable_dma(&mut self) {
unsafe { (*<$Periph>::ptr()).$ctl().modify(|_, w| w.$den().set_bit()) };
}
fn disable_dma(&mut self) {
unsafe { (*<$Periph>::ptr()).$ctl().modify(|_, w| w.$den().clear_bit()) };
}
}
impl<$($gen),*> $Trait<$N> for $Ty {}
)+
};
}
dma_map! {
DmaSrc<0> for Adc, u16, pac::Adc, rdata, ctl1, dma, [];
DmaSrc<1> for Spi<pac::Spi0, SCK, MISO, MOSI, spi::Byte>, u8, pac::Spi0, data, ctl1, dmaren, [SCK, MISO, MOSI];
DmaSrc<1> for Spi<pac::Spi0, SCK, MISO, MOSI, spi::Word>, u16, pac::Spi0, data, ctl1, dmaren, [SCK, MISO, MOSI];
DmaDst<1> for Usart<pac::Usart0, TX, RX, usart::Byte>, u8, pac::Usart0, tdata, ctl2, dent, [TX, RX];
DmaDst<1> for Usart<pac::Usart0, TX, RX, usart::Word>, u16, pac::Usart0, tdata, ctl2, dent, [TX, RX];
DmaDst<2> for Spi<pac::Spi0, SCK, MISO, MOSI, spi::Byte>, u8, pac::Spi0, data, ctl1, dmaten, [SCK, MISO, MOSI];
DmaDst<2> for Spi<pac::Spi0, SCK, MISO, MOSI, spi::Word>, u16, pac::Spi0, data, ctl1, dmaten, [SCK, MISO, MOSI];
DmaSrc<2> for Usart<pac::Usart0, TX, RX, usart::Byte>, u8, pac::Usart0, rdata, ctl2, denr, [TX, RX];
DmaSrc<2> for Usart<pac::Usart0, TX, RX, usart::Word>, u16, pac::Usart0, rdata, ctl2, denr, [TX, RX];
DmaSrc<3> for Spi<pac::Spi1, SCK, MISO, MOSI, spi::Byte>, u8, pac::Spi1, data, ctl1, dmaren, [SCK, MISO, MOSI];
DmaSrc<3> for Spi<pac::Spi1, SCK, MISO, MOSI, spi::Word>, u16, pac::Spi1, data, ctl1, dmaren, [SCK, MISO, MOSI];
DmaDst<3> for Usart<pac::Usart1, TX, RX, usart::Byte>, u8, pac::Usart1, tdata, ctl2, dent, [TX, RX];
DmaDst<3> for Usart<pac::Usart1, TX, RX, usart::Word>, u16, pac::Usart1, tdata, ctl2, dent, [TX, RX];
DmaDst<4> for Spi<pac::Spi1, SCK, MISO, MOSI, spi::Byte>, u8, pac::Spi1, data, ctl1, dmaten, [SCK, MISO, MOSI];
DmaDst<4> for Spi<pac::Spi1, SCK, MISO, MOSI, spi::Word>, u16, pac::Spi1, data, ctl1, dmaten, [SCK, MISO, MOSI];
DmaSrc<4> for Usart<pac::Usart1, TX, RX, usart::Byte>, u8, pac::Usart1, rdata, ctl2, denr, [TX, RX];
DmaSrc<4> for Usart<pac::Usart1, TX, RX, usart::Word>, u16, pac::Usart1, rdata, ctl2, denr, [TX, RX];
}
#[allow(private_bounds)]
impl<const N: u8> Channel<N>
where
Channel<N>: ChannelOps,
{
pub fn write_to<P: DmaDst<N>>(
mut self,
mut periph: P,
buf: &'static [P::Word],
prio: Prio,
) -> Transfer<N, P, &'static [P::Word]> {
self.set_maddr(buf.as_ptr() as u32);
self.set_paddr(periph.addr());
self.set_cnt(buf.len() as u16);
self.configure(Dir::M2P, <P::Word as DmaWord>::WIDTH, prio);
self.set_enabled(true);
periph.enable_dma();
Transfer {
channel: self,
periph,
buf,
}
}
pub fn read_from<P: DmaSrc<N>>(
mut self,
mut periph: P,
buf: &'static mut [P::Word],
prio: Prio,
) -> Transfer<N, P, &'static mut [P::Word]> {
self.set_maddr(buf.as_mut_ptr() as u32);
self.set_paddr(periph.addr());
self.set_cnt(buf.len() as u16);
self.configure(Dir::P2M, <P::Word as DmaWord>::WIDTH, prio);
self.set_enabled(true);
periph.enable_dma();
Transfer {
channel: self,
periph,
buf,
}
}
}
#[allow(private_bounds)]
#[must_use = "dropping a `Transfer` stops it at once; `wait` lets it finish"]
pub struct Transfer<const N: u8, P, BUF>
where
Channel<N>: ChannelOps,
P: DmaPeriph<N>,
{
channel: Channel<N>,
periph: P,
buf: BUF,
}
#[allow(private_bounds)]
impl<const N: u8, P, BUF> Transfer<N, P, BUF>
where
Channel<N>: ChannelOps,
P: DmaPeriph<N>,
{
pub fn wait(mut self) -> (Channel<N>, P, BUF) {
while !self.channel.is_complete() {}
self.periph.disable_dma();
self.channel.set_enabled(false);
self.channel.clear_flags();
let transfer = ManuallyDrop::new(self);
unsafe {
(
ptr::read(&raw const transfer.channel),
ptr::read(&raw const transfer.periph),
ptr::read(&raw const transfer.buf),
)
}
}
pub fn remaining(&self) -> u16 {
self.channel.cnt()
}
pub fn is_error(&self) -> bool {
self.channel.is_error()
}
}
#[allow(private_bounds)]
impl<const N: u8, P, BUF> Drop for Transfer<N, P, BUF>
where
Channel<N>: ChannelOps,
P: DmaPeriph<N>,
{
fn drop(&mut self) {
self.periph.disable_dma();
self.channel.set_enabled(false);
self.channel.clear_flags();
}
}
#[allow(missing_docs)]
pub struct Channels {
pub ch0: Channel<0>,
pub ch1: Channel<1>,
pub ch2: Channel<2>,
pub ch3: Channel<3>,
pub ch4: Channel<4>,
}
pub trait DmaExt {
type Channels;
fn split(self, rcu: &mut Rcu) -> Self::Channels;
}
impl DmaExt for pac::Dma {
type Channels = Channels;
fn split(self, rcu: &mut Rcu) -> Self::Channels {
<pac::Dma as Enable>::enable(rcu);
Channels {
ch0: Channel {
_marker: PhantomData,
},
ch1: Channel {
_marker: PhantomData,
},
ch2: Channel {
_marker: PhantomData,
},
ch3: Channel {
_marker: PhantomData,
},
ch4: Channel {
_marker: PhantomData,
},
}
}
}