diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3cc7654..05bec4cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: with: toolchain: stable target: thumbv7em-none-eabihf - - run: cargo build --features=${{ matrix.mcu }} --lib --examples + - run: "cargo build --features=${{ matrix.mcu }}' rt ld cortex-m/critical-section-single-core' --lib --examples" check-minimal-feature-set: name: Check minimal feature set diff --git a/Cargo.toml b/Cargo.toml index 55fbfa75..f6f0e239 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,10 +51,10 @@ cortex-m-semihosting = "0.5.0" defmt = "0.3.5" defmt-rtt = "0.4.0" defmt-test = "0.3.1" -panic-probe = "0.3.1" -panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] } +panic-probe = { version = "0.3.1", features = ["defmt", "print-defmt"] } +panic-rtt-target = { version = "0.1.3" } panic-semihosting = "0.6.0" -rtt-target = { version = "0.4.0" } +rtt-target = "0.5.0" systick-monotonic = "1.0" usb-device = "0.3.1" usbd-serial = "0.2.0" diff --git a/examples/serial_dma.rs b/examples/serial_dma.rs index bf580dbc..86820977 100644 --- a/examples/serial_dma.rs +++ b/examples/serial_dma.rs @@ -5,7 +5,8 @@ #![no_std] #![no_main] -use panic_semihosting as _; +use defmt_rtt as _; +use panic_probe as _; use cortex_m::{asm, singleton}; use cortex_m_rt::entry; @@ -40,8 +41,7 @@ fn main() -> ! { .pa10 .into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh), ); - let serial = Serial::new(dp.USART1, pins, 9600.Bd(), clocks, &mut rcc.apb2); - let (tx, rx) = serial.split(); + let mut serial = Serial::new(dp.USART1, pins, 9600.Bd(), clocks, &mut rcc.apb2); let dma1 = dp.DMA1.split(&mut rcc.ahb); @@ -50,6 +50,7 @@ fn main() -> ! { // the buffer we are going to receive the transmitted data in let rx_buf = singleton!(: [u8; 9] = [0; 9]).unwrap(); + // TODO: where to find this information and bind this information to serial implementation // DMA channel selection depends on the peripheral: // - USART1: TX = 4, RX = 5 // - USART2: TX = 6, RX = 7 @@ -57,23 +58,21 @@ fn main() -> ! { let (tx_channel, rx_channel) = (dma1.ch4, dma1.ch5); // start separate DMAs for sending and receiving the data - let sending = tx.write_all(tx_buf, tx_channel); - let receiving = rx.read_exact(rx_buf, rx_channel); + let transfer = serial.transfer_exact(rx_buf, tx_channel, tx_buf, rx_channel); // block until all data was transmitted and received - let (tx_buf, tx_channel, tx) = sending.wait(); - let (rx_buf, rx_channel, rx) = receiving.wait(); + let ((rx_buf, tx_buf), (rx_channel, tx_channel)) = transfer.wait(); + // let transfer = serial.read_exact(rx_buf, rx_channel); + // let (rx_buf, rx_channel) = transfer.wait(); assert_eq!(tx_buf, rx_buf); // After a transfer is finished its parts can be re-used for another one. tx_buf.copy_from_slice(b"hi again!"); - let sending = tx.write_all(tx_buf, tx_channel); - let receiving = rx.read_exact(rx_buf, rx_channel); + let transfer = serial.transfer_exact(rx_buf, rx_channel, tx_buf, tx_channel); - let (tx_buf, ..) = sending.wait(); - let (rx_buf, ..) = receiving.wait(); + let ((rx_buf, tx_buf), _) = transfer.wait(); assert_eq!(tx_buf, rx_buf); diff --git a/src/dma.rs b/src/dma.rs index 01f771c9..d8575188 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -48,18 +48,22 @@ pub trait Target { /// An in-progress one-shot DMA transfer #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Transfer { +pub struct Transfer<'t, B, C, T> +where + // C: Channel, + T: 't + Target, +{ // This is always a `Some` outside of `drop`. - inner: Option>, + inner: Option>, } -impl Transfer { +impl<'t, B, C: Channel, T: Target> Transfer<'t, B, C, T> { /// Start a DMA write transfer. /// /// # Panics /// /// Panics if the buffer is longer than 65535 words. - pub fn start_write(mut buffer: B, mut channel: C, target: T) -> Self + pub fn start_write(mut buffer: B, mut channel: C, target: &'t mut T) -> Self where B: WriteBuffer + 'static, T: OnChannel, @@ -91,7 +95,7 @@ impl Transfer { /// # Panics /// /// Panics if the buffer is longer than 65535 words. - pub fn start_read(buffer: B, mut channel: C, target: T) -> Self + pub fn start_read(buffer: B, mut channel: C, target: &'t mut T) -> Self where B: ReadBuffer + 'static, T: OnChannel, @@ -124,7 +128,7 @@ impl Transfer { /// /// - the given buffer will be valid for the duration of the transfer /// - the DMA channel is configured correctly for the given target and buffer - unsafe fn start(buffer: B, mut channel: C, mut target: T) -> Self + unsafe fn start(buffer: B, mut channel: C, target: &'t mut T) -> Self where T: OnChannel, { @@ -159,39 +163,151 @@ impl Transfer { /// # Panics /// /// Panics no transfer is ongoing. - pub fn stop(mut self) -> (B, C, T) { + pub fn stop(mut self) -> (B, C) { let mut inner = crate::unwrap!(self.inner.take()); inner.stop(); - (inner.buffer, inner.channel, inner.target) + (inner.buffer, inner.channel) } /// Block until this transfer is done and return ownership over its parts - pub fn wait(self) -> (B, C, T) { + pub fn wait(self) -> (B, C) { while !self.is_complete() {} self.stop() } } -impl Drop for Transfer { - fn drop(&mut self) { - if let Some(inner) = self.inner.as_mut() { - inner.stop(); +impl<'t, BR, BW, CR, CW, T> Transfer<'t, (BR, BW), (CR, CW), T> +where + CR: Channel, /* + ReceiveChannel */ + CW: Channel, /* + TransmitChannel */ + T: Target, +{ + /// Start a DMA write transfer. + /// + /// # Panics + /// + /// Panics if the buffer is longer than 65535 words. + // TODO: sort arguments (target at first, write at first (see embedded hal?)) + pub fn start_transfer( + buffer_read: BR, + mut buffer_write: BW, + mut channel_read: CR, + mut channel_write: CW, + target: &'t mut T, + ) -> Self + where + BR: ReadBuffer + 'static, + BW: WriteBuffer + 'static, + T: OnChannel + OnChannel, + { + // TODO: ensure same buffer length + // TODO: ensure same nord type + + // SAFETY: We don't know the concrete type of `buffer` here, all + // we can use are its `WriteBuffer` methods. Hence the only `&mut self` + // method we can call is `write_buffer`, which is allowed by + // `WriteBuffer`'s safety requirements. + let (ptr, len) = unsafe { buffer_write.write_buffer() }; + let len = crate::expect!(u16::try_from(len).ok(), "buffer is too large"); + + // SAFETY: We are using the address of a 'static WriteBuffer here, + // which is guaranteed to be safe for DMA. + unsafe { channel_write.set_memory_address(ptr as u32, Increment::Enable) }; + channel_write.set_transfer_length(len); + channel_write.set_word_size::(); + channel_write.set_direction(Direction::FromPeripheral); + + // SAFETY: We don't know the concrete type of `buffer` here, all + // we can use are its `ReadBuffer` methods. Hence there are no + // `&mut self` methods we can call, so we are safe according to + // `ReadBuffer`'s safety requirements. + let (ptr, len) = unsafe { buffer_read.read_buffer() }; + let len = crate::expect!(u16::try_from(len).ok(), "buffer is too large"); + + // SAFETY: We are using the address of a 'static ReadBuffer here, + // which is guaranteed to be safe for DMA. + unsafe { channel_read.set_memory_address(ptr as u32, Increment::Enable) }; + channel_read.set_transfer_length(len); + channel_read.set_word_size::(); + channel_read.set_direction(Direction::FromMemory); + + crate::assert!(!channel_read.is_enabled() && !channel_write.is_enabled()); + + atomic::compiler_fence(Ordering::Release); + + target.enable_dma(); + channel_read.enable(); + channel_write.enable(); + + Self { + inner: Some(TransferInner { + buffer: (buffer_read, buffer_write), + channel: (channel_read, channel_write), + target, + }), } } + + /// Is this transfer complete? + /// + /// # Panics + /// + /// Panics if no transfer is ongoing. + pub fn is_complete(&self) -> bool { + let inner = crate::unwrap!(self.inner.as_ref()); + defmt::dbg!(inner.channel.0.is_event_triggered(Event::Any)); + defmt::dbg!(inner.channel.1.is_event_triggered(Event::Any)); + inner.channel.0.is_event_triggered(Event::TransferComplete) + && inner.channel.1.is_event_triggered(Event::TransferComplete) + } + + /// Stop this transfer and return ownership over its parts + /// + /// # Panics + /// + /// Panics no transfer is ongoing. + pub fn stop(mut self) -> ((BR, BW), (CR, CW)) { + let mut inner = crate::unwrap!(self.inner.take()); + inner.stop(); + + (inner.buffer, inner.channel) + } + + /// Block until this transfer is done and return ownership over its parts + pub fn wait(self) -> ((BR, BW), (CR, CW)) { + defmt::dbg!(self.inner.as_ref().unwrap().channel.0.is_enabled()); + defmt::dbg!(self.inner.as_ref().unwrap().channel.1.is_enabled()); + while !self.is_complete() {} + + self.stop() + } } +// TODO: Use different drops in case of one or both channels +// impl<'t, B, C, T> Drop for Transfer<'t, B, C, T> +// where +// C: Channel, +// T: Target, +// { +// fn drop(&mut self) { +// if let Some(inner) = self.inner.as_mut() { +// inner.stop(); +// } +// } +// } + /// This only exists so we can implement `Drop` for `Transfer`. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -struct TransferInner { +struct TransferInner<'t, B, C, T> { buffer: B, channel: C, - target: T, + target: &'t mut T, } -impl TransferInner { +impl<'t, B, C: Channel, T: Target> TransferInner<'t, B, C, T> { /// Stop this transfer fn stop(&mut self) { self.channel.disable(); @@ -201,6 +317,22 @@ impl TransferInner { } } +impl<'t, B, CR, CW, T> TransferInner<'t, B, (CR, CW), T> +where + CR: Channel, + CW: Channel, + T: Target, +{ + /// Stop this transfer + fn stop(&mut self) { + self.channel.0.disable(); + self.channel.1.disable(); + self.target.disable_dma(); + + atomic::compiler_fence(Ordering::SeqCst); + } +} + /// DMA address increment mode #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -405,7 +537,7 @@ pub trait Channel: private::Channel { } /// Enable or disable the interrupt for the specified [`Event`]. - fn configure_intterupt(&mut self, event: Event, enable: bool) { + fn configure_interrupt(&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)), @@ -420,12 +552,12 @@ pub trait Channel: private::Channel { /// Enable the interrupt for the given [`Event`]. fn enable_interrupt(&mut self, event: Event) { - self.configure_intterupt(event, true); + self.configure_interrupt(event, true); } /// Disable the interrupt for the given [`Event`]. fn disable_interrupt(&mut self, event: Event) { - self.configure_intterupt(event, false); + self.configure_interrupt(event, false); } /// Start a transfer @@ -582,6 +714,10 @@ dma!( 2: { 1,2,3,4,5 } ); /// Marker trait mapping DMA targets to their channels pub trait OnChannel: Target + crate::private::Sealed {} +// TODO: put this in serial mod +// TODO: the naming is switched +pub trait TransmitChannel: crate::private::Sealed {} +pub trait ReceiveChannel: crate::private::Sealed {} macro_rules! on_channel { ( @@ -594,6 +730,11 @@ macro_rules! on_channel { impl crate::private::Sealed for serial::Serial<$USART, (Tx, Rx)> {} impl OnChannel<$dma::$TxChannel> for serial::Serial<$USART, (Tx, Rx)> {} impl OnChannel<$dma::$RxChannel> for serial::Serial<$USART, (Tx, Rx)> {} + + impl crate::private::Sealed for $dma::$TxChannel {} + impl TransmitChannel for $dma::$TxChannel {} + impl crate::private::Sealed for $dma::$RxChannel {} + impl ReceiveChannel for $dma::$RxChannel {} )+ )+ }; diff --git a/src/serial.rs b/src/serial.rs index 7af493f4..226b46b2 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -861,11 +861,11 @@ where Usart: Instance + Dma, { /// Fill the buffer with received data using DMA. - pub fn read_exact(self, buffer: B, mut channel: C) -> dma::Transfer + pub fn read_exact(&mut self, buffer: B, mut channel: C) -> dma::Transfer where Self: dma::OnChannel, B: dma::WriteBuffer + 'static, - C: dma::Channel, + C: dma::Channel + dma::ReceiveChannel, { // SAFETY: RDR is valid peripheral address, safe to dereference and pass to the DMA unsafe { @@ -879,11 +879,11 @@ where } /// Transmit all data in the buffer using DMA. - pub fn write_all(self, buffer: B, mut channel: C) -> dma::Transfer + pub fn write_all(&mut self, buffer: B, mut channel: C) -> dma::Transfer where Self: dma::OnChannel, B: dma::ReadBuffer + 'static, - C: dma::Channel, + C: dma::Channel + dma::TransmitChannel, { // SAFETY: TDR is valid peripheral address, safe to dereference and pass to the DMA unsafe { @@ -895,6 +895,39 @@ where dma::Transfer::start_read(buffer, channel, self) } + + pub fn transfer_exact( + &mut self, + read_buffer: BR, + mut channel_read: CR, + write_buffer: BW, + mut channel_write: CW, + ) -> dma::Transfer<(BR, BW), (CR, CW), Self> + where + Self: dma::OnChannel + dma::OnChannel, + BR: dma::ReadBuffer + 'static, + CR: dma::Channel + dma::TransmitChannel, + BW: dma::WriteBuffer + 'static, + CW: dma::Channel + dma::ReceiveChannel, + { + // SAFETY: TDR is valid peripheral address, safe to dereference and pass to the DMA + unsafe { + channel_read.set_peripheral_address( + core::ptr::addr_of!(self.usart.tdr) as u32, + dma::Increment::Disable, + ); + }; + + // SAFETY: RDR is valid peripheral address, safe to dereference and pass to the DMA + unsafe { + channel_write.set_peripheral_address( + core::ptr::addr_of!(self.usart.rdr) as u32, + dma::Increment::Disable, + ); + }; + + dma::Transfer::start_transfer(read_buffer, write_buffer, channel_read, channel_write, self) + } } impl dma::Target for Serial