diff --git a/embassy-stm32/src/bdma/mod.rs b/embassy-stm32/src/bdma/mod.rs index e85ade5df..39e8bec3b 100644 --- a/embassy-stm32/src/bdma/mod.rs +++ b/embassy-stm32/src/bdma/mod.rs @@ -1,69 +1,413 @@ #![macro_use] -#[cfg_attr(bdma_v1, path = "v1.rs")] -#[cfg_attr(bdma_v2, path = "v2.rs")] -mod _version; +use core::future::Future; +use core::task::Poll; -#[allow(unused)] -pub use _version::*; +use atomic_polyfill::{AtomicU8, Ordering}; +use embassy::interrupt::{Interrupt, InterruptExt}; +use embassy::util::{AtomicWaker, OnDrop}; +use futures::future::poll_fn; +use crate::dma_traits::{ReadDma, WriteDma}; +use crate::interrupt; use crate::pac; -use crate::peripherals; +use crate::pac::bdma::vals; -pub(crate) mod sealed { - use super::*; +const CH_COUNT: usize = pac::peripheral_count!(bdma) * 8; +const CH_STATUS_NONE: u8 = 0; +const CH_STATUS_COMPLETED: u8 = 1; +const CH_STATUS_ERROR: u8 = 2; - pub trait Channel { - fn num(&self) -> u8; +struct State { + ch_wakers: [AtomicWaker; CH_COUNT], + ch_status: [AtomicU8; CH_COUNT], +} - fn dma_num(&self) -> u8 { - self.num() / 8 - } - fn ch_num(&self) -> u8 { - self.num() % 8 - } - fn regs(&self) -> pac::dma::Dma { - pac::DMA(self.num() as _) +impl State { + const fn new() -> Self { + const AW: AtomicWaker = AtomicWaker::new(); + const AU: AtomicU8 = AtomicU8::new(CH_STATUS_NONE); + Self { + ch_wakers: [AW; CH_COUNT], + ch_status: [AU; CH_COUNT], } } } +static STATE: State = State::new(); + +#[allow(unused)] +pub(crate) async unsafe fn transfer_p2m( + regs: pac::bdma::Ch, + state_number: u8, + src: *const u8, + dst: &mut [u8], + #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, + #[cfg(dmamux)] dmamux_ch_num: u8, + #[cfg(dmamux)] request: u8, +) { + // ndtr is max 16 bits. + assert!(dst.len() <= 0xFFFF); + + // Reset status + // Generate a DMB here to flush the store buffer (M7) before enabling the DMA + STATE.ch_status[state_number as usize].store(CH_STATUS_NONE, Ordering::Release); + + let on_drop = OnDrop::new(|| unsafe { + regs.cr().modify(|w| { + w.set_tcie(false); + w.set_teie(false); + w.set_en(false); + }); + while regs.cr().read().en() {} + }); + + #[cfg(dmamux)] + crate::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + + regs.par().write_value(src as u32); + regs.mar().write_value(dst.as_mut_ptr() as u32); + regs.ndtr().write(|w| w.set_ndt(dst.len() as u16)); + regs.cr().write(|w| { + w.set_psize(vals::Size::BITS8); + w.set_msize(vals::Size::BITS8); + w.set_minc(vals::Inc::ENABLED); + w.set_teie(true); + w.set_tcie(true); + w.set_en(true); + }); + + let res = poll_fn(|cx| { + STATE.ch_wakers[state_number as usize].register(cx.waker()); + match STATE.ch_status[state_number as usize].load(Ordering::Acquire) { + CH_STATUS_NONE => Poll::Pending, + x => Poll::Ready(x), + } + }) + .await; + + // TODO handle error + assert!(res == CH_STATUS_COMPLETED); +} + +#[allow(unused)] +pub(crate) async unsafe fn transfer_m2p( + regs: pac::bdma::Ch, + state_number: u8, + src: &[u8], + dst: *mut u8, + #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, + #[cfg(dmamux)] dmamux_ch_num: u8, + #[cfg(dmamux)] request: u8, +) { + // ndtr is max 16 bits. + assert!(src.len() <= 0xFFFF); + + // Reset status + // Generate a DMB here to flush the store buffer (M7) before enabling the DMA + STATE.ch_status[state_number as usize].store(CH_STATUS_NONE, Ordering::Release); + + let on_drop = OnDrop::new(|| unsafe { + regs.cr().modify(|w| { + w.set_tcie(false); + w.set_teie(false); + w.set_en(false); + }); + while regs.cr().read().en() {} + }); + + #[cfg(dmamux)] + crate::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + + regs.par().write_value(dst as u32); + regs.mar().write_value(src.as_ptr() as u32); + regs.ndtr().write(|w| w.set_ndt(src.len() as u16)); + regs.cr().write(|w| { + w.set_psize(vals::Size::BITS8); + w.set_msize(vals::Size::BITS8); + w.set_minc(vals::Inc::ENABLED); + w.set_dir(vals::Dir::FROMMEMORY); + w.set_teie(true); + w.set_tcie(true); + w.set_en(true); + }); + + let res = poll_fn(|cx| { + STATE.ch_wakers[state_number as usize].register(cx.waker()); + match STATE.ch_status[state_number as usize].load(Ordering::Acquire) { + CH_STATUS_NONE => Poll::Pending, + x => Poll::Ready(x), + } + }) + .await; + + // TODO handle error + assert!(res == CH_STATUS_COMPLETED); +} + +unsafe fn on_irq() { + pac::peripherals! { + (bdma, $dma:ident) => { + let isr = pac::$dma.isr().read(); + pac::$dma.ifcr().write_value(isr); + let dman = ::NUM as usize; + + for chn in 0..crate::pac::dma_channels_count!($dma) { + let n = dman * 8 + chn; + if isr.teif(chn) { + STATE.ch_status[n].store(CH_STATUS_ERROR, Ordering::Relaxed); + STATE.ch_wakers[n].wake(); + } else if isr.tcif(chn) { + STATE.ch_status[n].store(CH_STATUS_COMPLETED, Ordering::Relaxed); + STATE.ch_wakers[n].wake(); + } + } + }; + } +} + +use crate::rcc::sealed::RccPeripheral; + +/// safety: must be called only once +pub(crate) unsafe fn init() { + pac::interrupts! { + (DMA, $irq:ident) => { + crate::interrupt::$irq::steal().enable(); + }; + } + pac::peripherals! { + (bdma, $peri:ident) => { + crate::peripherals::$peri::enable(); + }; + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Dma { + const NUM: u8; + } + + pub trait Channel { + const CH_NUM: u8; + const STATE_NUM: u8; + const DMA_REGS: pac::bdma::Dma; + + fn regs(&self) -> pac::bdma::Ch { + Self::DMA_REGS.ch(Self::CH_NUM as usize) + } + } +} + +pub trait Dma: sealed::Dma + Sized {} pub trait Channel: sealed::Channel + Sized {} +macro_rules! impl_dma { + ($peri:ident) => { + impl Dma for crate::peripherals::$peri {} + impl sealed::Dma for crate::peripherals::$peri { + const NUM: u8 = dma_num!($peri); + } + }; +} + macro_rules! impl_dma_channel { - ($channel_peri:ident, $dma_num:expr, $ch_num:expr) => { - impl Channel for peripherals::$channel_peri {} - impl sealed::Channel for peripherals::$channel_peri { - #[inline] - fn num(&self) -> u8 { - $dma_num * 8 + $ch_num + ($channel_peri:ident, $dma_peri:ident, $ch_num:expr) => { + impl Channel for crate::peripherals::$channel_peri {} + impl sealed::Channel for crate::peripherals::$channel_peri { + const CH_NUM: u8 = $ch_num; + const STATE_NUM: u8 = (dma_num!($dma_peri) * 8) + $ch_num; + const DMA_REGS: pac::bdma::Dma = crate::pac::$dma_peri; + + //#[inline] + //fn dma_regs() -> pac::bdma::Dma { + //crate::pac::$dma_peri + //} + + //fn state_num(&self) -> usize { + //(dma_num!($dma_peri) * 8) + $ch_num + //} + } + + #[cfg(not(dmamux))] + impl WriteDma for crate::peripherals::$channel_peri + where + T: 'static, + { + type WriteDmaFuture<'a> = impl Future; + + fn transfer<'a>(&'a mut self, buf: &'a [u8], dst: *mut u8) -> Self::WriteDmaFuture<'a> + where + T: 'a, + { + use sealed::Channel as _Channel; + + let state_num = Self::STATE_NUM; + let regs = self.regs(); + + unsafe { transfer_m2p(regs, state_num, buf, dst) } + } + } + + #[cfg(dmamux)] + impl WriteDma for crate::peripherals::$channel_peri + where + Self: crate::dmamux::sealed::PeripheralChannel, + T: 'static, + { + type WriteDmaFuture<'a> = impl Future; + + fn transfer<'a>(&'a mut self, buf: &'a [u8], dst: *mut u8) -> Self::WriteDmaFuture<'a> + where + T: 'a, + { + use sealed::Channel as _Channel; + + let state_num = Self::STATE_NUM; + let regs = self.regs(); + + use crate::dmamux::sealed::Channel as MuxChannel; + use crate::dmamux::sealed::PeripheralChannel; + let dmamux_regs = ::DMAMUX_REGS; + let dmamux_ch_num = + ::DMAMUX_CH_NUM; + let request = >::REQUEST; + unsafe { + transfer_m2p( + regs, + state_num, + buf, + dst, + dmamux_regs, + dmamux_ch_num, + request, + ) + } + } + } + + #[cfg(not(dmamux))] + impl ReadDma for crate::peripherals::$channel_peri + where + T: 'static, + { + type ReadDmaFuture<'a> = impl Future; + + fn transfer<'a>( + &'a mut self, + src: *const u8, + buf: &'a mut [u8], + ) -> Self::ReadDmaFuture<'a> + where + T: 'a, + { + use sealed::Channel as _Channel; + + let state_num = Self::STATE_NUM; + let regs = self.regs(); + unsafe { transfer_p2m(regs, state_num, src, buf) } + } + } + + #[cfg(dmamux)] + impl ReadDma for crate::peripherals::$channel_peri + where + Self: crate::dmamux::sealed::PeripheralChannel, + T: 'static, + { + type ReadDmaFuture<'a> = impl Future; + + fn transfer<'a>( + &'a mut self, + src: *const u8, + buf: &'a mut [u8], + ) -> Self::ReadDmaFuture<'a> + where + T: 'a, + { + use sealed::Channel as _Channel; + + let state_num = Self::STATE_NUM; + let regs = self.regs(); + + use crate::dmamux::sealed::Channel as MuxChannel; + use crate::dmamux::sealed::PeripheralChannel; + let dmamux_regs = ::DMAMUX_REGS; + let dmamux_ch_num = + ::DMAMUX_CH_NUM; + let request = >::REQUEST; + unsafe { + transfer_p2m( + regs, + state_num, + src, + buf, + dmamux_regs, + dmamux_ch_num, + request, + ) + } } } }; } -/* -crate::pac::peripherals!( - (dma,DMA1) => { - impl_dma_channel!(DMA1_CH0, 0, 0); - impl_dma_channel!(DMA1_CH1, 0, 1); - impl_dma_channel!(DMA1_CH2, 0, 2); - impl_dma_channel!(DMA1_CH3, 0, 3); - impl_dma_channel!(DMA1_CH4, 0, 4); - impl_dma_channel!(DMA1_CH5, 0, 5); - impl_dma_channel!(DMA1_CH6, 0, 6); - impl_dma_channel!(DMA1_CH7, 0, 7); +macro_rules! dma_num { + (DMA1) => { + 0 + }; + (DMA2) => { + 1 + }; + (BDMA) => { + 0 + }; +} +pac::peripherals! { + (bdma, $peri:ident) => { + impl_dma!($peri); + }; +} + +pac::bdma_channels! { + ($channel_peri:ident, $dma_peri:ident, $channel_num:expr) => { + impl_dma_channel!($channel_peri, $dma_peri, $channel_num); + }; +} + +pac::interrupts! { + (DMA, $irq:ident) => { + #[crate::interrupt] + unsafe fn $irq () { + on_irq() + } + }; +} + +#[cfg(usart)] +use crate::usart; + +pac::peripherals! { + (usart, $peri:ident) => { + impl> usart::TxDma for T {} + impl> usart::sealed::TxDma for T {} + + impl> usart::RxDma for T {} + impl> usart::sealed::RxDma for T {} }; - (dma,DMA2) => { - impl_dma_channel!(DMA2_CH0, 1, 0); - impl_dma_channel!(DMA2_CH1, 1, 1); - impl_dma_channel!(DMA2_CH2, 1, 2); - impl_dma_channel!(DMA2_CH3, 1, 3); - impl_dma_channel!(DMA2_CH4, 1, 4); - impl_dma_channel!(DMA2_CH5, 1, 5); - impl_dma_channel!(DMA2_CH6, 1, 6); - impl_dma_channel!(DMA2_CH7, 1, 7); + (uart, $peri:ident) => { + impl> usart::TxDma for T {} + impl> usart::sealed::TxDma for T {} + + impl> usart::RxDma for T {} + impl> usart::sealed::RxDma for T {} }; -); - */ +} diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 466cfa033..ed080cd16 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -1,6 +1,5 @@ #![macro_use] -#[cfg(dma)] #[cfg_attr(dma_v1, path = "v1.rs")] #[cfg_attr(dma_v2, path = "v2.rs")] mod _version; @@ -8,25 +7,3 @@ mod _version; #[cfg(dma)] #[allow(unused)] pub use _version::*; - -use core::future::Future; - -pub trait WriteDma { - type WriteDmaFuture<'a>: Future + 'a - where - Self: 'a; - - fn transfer<'a>(&'a mut self, buf: &'a [u8], dst: *mut u8) -> Self::WriteDmaFuture<'a> - where - T: 'a; -} - -pub trait ReadDma { - type ReadDmaFuture<'a>: Future + 'a - where - Self: 'a; - - fn transfer<'a>(&'a mut self, src: *const u8, buf: &'a mut [u8]) -> Self::ReadDmaFuture<'a> - where - T: 'a; -} diff --git a/embassy-stm32/src/dma/v2.rs b/embassy-stm32/src/dma/v2.rs index e7cd24710..df3b922ff 100644 --- a/embassy-stm32/src/dma/v2.rs +++ b/embassy-stm32/src/dma/v2.rs @@ -1,11 +1,12 @@ use core::task::Poll; +use crate::dma_traits::{ReadDma, WriteDma}; use atomic_polyfill::{AtomicU8, Ordering}; +use core::future::Future; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::util::AtomicWaker; use futures::future::poll_fn; -use super::*; use crate::interrupt; use crate::pac; use crate::pac::dma::{regs, vals}; diff --git a/embassy-stm32/src/dma_traits.rs b/embassy-stm32/src/dma_traits.rs new file mode 100644 index 000000000..8f1a9f40e --- /dev/null +++ b/embassy-stm32/src/dma_traits.rs @@ -0,0 +1,21 @@ +use core::future::Future; + +pub trait WriteDma { + type WriteDmaFuture<'a>: Future + 'a + where + Self: 'a; + + fn transfer<'a>(&'a mut self, buf: &'a [u8], dst: *mut u8) -> Self::WriteDmaFuture<'a> + where + T: 'a; +} + +pub trait ReadDma { + type ReadDmaFuture<'a>: Future + 'a + where + Self: 'a; + + fn transfer<'a>(&'a mut self, src: *const u8, buf: &'a mut [u8]) -> Self::ReadDmaFuture<'a> + where + T: 'a; +} diff --git a/embassy-stm32/src/dmamux/mod.rs b/embassy-stm32/src/dmamux/mod.rs new file mode 100644 index 000000000..ecea0b290 --- /dev/null +++ b/embassy-stm32/src/dmamux/mod.rs @@ -0,0 +1,137 @@ +#![macro_use] + +use crate::pac; +use crate::pac::bdma_channels; +use crate::pac::dma_requests; +use crate::pac::peripherals; +use crate::peripherals; + +pub(crate) unsafe fn configure_dmamux( + dmamux_regs: pac::dmamux::Dmamux, + dmamux_ch_num: u8, + request: u8, +) { + let ch_mux_regs = dmamux_regs.ccr(dmamux_ch_num as _); + ch_mux_regs.write(|reg| { + reg.set_nbreq(0); + reg.set_dmareq_id(request); + }); + + ch_mux_regs.modify(|reg| { + reg.set_ege(true); + }); +} + +pub(crate) mod sealed { + use super::*; + + pub trait Channel { + const DMAMUX_CH_NUM: u8; + const DMAMUX_REGS: pac::dmamux::Dmamux; + } + + pub trait PeripheralChannel: Channel { + const REQUEST: u8; + } +} + +pub trait Channel: sealed::Channel {} +pub trait PeripheralChannel: sealed::Channel {} + +pub struct P2M; +pub struct M2P; + +macro_rules! dma_num { + (DMA1) => { + 0 + }; + (DMA2) => { + 1 + }; + (BDMA) => { + 0 + }; +} + +macro_rules! dmamux_peri { + (DMA1) => { + crate::pac::DMAMUX1 + }; + (DMA2) => { + crate::pac::DMAMUX1 + }; + (BDMA) => { + crate::pac::DMAMUX1 + }; +} + +#[allow(unused)] +macro_rules! impl_dma_channel { + ($channel_peri:ident, $channel_num:expr, $dma_peri: ident) => { + impl Channel for peripherals::$channel_peri {} + impl sealed::Channel for peripherals::$channel_peri { + const DMAMUX_CH_NUM: u8 = (dma_num!($dma_peri) * 8) + $channel_num; + const DMAMUX_REGS: pac::dmamux::Dmamux = dmamux_peri!($dma_peri); + } + }; +} + +peripherals! { + (bdma, $peri:ident) => { + bdma_channels! { + ($channel_peri:ident, $peri, $channel_num:expr) => { + impl_dma_channel!($channel_peri, $channel_num, $peri); + }; + } + }; +} + +#[allow(unused)] +macro_rules! impl_peripheral_channel { + ($channel_peri:ident, $direction:ident, $peri:ident, $request:expr) => { + impl sealed::PeripheralChannel + for peripherals::$channel_peri + { + const REQUEST: u8 = $request; + } + + impl PeripheralChannel for peripherals::$channel_peri {} + }; +} + +#[allow(unused)] +macro_rules! impl_usart_dma_requests { + ($channel_peri:ident, $dma_peri:ident, $channel_num:expr) => { + dma_requests! { + (usart, $peri:ident, RX, $request:expr) => { + impl_peripheral_channel!($channel_peri, P2M, $peri, $request); + }; + + (usart, $peri:ident, TX, $request:expr) => { + impl_peripheral_channel!($channel_peri, M2P, $peri, $request); + }; + + (uart, $peri:ident, RX, $request:expr) => { + impl_peripheral_channel!($channel_peri, P2M, $peri, $request); + }; + + (uart, $peri:ident, TX, $request:expr) => { + impl_peripheral_channel!($channel_peri, M2P, $peri, $request); + }; + } + }; +} + +#[allow(unused)] +#[cfg(usart)] +use crate::usart; + +bdma_channels! { + ($channel_peri:ident, $dma_peri:ident, $channel_num:expr) => { + #[cfg(usart)] + impl_usart_dma_requests!($channel_peri, $dma_peri, $channel_num); + }; +} + +/// safety: must be called only once +pub(crate) unsafe fn init() {} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 76a6ecd88..4b2826ae8 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -20,14 +20,21 @@ pub mod gpio; pub mod rcc; // Sometimes-present hardware +#[cfg(any(dma, bdma, dmamux))] +pub mod dma_traits; + #[cfg(adc)] pub mod adc; +#[cfg(bdma)] +pub mod bdma; #[cfg(timer)] pub mod clock; #[cfg(dac)] pub mod dac; -#[cfg(any(dma, dmamux))] +#[cfg(dma)] pub mod dma; +#[cfg(dmamux)] +pub mod dmamux; #[cfg(all(eth, feature = "net"))] pub mod eth; #[cfg(exti)] @@ -86,8 +93,13 @@ pub fn init(config: Config) -> Peripherals { unsafe { #[cfg(dma)] dma::init(); + #[cfg(bdma)] + bdma::init(); + #[cfg(dmamux)] + dmamux::init(); #[cfg(exti)] exti::init(); + rcc::init(config.rcc); } diff --git a/embassy-stm32/src/rcc/h7/mod.rs b/embassy-stm32/src/rcc/h7/mod.rs index 309902335..5ec531820 100644 --- a/embassy-stm32/src/rcc/h7/mod.rs +++ b/embassy-stm32/src/rcc/h7/mod.rs @@ -532,6 +532,7 @@ pub unsafe fn init(config: Config) { ahb1: core_clocks.hclk, ahb2: core_clocks.hclk, ahb3: core_clocks.hclk, + ahb4: core_clocks.hclk, apb1: core_clocks.pclk1, apb2: core_clocks.pclk2, apb4: core_clocks.pclk4, diff --git a/embassy-stm32/src/rcc/l0/mod.rs b/embassy-stm32/src/rcc/l0/mod.rs index 0b11e708f..6107d5f55 100644 --- a/embassy-stm32/src/rcc/l0/mod.rs +++ b/embassy-stm32/src/rcc/l0/mod.rs @@ -170,7 +170,7 @@ impl<'d> Rcc<'d> { pub fn enable_debug_wfe(&mut self, _dbg: &mut peripherals::DBGMCU, enable_dma: bool) { // NOTE(unsafe) We have exclusive access to the RCC and DBGMCU unsafe { - pac::RCC.ahbenr().modify(|w| w.set_dmaen(enable_dma)); + pac::RCC.ahbenr().modify(|w| w.set_dma1en(enable_dma)); pac::DBGMCU.cr().modify(|w| { w.set_dbg_sleep(true); diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c7d1ae615..b7b692f15 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -26,6 +26,9 @@ pub struct Clocks { #[cfg(any(rcc_l4, rcc_f4, rcc_h7, rcc_wb55, rcc_wl5x))] pub ahb3: Hertz, + #[cfg(any(rcc_h7))] + pub ahb4: Hertz, + #[cfg(any(rcc_h7))] pub apb4: Hertz, } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 2fa758ecc..ddaed5bb9 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -8,7 +8,6 @@ use crate::peripherals; pub use _version::*; use crate::gpio::Pin; -use crate::pac::usart::Usart; use crate::rcc::RccPeripheral; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -58,6 +57,7 @@ impl Default for Config { /// Serial error #[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { /// Framing error @@ -73,11 +73,11 @@ pub enum Error { pub(crate) mod sealed { use super::*; - #[cfg(any(dma, dmamux))] - use crate::dma::WriteDma; + #[cfg(any(dma, bdma, dmamux))] + use crate::dma_traits::WriteDma; pub trait Instance { - fn regs(&self) -> Usart; + fn regs(&self) -> crate::pac::usart::Usart; } pub trait RxPin: Pin { fn af_num(&self) -> u8; @@ -95,10 +95,10 @@ pub(crate) mod sealed { fn af_num(&self) -> u8; } - #[cfg(any(dma, dmamux))] + #[cfg(any(bdma, dma, dmamux))] pub trait RxDma {} - #[cfg(any(dma, dmamux))] + #[cfg(any(bdma, dma, dmamux))] pub trait TxDma: WriteDma {} } @@ -109,10 +109,10 @@ pub trait CtsPin: sealed::CtsPin {} pub trait RtsPin: sealed::RtsPin {} pub trait CkPin: sealed::CkPin {} -#[cfg(any(dma, dmamux))] +#[cfg(any(bdma, dma, dmamux))] pub trait RxDma: sealed::RxDma {} -#[cfg(any(dma, dmamux))] +#[cfg(any(bdma, dma, dmamux))] pub trait TxDma: sealed::TxDma {} crate::pac::peripherals!( diff --git a/embassy-stm32/src/usart/v2.rs b/embassy-stm32/src/usart/v2.rs index 8b1378917..22041b4aa 100644 --- a/embassy-stm32/src/usart/v2.rs +++ b/embassy-stm32/src/usart/v2.rs @@ -1 +1,119 @@ +use core::marker::PhantomData; +use embassy::util::Unborrow; +use embassy_extras::unborrow; + +use crate::pac::usart::vals; + +use super::*; + +pub struct Uart<'d, T: Instance> { + inner: T, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Uart<'d, T> { + pub fn new( + inner: impl Unborrow, + rx: impl Unborrow>, + tx: impl Unborrow>, + config: Config, + ) -> Self { + unborrow!(inner, rx, tx); + + T::enable(); + let pclk_freq = T::frequency(); + + // TODO: better calculation, including error checking and OVER8 if possible. + let div = pclk_freq.0 / config.baudrate; + + let r = inner.regs(); + + unsafe { + rx.set_as_af(rx.af_num()); + tx.set_as_af(tx.af_num()); + + r.cr2().write(|_w| {}); + r.cr3().write(|_w| {}); + + r.brr().write(|w| w.set_brr(div as u16)); + r.cr1().write(|w| { + w.set_ue(true); + w.set_te(true); + w.set_re(true); + w.set_m0(vals::M0::BIT8); + w.set_m1(vals::M1::M0); + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + Self { + inner, + phantom: PhantomData, + } + } + + #[cfg(bdma)] + pub async fn write_dma(&mut self, ch: &mut impl TxDma, buffer: &[u8]) -> Result<(), Error> { + unsafe { + self.inner.regs().cr3().modify(|reg| { + reg.set_dmat(true); + }); + } + let r = self.inner.regs(); + let dst = r.tdr().ptr() as *mut u8; + ch.transfer(buffer, dst).await; + Ok(()) + } + + pub fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + unsafe { + let r = self.inner.regs(); + for b in buffer { + loop { + let sr = r.isr().read(); + if sr.pe() { + r.rdr().read(); + return Err(Error::Parity); + } else if sr.fe() { + r.rdr().read(); + return Err(Error::Framing); + } else if sr.ore() { + r.rdr().read(); + return Err(Error::Overrun); + } else if sr.rxne() { + break; + } + } + *b = r.rdr().read().0 as u8; + } + } + Ok(()) + } +} + +impl<'d, T: Instance> embedded_hal::blocking::serial::Write for Uart<'d, T> { + type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + unsafe { + let r = self.inner.regs(); + for &b in buffer { + while !r.isr().read().txe() {} + r.tdr().write(|w| w.set_dr(b as u16)); + } + } + Ok(()) + } + fn bflush(&mut self) -> Result<(), Self::Error> { + unsafe { + let r = self.inner.regs(); + while !r.isr().read().tc() {} + } + Ok(()) + } +} diff --git a/embassy-stm32/src/usart/v3.rs b/embassy-stm32/src/usart/v3.rs index 1e9051443..0071c597a 100644 --- a/embassy-stm32/src/usart/v3.rs +++ b/embassy-stm32/src/usart/v3.rs @@ -57,7 +57,7 @@ impl<'d, T: Instance> Uart<'d, T> { } } - #[cfg(dma)] + #[cfg(any(dma, dmamux))] pub async fn write_dma(&mut self, ch: &mut impl TxDma, buffer: &[u8]) -> Result<(), Error> { unsafe { self.inner.regs().cr3().modify(|reg| { diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 5b6d9eaa1..0b14eeb59 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -82,10 +82,6 @@ fn main() -> ! { w.syscfgen().set_bit(); w }); - //pp.RCC.apb1enr.modify(|_, w| { - //w.usart3en().enabled(); - //w - //}); unsafe { embassy::time::set_clock(&ZeroClock) }; diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs new file mode 100644 index 000000000..cc630e0df --- /dev/null +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -0,0 +1,96 @@ +#![no_std] +#![no_main] +#![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; +use core::fmt::Write; +use cortex_m_rt::entry; +use embassy::executor::Executor; +use embassy::time::Clock; +use embassy::util::Forever; +use embassy_stm32::usart::{Config, Uart}; +use example_common::*; +use heapless::String; +use stm32l4::stm32l4x5 as pac; + +#[embassy::task] +async fn main_task() { + let mut p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, config); + + for n in 0u32.. { + let mut s: String<128> = String::new(); + core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap(); + + usart + .write_dma(&mut p.DMA1_3, s.as_bytes()) + .await + .unwrap(); + info!("wrote DMA"); + } +} + +struct ZeroClock; + +impl Clock for ZeroClock { + fn now(&self) -> u64 { + 0 + } +} + +static EXECUTOR: Forever = Forever::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let pp = pac::Peripherals::take().unwrap(); + + pp.DBGMCU.cr.modify(|_, w| { + w.dbg_sleep().set_bit(); + w.dbg_standby().set_bit(); + w.dbg_stop().set_bit() + }); + + pp.RCC.ahb1enr.modify(|_, w| { + unsafe { + w.bits( 0x07 ); + } + w + //w.dmamuxen().set_bit(); + //w.dma1en().set_bit(); + //w.dma2en().set_bit(); + //w + }); + + pp.RCC.ahb2enr.modify(|_, w| { + w.gpioaen().set_bit(); + w.gpioben().set_bit(); + w.gpiocen().set_bit(); + w.gpioden().set_bit(); + w.gpioeen().set_bit(); + w.gpiofen().set_bit(); + w + }); + + pp.RCC.apb2enr.modify(|_, w| { + w.syscfgen().set_bit(); + w + }); + + + unsafe { embassy::time::set_clock(&ZeroClock) }; + + let executor = EXECUTOR.put(Executor::new()); + + executor.run(|spawner| { + unwrap!(spawner.spawn(main_task())); + }) +} diff --git a/stm32-data b/stm32-data index 409ed5502..bc74a9801 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 409ed5502c254e462f3e31b0ea5ddee95f818a70 +Subproject commit bc74a98017800aad50dd1448a24e3f54eaec8eb4 diff --git a/stm32-metapac-gen/src/lib.rs b/stm32-metapac-gen/src/lib.rs index 978f70b92..37254f6f0 100644 --- a/stm32-metapac-gen/src/lib.rs +++ b/stm32-metapac-gen/src/lib.rs @@ -141,6 +141,21 @@ macro_rules! peripheral_count {{ write!(out, " }}\n").unwrap(); } +fn make_dma_channel_counts(out: &mut String, data: &HashMap) { + write!(out, + "#[macro_export] +macro_rules! dma_channels_count {{ + ").unwrap(); + for (name, count) in data { + write!(out, + "({}) => ({});\n", + name, count, + ).unwrap(); + } + write!(out, + " }}\n").unwrap(); +} + fn make_table(out: &mut String, name: &str, data: &Vec>) { write!( out, @@ -252,9 +267,11 @@ pub fn gen(options: Options) { let mut peripheral_pins_table: Vec> = Vec::new(); let mut peripheral_rcc_table: Vec> = Vec::new(); let mut dma_channels_table: Vec> = Vec::new(); + let mut bdma_channels_table: Vec> = Vec::new(); let mut dma_requests_table: Vec> = Vec::new(); let mut peripheral_dma_channels_table: Vec> = Vec::new(); let mut peripheral_counts: HashMap = HashMap::new(); + let mut dma_channel_counts: HashMap = HashMap::new(); let dma_base = core .peripherals @@ -266,14 +283,6 @@ pub fn gen(options: Options) { let gpio_base = core.peripherals.get(&"GPIOA".to_string()).unwrap().address; let gpio_stride = 0x400; - for (id, channel_info) in &core.dma_channels { - let mut row = Vec::new(); - row.push(id.clone()); - row.push(channel_info.dma.clone()); - row.push(channel_info.channel.to_string()); - dma_channels_table.push(row); - } - let number_suffix_re = Regex::new("^(.*?)[0-9]*$").unwrap(); for (name, p) in &core.peripherals { @@ -295,6 +304,11 @@ pub fn gen(options: Options) { if let Some(block) = &p.block { let bi = BlockInfo::parse(block); + peripheral_counts.insert( + bi.module.clone(), + peripheral_counts.get(&bi.module).map_or(1, |v| v + 1), + ); + for pin in &p.pins { let mut row = Vec::new(); row.push(name.clone()); @@ -421,14 +435,16 @@ pub fn gen(options: Options) { clock.to_ascii_lowercase() }; - peripheral_rcc_table.push(vec![ - name.clone(), - clock, - enable_reg.to_ascii_lowercase(), - reset_reg.to_ascii_lowercase(), - format!("set_{}", enable_field.to_ascii_lowercase()), - format!("set_{}", reset_field.to_ascii_lowercase()), - ]); + if !name.starts_with("GPIO") { + peripheral_rcc_table.push(vec![ + name.clone(), + clock, + enable_reg.to_ascii_lowercase(), + reset_reg.to_ascii_lowercase(), + format!("set_{}", enable_field.to_ascii_lowercase()), + format!("set_{}", reset_field.to_ascii_lowercase()), + ]); + } } (None, Some(_)) => { print!("Unable to find enable register for {}", name) @@ -447,6 +463,30 @@ pub fn gen(options: Options) { dev.peripherals.push(ir_peri); } + for (id, channel_info) in &core.dma_channels { + let mut row = Vec::new(); + let dma_peri = core.peripherals.get(&channel_info.dma); + row.push(id.clone()); + row.push(channel_info.dma.clone()); + row.push(channel_info.channel.to_string()); + if let Some(dma_peri) = dma_peri { + if let Some(ref block) = dma_peri.block { + let bi = BlockInfo::parse(block); + if bi.module == "bdma" { + bdma_channels_table.push(row); + } else { + dma_channels_table.push(row); + } + } + } + + let dma_peri_name = channel_info.dma.clone(); + dma_channel_counts.insert( + dma_peri_name.clone(), + dma_channel_counts.get(&dma_peri_name).map_or(1, |v| v + 1), + ); + } + for (name, &num) in &core.interrupts { dev.interrupts.push(ir::Interrupt { name: name.clone(), @@ -493,8 +533,10 @@ pub fn gen(options: Options) { ); make_table(&mut extra, "peripheral_rcc", &peripheral_rcc_table); make_table(&mut extra, "dma_channels", &dma_channels_table); + make_table(&mut extra, "bdma_channels", &bdma_channels_table); make_table(&mut extra, "dma_requests", &dma_requests_table); make_peripheral_counts(&mut extra, &peripheral_counts); + make_dma_channel_counts(&mut extra, &dma_channel_counts); for (module, version) in peripheral_versions { all_peripheral_versions.insert((module.clone(), version.clone()));