diff --git a/examples/uart-dma.rs b/examples/uart-dma.rs new file mode 100644 index 0000000..737fac6 --- /dev/null +++ b/examples/uart-dma.rs @@ -0,0 +1,53 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; +extern crate stm32g0xx_hal as hal; + +use core::fmt::Write; + +use hal::prelude::*; +use hal::serial::*; +use hal::stm32; +use hal::dma::{self, Channel, Target}; + +use rt::entry; + +#[entry] +fn main() -> ! { + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let mut rcc = dp.RCC.constrain(); + let gpioa = dp.GPIOA.split(&mut rcc); + + let mut usart1 = dp.USART1.usart( + gpioa.pa9, + gpioa.pa10, + FullConfig::default().baudrate(115200.bps()), &mut rcc).unwrap(); + + writeln!(usart1, "Hello without DMA\r\n").unwrap(); + + let tx_buffer: [u8; 16] = *b"Hello with DMA!\n"; + + let (mut tx, _rx) = usart1.split(); + + let mut dma = dp.DMA.split(&mut rcc, dp.DMAMUX); + + let usart = unsafe { &(*stm32::USART1::ptr()) }; + let tx_data_register_addr = &usart.tdr as *const _ as u32; + let tx_dma_buf_addr : u32 = tx_buffer.as_ptr() as u32; + + dma.ch1.set_direction(dma::Direction::FromMemory); + dma.ch1.set_memory_address(tx_dma_buf_addr, true); + dma.ch1.set_peripheral_address(tx_data_register_addr, false); + dma.ch1.set_transfer_length(tx_buffer.len() as u16); + + dma.ch1.select_peripheral(tx.dmamux()); + + tx.enable_dma(); + dma.ch1.enable(); + + loop { + continue; + } +} diff --git a/src/dma.rs b/src/dma.rs index fde78e1..c4b76b2 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -1,250 +1,389 @@ //! Direct Memory Access Engine use crate::rcc::Rcc; -use crate::stm32::DMA; -use as_slice::{AsMutSlice, AsSlice}; -use core::ops; -use core::pin::Pin; -use core::sync::atomic::{self, Ordering}; - -#[derive(Debug)] -pub enum Error { - Overrun, - BufferError, -} +use crate::stm32::{self, DMA, DMAMUX}; +use crate::dmamux::{self, DmaMuxIndex}; -#[derive(Debug)] -pub enum Event { - HalfTransfer, - TransferComplete, -} +use crate::dmamux::DmaMuxExt; -#[derive(Clone, Copy, PartialEq)] -pub enum Half { - First, - Second, -} +/// Extension trait to split a DMA peripheral into independent channels +pub trait DmaExt { + /// The type to split the DMA into + type Channels; + + /// Reset the DMA peripheral + fn reset(self, rcc: &mut Rcc) -> Self; -#[derive(Clone, Copy, PartialEq)] -pub enum TransferDirection { - MemoryToMemory, - MemoryToPeriph, - PeriphToMemory, + /// Split the DMA into independent channels + fn split(self, rcc: &mut Rcc, dmamux: DMAMUX) -> Self::Channels; } -#[derive(Clone, Copy, PartialEq)] +/// Channel priority level pub enum Priority { + /// Low Low = 0b00, + /// Medium Medium = 0b01, + /// High High = 0b10, + /// Very high VeryHigh = 0b11, } -pub struct Transfer { - pub channel: CHANNEL, - pub buffer: BUFFER, +impl From for u8 { + fn from(prio: Priority) -> Self { + match prio { + Priority::Low => 0b00, + Priority::Medium => 0b01, + Priority::High => 0b10, + Priority::VeryHigh => 0b11, + } + } } -pub trait ReadDma -where - B: ops::DerefMut + 'static, - B::Target: AsMutSlice + Unpin, - Self: core::marker::Sized, -{ - /// Receives data into the given `buffer` until it's filled - /// - /// Returns a value that represents the in-progress DMA transfer - fn read(self, buffer: Pin) -> Transfer>; +/// DMA transfer direction +pub enum Direction { + /// From memory to peripheral + FromMemory, + /// From peripheral to memory + FromPeripheral, } -pub trait WriteDma -where - B: ops::Deref + 'static, - B::Target: AsSlice, - Self: core::marker::Sized, -{ - /// Sends out the given `buffer` - /// - /// Returns a value that represents the in-progress DMA transfer - fn write(self, buffer: Pin) -> Transfer>; +impl From for bool { + fn from(dir: Direction) -> Self { + match dir { + Direction::FromMemory => true, + Direction::FromPeripheral => false, + } + } } -pub trait CopyDma -where - F: ops::Deref + 'static, - F::Target: AsSlice, - T: ops::Deref + 'static, - T::Target: AsMutSlice + Unpin, - Self: core::marker::Sized, -{ - /// Copy data between buffers - /// - /// Returns a value that represents the in-progress DMA transfer - fn copy(self, from: Pin, to: Pin) -> Transfer, Pin)>; +#[doc = "Peripheral size"] +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +pub enum WordSize { + #[doc = "0: 8-bit size"] + BITS8 = 0, + #[doc = "1: 16-bit size"] + BITS16 = 1, + #[doc = "2: 32-bit size"] + BITS32 = 2, +} +impl From for u8 { + #[inline(always)] + fn from(variant: WordSize) -> Self { + variant as _ + } } -pub trait DmaExt { - type Channels; +/// DMA events +pub enum Event { + /// First half of a transfer is done + HalfTransfer, + /// Transfer is complete + TransferComplete, + /// A transfer error occurred + TransferError, + /// Any of the above events occurred + Any, +} + +mod private { + use crate::stm32; - fn split(self, rcc: &mut Rcc) -> Self::Channels; + /// Channel methods private to this module + pub trait Channel { + /// Return the register block for this channel + fn ch(&self) -> &stm32::dma::CH; + } } -pub trait DmaChannel { - fn set_peripheral_address(&mut self, address: u32, inc: bool); - fn set_memory_address(&mut self, address: u32, inc: bool); - fn set_transfer_length(&mut self, len: usize); - fn set_direction(&mut self, dir: TransferDirection); - fn set_priority(&mut self, priority: Priority); - fn start(&mut self); - fn stop(&mut self); - fn listen(&mut self, event: Event); - fn unlisten(&mut self, event: Event); +/// Trait implemented by all DMA channels +pub trait Channel: private::Channel { + + /// Connects the DMAMUX channel to the peripheral corresponding to index + fn select_peripheral(&mut self, index: DmaMuxIndex); + + /// Is the interrupt flag for the given event set? + fn event_occurred(&self, event: Event) -> bool; + + /// Clear the interrupt flag for the given event. + /// + /// Passing `Event::Any` clears all interrupt flags. + /// + /// Note that the the global interrupt flag is not automatically cleared + /// even when all other flags are cleared. The only way to clear it is to + /// call this method with `Event::Any`. + fn clear_event(&mut self, event: Event); + + /// Reset the control registers of this channel. + /// This stops any ongoing transfers. + 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); + } + + /// Set the base address of the peripheral data register from/to which the + /// data will be read/written. + /// + /// Only call this method on disabled channels. + /// + /// # Panics + /// + /// Panics if this channel is enabled. + fn set_peripheral_address(&mut self, address: u32, inc: bool) { + assert!(!self.is_enabled()); + + self.ch().par.write(|w| unsafe {w.pa().bits(address)}); + self.ch().cr.modify(|_, w| w.pinc().bit(inc)); + } + + /// Set the base address of the memory area from/to which + /// the data will be read/written. + /// + /// Only call this method on disabled channels. + /// + /// # Panics + /// + /// Panics if this channel is enabled. + fn set_memory_address(&mut self, address: u32, inc: bool) { + assert!(!self.is_enabled()); + + self.ch().mar.write(|w| unsafe {w.ma().bits(address)}); + self.ch().cr.modify(|_, w| w.minc().bit(inc)); + } + + /// Set the number of words to transfer. + /// + /// Only call this method on disabled channels. + /// + /// # Panics + /// + /// Panics if this channel is enabled. + fn set_transfer_length(&mut self, len: u16) { + assert!(!self.is_enabled()); + + self.ch().ndtr.write(|w| unsafe {w.ndt().bits(len)}); + } + + /// Set the word size. + fn set_word_size(&mut self, wsize: WordSize) { + self.ch().cr.modify(|_, w| unsafe { + w.psize().bits(wsize as u8); + w.msize().bits(wsize as u8) + }); + } + + /// Set the priority level of this channel + fn set_priority_level(&mut self, priority: Priority) { + let pl = priority.into(); + self.ch().cr.modify(|_, w| unsafe {w.pl().bits(pl) }); + } + + /// Set the transfer direction + fn set_direction(&mut self, direction: Direction) { + let dir = direction.into(); + self.ch().cr.modify(|_, w| w.dir().bit(dir)); + } + + /// Enable the interrupt for the given event + fn listen(&mut self, event: Event) { + use Event::*; + match event { + HalfTransfer => self.ch().cr.modify(|_, w| w.htie().set_bit()), + TransferComplete => self.ch().cr.modify(|_, w| w.tcie().set_bit()), + TransferError => self.ch().cr.modify(|_, w| w.teie().set_bit()), + Any => self.ch().cr.modify(|_, w| { + w.htie().set_bit(); + w.tcie().set_bit(); + w.teie().set_bit() + }), + } + } + + /// Disable the interrupt for the given event + fn unlisten(&mut self, event: Event) { + use Event::*; + match event { + HalfTransfer => self.ch().cr.modify(|_, w| w.htie().clear_bit()), + TransferComplete => self.ch().cr.modify(|_, w| w.tcie().clear_bit()), + TransferError => self.ch().cr.modify(|_, w| w.teie().clear_bit()), + Any => self.ch().cr.modify(|_, w| { + w.htie().clear_bit(); + w.tcie().clear_bit(); + w.teie().clear_bit() + }), + } + } + + /// Start a transfer + fn enable(&mut self) { + self.clear_event(Event::Any); + self.ch().cr.modify(|_, w| w.en().set_bit()); + } + + /// Stop the current transfer + fn disable(&mut self) { + self.ch().cr.modify(|_, w| w.en().clear_bit()); + } + + /// Is there a transfer in progress on this channel? + fn is_enabled(&self) -> bool { + self.ch().cr.read().en().bit_is_set() + } + } macro_rules! dma { - ($($DMAX:ident: ($dmaXen:ident, $dmaXrst:ident, { - $($CX:ident: ($ccrX:ident, $cndtrX:ident, $cparX:ident, $cmarX:ident, $cgifX:ident),)+ - }),)+) => { + ( + channels: { + $( $Ci:ident: ( + $chi:ident, + $htifi:ident, $tcifi:ident, $teifi:ident, $gifi:ident, + $chtifi:ident, $ctcifi:ident, $cteifi:ident, $cgifi:ident, + $MuxCi: ident + ), )+ + }, + ) => { + + /// DMA channels + pub struct Channels { + $( pub $chi: $Ci, )+ + } + + impl Channels { + /// Reset the control registers of all channels. + /// This stops any ongoing transfers. + fn reset(&mut self) { + $( self.$chi.reset(); )+ + } + } + + $( - impl DmaExt for $DMAX { - type Channels = Channels; - - fn split(self, rcc: &mut Rcc) -> Channels { - rcc.rb.ahbenr.modify(|_, w| w.$dmaXen().set_bit()); - $( - self.$ccrX.reset(); - )+ - Channels((), $($CX { }),+) + /// Singleton that represents a DMA channel + pub struct $Ci { + mux: dmamux::$MuxCi, + } + + impl private::Channel for $Ci { + fn ch(&self) -> &stm32::dma::CH { + // NOTE(unsafe) $Ci grants exclusive access to this register + unsafe { &(*DMA::ptr()).$chi } } } - pub struct Channels((), $(pub $CX),+); + impl $Ci { + pub fn mux(&mut self) -> &mut dyn dmamux::DmaMuxChannel { + &mut self.mux + } + } - $( - pub struct $CX; + impl Channel for $Ci { - impl DmaChannel for $CX { - /// Associated peripheral `address` - /// - /// `inc` indicates whether the address will be incremented after every byte transfer - fn set_peripheral_address(&mut self, address: u32, inc: bool) { - let dma = unsafe { &(*$DMAX::ptr()) }; - dma.$cparX.write(|w| unsafe { w.pa().bits(address) }); - dma.$ccrX.modify(|_, w| w.pinc().bit(inc) ); - } + fn select_peripheral(&mut self, index: DmaMuxIndex) { + self.mux().select_peripheral(index); + } - /// `address` where from/to data will be read/write - /// - /// `inc` indicates whether the address will be incremented after every byte transfer - fn set_memory_address(&mut self, address: u32, inc: bool) { - let dma = unsafe { &(*$DMAX::ptr()) }; - dma.$cmarX.write(|w| unsafe { w.ma().bits(address) }); - dma.$ccrX.modify(|_, w| w.minc().bit(inc) ); - } + fn event_occurred(&self, event: Event) -> bool { + use Event::*; - /// Number of bytes to transfer - fn set_transfer_length(&mut self, len: usize) { - let dma = unsafe { &(*$DMAX::ptr()) }; - dma.$cndtrX.write(|w| unsafe { w.ndt().bits(len as u16) }); + // NOTE(unsafe) atomic read + let flags = unsafe { (*DMA::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(), } + } - /// DMA Transfer direction - fn set_direction(&mut self, dir: TransferDirection) { - let dma = unsafe { &(*$DMAX::ptr()) }; - match dir { - TransferDirection::MemoryToMemory => dma.$ccrX.modify(|_, w| { - w.mem2mem().set_bit().circ().clear_bit() - }), - TransferDirection::MemoryToPeriph => dma.$ccrX.modify(|_, w| { - w.mem2mem().clear_bit().circ().clear_bit().dir().set_bit() - }), - TransferDirection::PeriphToMemory => dma.$ccrX.modify(|_, w| { - w.mem2mem().clear_bit().circ().clear_bit().dir().clear_bit() - }), - } - } + fn clear_event(&mut self, event: Event) { + use Event::*; - /// Set channel priority - fn set_priority(&mut self, priority: Priority) { - let dma = unsafe { &(*$DMAX::ptr()) }; - dma.$ccrX.modify(|_, w| unsafe { w.pl().bits(priority as u8) }); + // NOTE(unsafe) atomic write to a stateless register + unsafe { + &(*DMA::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(), + }); } + } - /// Starts the DMA transfer - fn start(&mut self) { - let dma = unsafe { &(*$DMAX::ptr()) }; - dma.$ccrX.modify(|_, w| w.en().set_bit() ); - } + } + )+ + } +} - /// Stops the DMA transfer - fn stop(&mut self) { - let dma = unsafe { &(*$DMAX::ptr()) }; - dma.ifcr.write(|w| w.$cgifX().set_bit()); - dma.$ccrX.modify(|_, w| w.en().clear_bit() ); - } +#[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] +dma!( + channels: { + C1: (ch1, htif1, tcif1, teif1, gif1, chtif1, ctcif1, cteif1, cgif1, C0), + C2: (ch2, htif2, tcif2, teif2, gif2, chtif2, ctcif2, cteif2, cgif2, C1), + C3: (ch3, htif3, tcif3, teif3, gif3, chtif3, ctcif3, cteif3, cgif3, C2), + C4: (ch4, htif4, tcif4, teif4, gif4, chtif4, ctcif4, cteif4, cgif4, C3), + C5: (ch5, htif5, tcif5, teif5, gif5, chtif5, ctcif5, cteif5, cgif5, C4), + C6: (ch6, htif6, tcif6, teif6, gif6, chtif6, ctcif6, cteif6, cgif6, C5), + C7: (ch7, htif7, tcif7, teif7, gif7, chtif7, ctcif7, cteif7, cgif7, C6), + }, +); - fn listen(&mut self, event: Event) { - let dma = unsafe { &(*$DMAX::ptr()) }; - match event { - Event::HalfTransfer => dma.$ccrX.modify(|_, w| w.htie().set_bit()), - Event::TransferComplete => { - dma.$ccrX.modify(|_, w| w.tcie().set_bit()) - } - } - } +#[cfg(any(feature = "stm32g030",feature = "stm32g031", feature = "stm32g041"))] +dma!( + channels: { + C1: (ch1, htif1, tcif1, teif1, gif1, chtif1, ctcif1, cteif1, cgif1, C0), + C2: (ch2, htif2, tcif2, teif2, gif2, chtif2, ctcif2, cteif2, cgif2, C1), + C3: (ch3, htif3, tcif3, teif3, gif3, chtif3, ctcif3, cteif3, cgif3, C2), + C4: (ch4, htif4, tcif4, teif4, gif4, chtif4, ctcif4, cteif4, cgif4, C3), + C5: (ch5, htif5, tcif5, teif5, gif5, chtif5, ctcif5, cteif5, cgif5, C4), + }, +); - fn unlisten(&mut self, event: Event) { - let dma = unsafe { &(*$DMAX::ptr()) }; - match event { - Event::HalfTransfer => { - dma.$ccrX.modify(|_, w| w.htie().clear_bit()) - }, - Event::TransferComplete => { - dma.$ccrX.modify(|_, w| w.tcie().clear_bit()) - } - } - } - } +impl DmaExt for DMA { - impl CopyDma for $CX - where - F: ops::Deref + 'static, - T: ops::Deref + 'static, - F::Target: AsSlice, - T::Target: AsMutSlice + Unpin, - Self: core::marker::Sized, - { - fn copy(mut self, buf_from: Pin, buf_to: Pin) -> Transfer, Pin)> { - let slice_from = buf_from.as_slice(); - let slice_to = buf_to.as_slice(); - let (ptr_from, len_from) = (slice_from.as_ptr(), slice_from.len()); - let (ptr_to, len_to) = (slice_to.as_ptr(), slice_to.len()); - assert!(len_from == len_to); - - self.set_direction(TransferDirection::MemoryToMemory); - self.set_memory_address(ptr_from as u32, true); - self.set_peripheral_address(ptr_to as u32, false); - self.set_transfer_length(len_from); - - atomic::compiler_fence(Ordering::SeqCst); - self.start(); - - Transfer { - buffer: (buf_from, buf_to), - channel: self, - } - } - } - )+ - )+ + type Channels = Channels; + + fn reset(self, rcc: &mut Rcc) -> Self { + // reset DMA + rcc.rb.ahbrstr.modify(|_, w| w.dmarst().set_bit()); + rcc.rb.ahbrstr.modify(|_, w| w.dmarst().clear_bit()); + self + } + + fn split(self, rcc: &mut Rcc, dmamux: DMAMUX) -> Self::Channels { + + let muxchannels = dmamux.split(); + // enable DMA clock + rcc.rb.ahbenr.modify(|_,w| w.dmaen().set_bit()); + + let mut channels = Channels { + ch1: C1 { mux: muxchannels.ch0 }, + ch2: C2 { mux: muxchannels.ch1 }, + ch3: C3 { mux: muxchannels.ch2 }, + ch4: C4 { mux: muxchannels.ch3 }, + ch5: C5 { mux: muxchannels.ch4 }, + ch6: C6 { mux: muxchannels.ch5 }, + ch7: C7 { mux: muxchannels.ch6 }, + }; + channels.reset(); + channels } + } -dma! { - DMA: (dmaen, dma1rst, { - Channel1: ( ccr1, cndtr1, cpar1, cmar1, cgif1 ), - Channel2: ( ccr2, cndtr2, cpar2, cmar2, cgif2 ), - Channel3: ( ccr3, cndtr3, cpar3, cmar3, cgif3 ), - Channel4: ( ccr4, cndtr4, cpar4, cmar4, cgif4 ), - Channel5: ( ccr5, cndtr5, cpar5, cmar5, cgif5 ), - }), +/// Trait implemented by DMA targets. +pub trait Target { + + /// Returns the correct DMAMUX index to configure DMA channel for this peripheral + fn dmamux(&self) -> crate::dmamux::DmaMuxIndex; + + /// Enable DMA on the target + fn enable_dma(&mut self) {} + /// Disable DMA on the target + fn disable_dma(&mut self) {} } + diff --git a/src/dmamux.rs b/src/dmamux.rs new file mode 100644 index 0000000..2e84e3d --- /dev/null +++ b/src/dmamux.rs @@ -0,0 +1,230 @@ +use crate::stm32::DMAMUX; + +/// Extension trait to split a DMA peripheral into independent channels +pub trait DmaMuxExt { + /// The type to split the DMA into + type Channels; + + /// Split the DMA into independent channels + fn split(self) -> Self::Channels; +} + + +pub enum DmaMuxIndex { + dmamux_req_gen0 = 0, + dmamux_req_gen1 = 1, + dmamux_req_gen2 = 2, + dmamux_req_gen3 = 3, + ADC = 5, + + #[cfg(any(feature = "stm32g041", feature = "stm32g081"))] + AES_IN = 6, + #[cfg(any(feature = "stm32g041", feature = "stm32g081"))] + AES_OUT = 7, + #[cfg(feature = "stm32g0x1")] + DAC_Channel1 = 8, + #[cfg(feature = "stm32g0x1")] + DAC_Channel2 = 9, + + I2C1_RX = 10, + I2C1_TX = 11, + I2C2_RX = 12, + I2C2_TX = 13, + + #[cfg(feature = "stm32g0x1")] + LPUART_RX = 14, + #[cfg(feature = "stm32g0x1")] + LPUART_TX = 15, + + SPI1_RX = 16, + SPI1_TX = 17, + SPI2_RX = 18, + SPI2_TX = 19, + + TIM1_CH1 = 20, + TIM1_CH2 = 21, + TIM1_CH3 = 22, + TIM1_CH4 = 23, + TIM1_TRIG_COM = 24, + TIM1_UP = 25, + + #[cfg(feature = "stm32g0x1")] + TIM2_CH1 = 26, + #[cfg(feature = "stm32g0x1")] + TIM2_CH2 = 27, + #[cfg(feature = "stm32g0x1")] + TIM2_CH3 = 28, + #[cfg(feature = "stm32g0x1")] + TIM2_CH4 = 29, + #[cfg(feature = "stm32g0x1")] + TIM2_TRIG = 30, + #[cfg(feature = "stm32g0x1")] + TIM2_UP = 31, + + TIM3_CH1 = 32, + TIM3_CH2 = 33, + TIM3_CH3 = 34, + TIM3_CH4 = 35, + TIM3_TRIG = 36, + TIM3_UP = 37, + #[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] + TIM6_UP = 38, + #[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] + TIM7_UP = 39, + TIM15_CH1 = 40, + TIM15_CH2 = 41, + TIM15_TRIG_COM = 42, + TIM15_UP = 43, + TIM16_CH1 = 44, + TIM16_COM = 45, + TIM16_UP = 46, + TIM17_CH1 = 47, + TIM17_COM = 48, + TIM17_UP = 49, + + USART1_RX = 50, + USART1_TX = 51, + USART2_RX = 52, + USART2_TX = 53, + #[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] + USART3_RX = 54, + #[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] + USART3_TX = 55, + #[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] + USART4_RX = 56, + #[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] + USART4_TX = 57, + + #[cfg(any(feature = "stm32g071", feature = "stm32g081"))] + UCPD1_RX = 58, + #[cfg(any(feature = "stm32g071", feature = "stm32g081"))] + UCPD1_TX = 59, + #[cfg(any(feature = "stm32g071", feature = "stm32g081"))] + UCPD2_RX = 60, + #[cfg(any(feature = "stm32g071", feature = "stm32g081"))] + UCPD2_TX = 61, +} + +impl DmaMuxIndex { + pub fn val(self) -> u8 { + self as u8 + } +} + + +pub enum DmaMuxTriggerSync { + EXTI_LINE0 = 0, + EXTI_LINE1 = 1, + EXTI_LINE2 = 2, + EXTI_LINE3 = 3, + EXTI_LINE4 = 4, + EXTI_LINE5 = 5, + EXTI_LINE6 = 6, + EXTI_LINE7 = 7, + EXTI_LINE8 = 8, + EXTI_LINE9 = 9, + EXTI_LINE10 = 10, + EXTI_LINE11 = 11, + EXTI_LINE12 = 12, + EXTI_LINE13 = 13, + EXTI_LINE14 = 14, + EXTI_LINE15 = 15, + dmamux_evt0 = 16, + dmamux_evt1 = 17, + dmamux_evt2 = 18, + dmamux_evt3 = 19, + + #[cfg(feature = "stm32g0x1")] + LPTIM1_OUT = 20, + #[cfg(feature = "stm32g0x1")] + LPTIM2_OUT = 21, + + TIM14_OC = 22 +} +impl DmaMuxTriggerSync { + pub fn val(self) -> u8 { + self as u8 + } +} + +pub trait DmaMuxChannel { + fn select_peripheral(&mut self, index: DmaMuxIndex); +} + +macro_rules! dma_mux { + ( + channels: { + $( $Ci:ident: ($chi:ident, $cr:ident), )+ + }, + ) => { + + /// DMAMUX channels + pub struct Channels { + $( pub $chi: $Ci, )+ + } + + $( + /// Singleton that represents a DMAMUX channel + pub struct $Ci { + _0: (), + } + + impl DmaMuxChannel for $Ci { + fn select_peripheral(&mut self, index: DmaMuxIndex) { + let reg = unsafe { &(*DMAMUX::ptr()).$cr }; + reg.write( |w| unsafe { + w.dmareq_id().bits(index.val()) + .ege().set_bit() + }); + + } + } + )+ + + } +} + + +#[cfg(any(feature = "stm32g070",feature = "stm32g071", feature = "stm32g081"))] +dma_mux!( + channels: { + C0: (ch0, dmamux_c0cr), + C1: (ch1, dmamux_c1cr), + C2: (ch2, dmamux_c2cr), + C3: (ch3, dmamux_c3cr), + C4: (ch4, dmamux_c4cr), + C5: (ch5, dmamux_c5cr), + C6: (ch6, dmamux_c6cr), + }, +); + +#[cfg(any(feature = "stm32g030",feature = "stm32g031", feature = "stm32g041"))] +dma_mux!( + channels: { + C0: (ch0, dmamux_c0cr), + C1: (ch1, dmamux_c1cr), + C2: (ch2, dmamux_c2cr), + C3: (ch3, dmamux_c3cr), + C4: (ch4, dmamux_c4cr) + }, +); + + +impl DmaMuxExt for DMAMUX { + type Channels = Channels; + + fn split(self) -> Self::Channels { + + let channels = Channels { + ch0: C0 { _0: () }, + ch1: C1 { _0: () }, + ch2: C2 { _0: () }, + ch3: C3 { _0: () }, + ch4: C4 { _0: () }, + ch5: C5 { _0: () }, + ch6: C6 { _0: () }, + }; + channels + } + +} diff --git a/src/lib.rs b/src/lib.rs index 486ac06..4bb4e0d 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ pub mod analog; // pub mod comparator; pub mod crc; pub mod delay; +pub mod dmamux; pub mod dma; pub mod exti; pub mod gpio; diff --git a/src/prelude.rs b/src/prelude.rs index c702bae..829d0ee 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,10 +13,10 @@ pub use crate::analog::dac::DacOut as _; // pub use crate::comparator::ComparatorExt as _; pub use crate::crc::CrcExt as _; pub use crate::delay::DelayExt as _; -pub use crate::dma::CopyDma as _; +// pub use crate::dma::CopyDma as _; pub use crate::dma::DmaExt as _; -pub use crate::dma::ReadDma as _; -pub use crate::dma::WriteDma as _; +// pub use crate::dma::ReadDma as _; +// pub use crate::dma::WriteDma as _; pub use crate::exti::ExtiExt as _; pub use crate::gpio::GpioExt as _; pub use crate::i2c::I2cExt as _; diff --git a/src/serial/usart.rs b/src/serial/usart.rs index 03b37c4..64568ee 100644 --- a/src/serial/usart.rs +++ b/src/serial/usart.rs @@ -1,18 +1,16 @@ use core::fmt; use core::marker::PhantomData; -// use core::ops; -// use core::pin::Pin; -// use core::sync::atomic::{self, Ordering}; -// use crate::dma::{DmaChannel, ReadDma, Transfer, TransferDirection, WriteDma}; use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiod::*}; use crate::gpio::{AltFunction, DefaultMode}; use crate::prelude::*; use crate::rcc::Rcc; use crate::stm32::*; - +use crate::dma; +use crate::dmamux::DmaMuxIndex; use nb::block; +use cortex_m::interrupt; use crate::serial::config::*; /// Serial error @@ -140,7 +138,7 @@ where } macro_rules! uart_shared { - ($USARTX:ident) => { + ($USARTX:ident, $dmamux_rx:ident, $dmamux_tx:ident) => { impl Rx<$USARTX, Config> { pub fn listen(&mut self) { @@ -204,7 +202,6 @@ macro_rules! uart_shared { let usart = unsafe { &(*$USARTX::ptr()) }; usart.cr1.modify(|_, w| w.txeie().clear_bit()); } - } impl hal::serial::Write for Tx<$USARTX, Config> { @@ -253,6 +250,52 @@ macro_rules! uart_shared { } + impl dma::Target for Rx<$USARTX, Config> { + + fn dmamux(&self) -> DmaMuxIndex { + DmaMuxIndex::$dmamux_rx + } + + fn enable_dma(&mut self) { + // NOTE(unsafe) critical section prevents races + interrupt::free(|_| unsafe { + let cr3 = &(*$USARTX::ptr()).cr3; + cr3.modify(|_, w| w.dmar().set_bit()); + }); + } + + fn disable_dma(&mut self) { + // NOTE(unsafe) critical section prevents races + interrupt::free(|_| unsafe { + let cr3 = &(*$USARTX::ptr()).cr3; + cr3.modify(|_, w| w.dmar().clear_bit()); + }); + } + } + + impl dma::Target for Tx<$USARTX, Config> { + + fn dmamux(&self) -> DmaMuxIndex { + DmaMuxIndex::$dmamux_tx + } + + fn enable_dma(&mut self) { + // NOTE(unsafe) critical section prevents races + interrupt::free(|_| unsafe { + let cr3 = &(*$USARTX::ptr()).cr3; + cr3.modify(|_, w| w.dmat().set_bit()); + }); + } + + fn disable_dma(&mut self) { + // NOTE(unsafe) critical section prevents races + interrupt::free(|_| unsafe { + let cr3 = &(*$USARTX::ptr()).cr3; + cr3.modify(|_, w| w.dmat().clear_bit()); + }); + } + } + } } @@ -575,10 +618,10 @@ macro_rules! uart_full { } } -uart_shared!(USART1); -uart_shared!(USART2); -uart_shared!(USART3); -uart_shared!(USART4); +uart_shared!(USART1, USART1_RX, USART1_TX); +uart_shared!(USART2, USART2_RX, USART2_TX); +uart_shared!(USART3, USART3_RX, USART3_TX); +uart_shared!(USART4, USART4_RX, USART4_TX); uart_full!( USART1, usart1, apbenr2, usart1en, 1,