#![cfg_attr(not(feature = "stm32l082"), allow(dead_code, unused_imports))]
use core::{
fmt,
mem,
ops::Deref,
pin::Pin,
sync::atomic::{
compiler_fence,
Ordering,
}
};
use as_slice::AsSlice;
use crate::{
i2c,
pac::{
self,
dma1::ch::cr,
I2C1,
I2C2,
I2C3,
USART1,
USART2,
},
rcc::Rcc,
serial,
};
#[cfg(feature = "stm32l082")]
use crate::aes;
pub struct DMA {
pub handle: Handle,
pub channels: Channels
}
impl DMA {
pub fn new(dma: pac::DMA1, rcc: &mut Rcc) -> Self {
rcc.rb.ahbrstr.modify(|_, w| w.dmarst().set_bit());
rcc.rb.ahbrstr.modify(|_, w| w.dmarst().clear_bit());
rcc.rb.ahbenr.modify(|_, w| w.dmaen().set_bit());
Self {
handle: Handle { dma },
channels: Channels::new(),
}
}
}
pub struct Handle {
dma: pac::DMA1,
}
pub struct Transfer<T, C, B, State> {
res: TransferResources<T, C, B>,
_state: State,
}
impl<T, C, B> Transfer<T, C, B, Ready>
where
T: Target<C>,
C: Channel,
{
pub(crate) unsafe fn new<Word>(
handle: &mut Handle,
target: T,
channel: C,
buffer: Pin<B>,
num_words: usize,
address: u32,
priority: Priority,
dir: Direction,
)
-> Self
where
B: Deref,
B::Target: Buffer<Word>,
Word: SupportedWordSize,
{
assert!(buffer.len() >= num_words);
assert!(num_words <= u16::max_value() as usize);
assert_eq!(buffer.as_ptr().align_offset(mem::size_of::<Word>()), 0);
channel.select_target(handle, &target);
channel.set_peripheral_address(handle, address);
channel.set_memory_address(handle, buffer.as_ptr() as u32);
channel.set_transfer_len(handle, num_words as u16);
channel.configure::<Word>(handle, priority.0, dir.0);
Transfer {
res: TransferResources {
target,
channel,
buffer,
},
_state: Ready,
}
}
pub fn enable_interrupts(&mut self, interrupts: Interrupts) {
self.res.channel.enable_interrupts(interrupts);
}
pub fn start(self) -> Transfer<T, C, B, Started> {
compiler_fence(Ordering::SeqCst);
self.res.channel.start();
Transfer {
res: self.res,
_state: Started,
}
}
}
impl<T, C, B> Transfer<T, C, B, Started>
where C: Channel
{
pub fn is_active(&self) -> bool {
self.res.channel.is_active()
}
pub fn wait(self)
-> Result<
TransferResources<T, C, B>,
(TransferResources<T, C, B>, Error)
>
{
while self.res.channel.is_active() {
if self.res.channel.error_occured() {
return Err((self.res, Error));
}
}
self.res.channel.clear_complete_flag();
compiler_fence(Ordering::SeqCst);
if self.res.channel.error_occured() {
return Err((self.res, Error));
}
Ok(self.res)
}
}
pub struct TransferResources<T, C, B> {
pub target: T,
pub channel: C,
pub buffer: Pin<B>,
}
impl<T, C, B> fmt::Debug for TransferResources<T, C, B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TransferResources {{ ... }}")
}
}
pub struct Priority(cr::PL_A);
impl Priority {
pub fn low() -> Self {
Self(cr::PL_A::LOW)
}
pub fn medium() -> Self {
Self(cr::PL_A::MEDIUM)
}
pub fn high() -> Self {
Self(cr::PL_A::HIGH)
}
pub fn very_high() -> Self {
Self(cr::PL_A::VERYHIGH)
}
}
pub(crate) struct Direction(cr::DIR_A);
impl Direction {
pub fn memory_to_peripheral() -> Self {
Self(cr::DIR_A::FROMMEMORY)
}
pub fn peripheral_to_memory() -> Self {
Self(cr::DIR_A::FROMPERIPHERAL)
}
}
#[derive(Debug)]
pub struct Error;
pub trait Channel: Sized {
fn select_target<T: Target<Self>>(&self, _: &mut Handle, target: &T);
fn set_peripheral_address(&self, _: &mut Handle, address: u32);
fn set_memory_address(&self, _: &mut Handle, address: u32);
fn set_transfer_len(&self, _: &mut Handle, len: u16);
fn configure<Word>(&self,
_: &mut Handle,
priority: cr::PL_A,
dir: cr::DIR_A,
)
where Word: SupportedWordSize;
fn enable_interrupts(&self, interrupts: Interrupts);
fn start(&self);
fn is_active(&self) -> bool;
fn clear_complete_flag(&self);
fn error_occured(&self) -> bool;
}
macro_rules! impl_channel {
(
$(
$channel:ident,
$field:ident,
$chfield:ident,
$cxs:ident,
$tcif:ident,
$teif:ident,
$ctcif:ident,
$cteif:ident;
)*
) => {
pub struct Channels {
$(pub $field: $channel,)*
}
impl Channels {
pub fn new() -> Self {
Self {
$($field: $channel(()),)*
}
}
}
$(
pub struct $channel(());
impl Channel for $channel {
fn select_target<T: Target<Self>>(&self,
handle: &mut Handle,
_target: &T,
) {
handle.dma.cselr.modify(|_, w| w.$cxs().bits(T::REQUEST));
}
fn set_peripheral_address(&self,
handle: &mut Handle,
address: u32,
) {
handle.dma.$chfield.par.write(|w| w.pa().bits(address));
}
fn set_memory_address(&self,
handle: &mut Handle,
address: u32,
) {
handle.dma.$chfield.mar.write(|w| w.ma().bits(address));
}
fn set_transfer_len(&self, handle: &mut Handle, len: u16) {
handle.dma.$chfield.ndtr.write(|w| w.ndt().bits(len));
}
fn configure<Word>(&self,
handle: &mut Handle,
priority: cr::PL_A,
dir: cr::DIR_A,
)
where Word: SupportedWordSize
{
handle.dma.$chfield.cr.write(|w| {
w
.msize().variant(Word::size())
.psize().variant(Word::size())
.mem2mem().disabled()
.pl().variant(priority)
.minc().enabled()
.pinc().disabled()
.circ().disabled()
.dir().variant(dir)
.teie().disabled()
.htie().disabled()
.tcie().disabled()
});
}
fn enable_interrupts(&self, interrupts: Interrupts) {
let ccr = &unsafe { &*pac::DMA1::ptr() }.$chfield.cr;
ccr.modify(|_, w|
w
.teie().bit(interrupts.transfer_error)
.htie().bit(interrupts.half_transfer)
.tcie().bit(interrupts.transfer_complete)
);
}
fn start(&self) {
let ccr = &unsafe { &*pac::DMA1::ptr() }.$chfield.cr;
ccr.modify(|_, w| w.en().enabled());
}
fn is_active(&self) -> bool {
let dma = unsafe { &*pac::DMA1::ptr() };
if dma.isr.read().$tcif().is_complete() {
false
}
else {
true
}
}
fn clear_complete_flag(&self) {
let dma = unsafe { &*pac::DMA1::ptr() };
if dma.isr.read().$tcif().is_complete() {
dma.ifcr.write(|w| w.$ctcif().set_bit());
dma.$chfield.cr.modify(|_, w| w.en().disabled());
}
}
fn error_occured(&self) -> bool {
let dma = unsafe { &*pac::DMA1::ptr() };
if dma.isr.read().$teif().is_error() {
dma.ifcr.write(|w| w.$cteif().set_bit());
true
}
else {
false
}
}
}
)*
}
}
impl_channel!(
Channel1, channel1, ch1,
c1s, tcif1, teif1, ctcif1, cteif1;
Channel2, channel2, ch2,
c2s, tcif2, teif2, ctcif2, cteif2;
Channel3, channel3, ch3,
c3s, tcif3, teif3, ctcif3, cteif3;
Channel4, channel4, ch4,
c4s, tcif4, teif4, ctcif4, cteif4;
Channel5, channel5, ch5,
c5s, tcif5, teif5, ctcif5, cteif5;
Channel6, channel6, ch6,
c6s, tcif6, teif6, ctcif6, cteif6;
Channel7, channel7, ch7,
c7s, tcif7, teif7, ctcif7, cteif7;
);
pub trait Target<Channel> {
const REQUEST: u8;
}
macro_rules! impl_target {
($($target:ty, $channel:ty, $request:expr;)*) => {
$(
impl Target<$channel> for $target {
const REQUEST: u8 = $request;
}
)*
}
}
impl_target!(
serial::Tx<USART1>, Channel2, 3;
serial::Tx<USART1>, Channel4, 3;
serial::Rx<USART1>, Channel3, 3;
serial::Rx<USART1>, Channel5, 3;
serial::Tx<USART2>, Channel4, 4;
serial::Tx<USART2>, Channel7, 4;
serial::Rx<USART2>, Channel5, 4;
serial::Rx<USART2>, Channel6, 4;
);
#[cfg(feature = "stm32l0x2")]
impl_target!(
i2c::Tx<I2C1>, Channel2, 6;
i2c::Rx<I2C1>, Channel3, 6;
i2c::Tx<I2C1>, Channel6, 6;
i2c::Rx<I2C1>, Channel7, 6;
i2c::Tx<I2C2>, Channel4, 7;
i2c::Rx<I2C2>, Channel5, 7;
i2c::Tx<I2C3>, Channel2, 14;
i2c::Rx<I2C3>, Channel3, 14;
i2c::Tx<I2C3>, Channel4, 14;
i2c::Rx<I2C3>, Channel5, 14;
);
#[cfg(feature = "stm32l082")]
impl_target!(
aes::Tx, Channel1, 11;
aes::Tx, Channel5, 11;
aes::Rx, Channel2, 11;
aes::Rx, Channel3, 11;
);
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> {
pub ptr: *const Word,
pub len: usize,
}
impl<Word> Deref for PtrBuffer<Word> {
type Target = Self;
fn deref(&self) -> &Self::Target {
self
}
}
impl<Word> Buffer<Word> for PtrBuffer<Word> {
fn as_ptr(&self) -> *const Word {
self.ptr
}
fn len(&self) -> usize {
self.len
}
}
pub trait SupportedWordSize {
fn size() -> cr::MSIZE_A;
}
impl SupportedWordSize for u8 {
fn size() -> cr::MSIZE_A {
cr::MSIZE_A::BITS8
}
}
impl SupportedWordSize for u16 {
fn size() -> cr::MSIZE_A {
cr::MSIZE_A::BITS16
}
}
impl SupportedWordSize for u32 {
fn size() -> cr::MSIZE_A {
cr::MSIZE_A::BITS32
}
}
#[derive(Clone, Copy)]
pub struct Interrupts {
pub transfer_error: bool,
pub half_transfer: bool,
pub transfer_complete: bool,
}
impl Default for Interrupts {
fn default() -> Self {
Self {
transfer_error: false,
half_transfer: false,
transfer_complete: false,
}
}
}