pub use embedded_dma::{ReadBuffer, WriteBuffer};
use crate::{
pac::{self, dma1::ch::cr},
rcc::AHB,
serial,
};
use core::{
convert::TryFrom,
mem,
sync::atomic::{self, Ordering},
};
#[cfg(feature = "enumset")]
use enumset::EnumSetType;
pub trait DmaExt {
type Channels;
fn split(self, ahb: &mut AHB) -> Self::Channels;
}
pub trait Target {
fn enable_dma(&mut self) {}
fn disable_dma(&mut self) {}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Transfer<B, C: Channel, T: Target> {
inner: Option<TransferInner<B, C, T>>,
}
impl<B, C: Channel, T: Target> Transfer<B, C, T> {
pub fn start_write(mut buffer: B, mut channel: C, target: T) -> Self
where
B: WriteBuffer + 'static,
T: OnChannel<C>,
{
let (ptr, len) = unsafe { buffer.write_buffer() };
let len = crate::expect!(u16::try_from(len).ok(), "buffer is too large");
unsafe { channel.set_memory_address(ptr as u32, Increment::Enable) };
channel.set_transfer_length(len);
channel.set_word_size::<B::Word>();
channel.set_direction(Direction::FromPeripheral);
unsafe { Self::start(buffer, channel, target) }
}
pub fn start_read(buffer: B, mut channel: C, target: T) -> Self
where
B: ReadBuffer + 'static,
T: OnChannel<C>,
{
let (ptr, len) = unsafe { buffer.read_buffer() };
let len = crate::expect!(u16::try_from(len).ok(), "buffer is too large");
unsafe { channel.set_memory_address(ptr as u32, Increment::Enable) };
channel.set_transfer_length(len);
channel.set_word_size::<B::Word>();
channel.set_direction(Direction::FromMemory);
unsafe { Self::start(buffer, channel, target) }
}
unsafe fn start(buffer: B, mut channel: C, mut target: T) -> Self
where
T: OnChannel<C>,
{
crate::assert!(!channel.is_enabled());
atomic::compiler_fence(Ordering::Release);
target.enable_dma();
channel.enable();
Self {
inner: Some(TransferInner {
buffer,
channel,
target,
}),
}
}
pub fn is_complete(&self) -> bool {
let inner = crate::unwrap!(self.inner.as_ref());
inner.channel.is_event_triggered(Event::TransferComplete)
}
pub fn stop(mut self) -> (B, C, T) {
let mut inner = crate::unwrap!(self.inner.take());
inner.stop();
(inner.buffer, inner.channel, inner.target)
}
pub fn wait(self) -> (B, C, T) {
while !self.is_complete() {}
self.stop()
}
}
impl<B, C: Channel, T: Target> Drop for Transfer<B, C, T> {
fn drop(&mut self) {
if let Some(inner) = self.inner.as_mut() {
inner.stop();
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct TransferInner<B, C, T> {
buffer: B,
channel: C,
target: T,
}
impl<B, C: Channel, T: Target> TransferInner<B, C, T> {
fn stop(&mut self) {
self.channel.disable();
self.target.disable_dma();
atomic::compiler_fence(Ordering::SeqCst);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Increment {
Enable,
Disable,
}
impl From<Increment> for cr::PINC_A {
fn from(inc: Increment) -> Self {
match inc {
Increment::Enable => cr::PINC_A::ENABLED,
Increment::Disable => cr::PINC_A::DISABLED,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Priority {
Low,
Medium,
High,
VeryHigh,
}
impl From<Priority> for cr::PL_A {
fn from(prio: Priority) -> Self {
match prio {
Priority::Low => cr::PL_A::LOW,
Priority::Medium => cr::PL_A::MEDIUM,
Priority::High => cr::PL_A::HIGH,
Priority::VeryHigh => cr::PL_A::VERYHIGH,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Direction {
FromMemory,
FromPeripheral,
}
impl From<Direction> for cr::DIR_A {
fn from(dir: Direction) -> Self {
match dir {
Direction::FromMemory => cr::DIR_A::FROMMEMORY,
Direction::FromPeripheral => cr::DIR_A::FROMPERIPHERAL,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "enumset", derive(EnumSetType))]
#[cfg_attr(not(feature = "enumset"), derive(Copy, Clone, PartialEq, Eq))]
pub enum Event {
HalfTransfer,
TransferComplete,
TransferError,
Any,
}
pub trait Channel: private::Channel {
fn is_event_triggered(&self, event: Event) -> bool;
fn clear_event(&mut self, event: Event);
fn clear_events(&mut self) {
self.clear_event(Event::Any);
}
fn reset(&mut self) {
self.ch().cr.reset();
self.ch().ndtr.reset();
self.ch().par.reset();
self.ch().mar.reset();
self.clear_event(Event::Any);
}
unsafe fn set_peripheral_address(&mut self, address: u32, inc: Increment) {
crate::assert!(!self.is_enabled());
self.ch().par.write(|w| w.pa().bits(address));
self.ch().cr.modify(|_, w| w.pinc().variant(inc.into()));
}
unsafe fn set_memory_address(&mut self, address: u32, inc: Increment) {
crate::assert!(!self.is_enabled());
self.ch().mar.write(|w| w.ma().bits(address));
self.ch().cr.modify(|_, w| w.minc().variant(inc.into()));
}
fn set_transfer_length(&mut self, len: u16) {
crate::assert!(!self.is_enabled());
self.ch().ndtr.write(|w| w.ndt().bits(len));
}
fn set_word_size<W>(&mut self) {
use cr::PSIZE_A::*;
let psize = match mem::size_of::<W>() {
1 => BITS8,
2 => BITS16,
4 => BITS32,
#[cfg(not(feature = "defmt"))]
s => core::panic!("unsupported word size: {:?}", s),
#[cfg(feature = "defmt")]
_ => defmt::panic!("unsupported word size"),
};
self.ch().cr.modify(|_, w| {
w.psize().variant(psize);
w.msize().variant(psize)
});
}
fn set_priority_level(&mut self, priority: Priority) {
let pl = priority.into();
self.ch().cr.modify(|_, w| w.pl().variant(pl));
}
fn set_direction(&mut self, direction: Direction) {
let dir = direction.into();
self.ch().cr.modify(|_, w| w.dir().variant(dir));
}
fn configure_intterupt(&mut self, event: Event, enable: bool) {
match event {
Event::HalfTransfer => self.ch().cr.modify(|_, w| w.htie().bit(enable)),
Event::TransferComplete => self.ch().cr.modify(|_, w| w.tcie().bit(enable)),
Event::TransferError => self.ch().cr.modify(|_, w| w.teie().bit(enable)),
Event::Any => self.ch().cr.modify(|_, w| {
w.htie().bit(enable);
w.tcie().bit(enable);
w.teie().bit(enable)
}),
}
}
fn enable_interrupt(&mut self, event: Event) {
self.configure_intterupt(event, true);
}
fn disable_interrupt(&mut self, event: Event) {
self.configure_intterupt(event, false);
}
fn enable(&mut self) {
self.clear_event(Event::Any);
self.ch().cr.modify(|_, w| w.en().enabled());
}
fn disable(&mut self) {
self.ch().cr.modify(|_, w| w.en().disabled());
}
fn is_enabled(&self) -> bool {
self.ch().cr.read().en().is_enabled()
}
}
mod private {
use crate::pac;
pub trait Channel {
fn ch(&self) -> &pac::dma1::CH;
}
}
macro_rules! dma {
(
$DMAx:ident, $dmax:ident, $dmaxen:ident,
channels: {
$( $Ci:ident: (
$chi:ident,
$htifi:ident, $tcifi:ident, $teifi:ident, $gifi:ident,
$chtifi:ident, $ctcifi:ident, $cteifi:ident, $cgifi:ident
), )+
},
) => {
paste::paste! {
#[doc = "All associated types, traits and methods of the `" $DMAx "` peripheral."]
pub mod $dmax {
use super::*;
use crate::pac::$DMAx;
use crate::rcc::Enable;
impl DmaExt for $DMAx {
type Channels = Channels;
fn split(self, ahb: &mut AHB) -> Channels {
<$DMAx>::enable(ahb);
let mut channels = Channels {
$( $chi: $Ci { _0: () }, )+
};
channels.reset();
channels
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Channels {
$(
pub $chi: $Ci,
)+
}
impl Channels {
fn reset(&mut self) {
$( self.$chi.reset(); )+
}
}
$(
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct $Ci {
_0: (),
}
impl private::Channel for $Ci {
fn ch(&self) -> &pac::dma1::CH {
unsafe { &(*$DMAx::ptr()).$chi }
}
}
impl Channel for $Ci {
fn is_event_triggered(&self, event: Event) -> bool {
use Event::*;
let flags = unsafe { (*$DMAx::ptr()).isr.read() };
match event {
HalfTransfer => flags.$htifi().bit_is_set(),
TransferComplete => flags.$tcifi().bit_is_set(),
TransferError => flags.$teifi().bit_is_set(),
Any => flags.$gifi().bit_is_set(),
}
}
fn clear_event(&mut self, event: Event) {
use Event::*;
unsafe {
(*$DMAx::ptr()).ifcr.write(|w| match event {
HalfTransfer => w.$chtifi().set_bit(),
TransferComplete => w.$ctcifi().set_bit(),
TransferError => w.$cteifi().set_bit(),
Any => w.$cgifi().set_bit(),
});
}
}
}
)+
}
}
};
( $X:literal: {$($C:literal),+} ) => {
paste::paste! {
dma!(
[<DMA $X>], [<dma $X>], [<dma $X en>],
channels: {
$(
[<C $C>]:
(
[<ch $C>],
[<htif $C>],
[<tcif $C>],
[<teif $C>],
[<gif $C>],
[<chtif $C>],
[<ctcif $C>],
[<cteif $C>],
[<cgif $C>]
),
)+
},
);
}
};
}
dma!( 1: { 1,2,3,4,5,6,7 } );
#[cfg(any(feature = "gpio-f303", feature = "gpio-f303e",))]
dma!( 2: { 1,2,3,4,5 } );
pub trait OnChannel<C: Channel>: Target + crate::private::Sealed {}
use crate::serial::{RxPin, TxPin};
macro_rules! on_channel {
(
$(
$dma:ident: [$(($USART:ty, ($TxChannel:ident, $RxChannel:ident)),)+],
),+
) => {
$(
$(
impl<Pin> crate::private::Sealed for serial::Tx<$USART, Pin> {}
impl<Pin> OnChannel<$dma::$TxChannel> for serial::Tx<$USART, Pin> where Pin: TxPin<$USART> {}
impl<Pin> crate::private::Sealed for serial::Rx<$USART, Pin> {}
impl<Pin> OnChannel<$dma::$RxChannel> for serial::Rx<$USART, Pin> where Pin: RxPin<$USART> {}
impl<Tx, Rx> crate::private::Sealed for serial::Serial<$USART, (Tx, Rx)> {}
impl<Tx, Rx> OnChannel<$dma::$TxChannel> for serial::Serial<$USART, (Tx, Rx)> {}
impl<Tx, Rx> OnChannel<$dma::$RxChannel> for serial::Serial<$USART, (Tx, Rx)> {}
)+
)+
};
}
on_channel!(
dma1: [
(pac::USART1, (C4, C5)),
(pac::USART2, (C7, C6)),
(pac::USART3, (C2, C3)),
],
);
#[cfg(any(feature = "gpio-f303", feature = "gpio-f303e",))]
on_channel!(
dma2: [
(pac::UART4, (C5, C3)),
],
);