From a4d3b4b6ae3f3265ea372e446a6e7b5d3685ea3a Mon Sep 17 00:00:00 2001 From: elagil Date: Mon, 25 Aug 2025 21:10:59 +0200 Subject: feat: wip, write buffer in halves --- embassy-stm32/src/dma/gpdma/ringbuffered.rs | 84 ++++------------------------- embassy-stm32/src/dma/ringbuffer/mod.rs | 76 ++++++++++++++------------ embassy-stm32/src/usart/ringbuffered.rs | 2 +- 3 files changed, 54 insertions(+), 108 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 99c85a221..a5d2c700c 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs @@ -46,29 +46,11 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { } } -/// The current buffer half (e.g. for DMA or the user application). -#[derive(Debug, PartialEq, PartialOrd)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum BufferHalf { - First, - Second, -} - -impl BufferHalf { - fn toggle(&mut self) { - *self = match *self { - Self::First => Self::Second, - Self::Second => Self::First, - }; - } -} - /// Ringbuffer for receiving data using GPDMA linked-list mode. pub struct ReadableRingBuffer<'a, W: Word> { channel: PeripheralRef<'a, AnyChannel>, ringbuf: ReadableDmaRingBuffer<'a, W>, table: Table<2>, - user_buffer_half: BufferHalf, } impl<'a, W: Word> ReadableRingBuffer<'a, W> { @@ -99,7 +81,6 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { channel, ringbuf: ReadableDmaRingBuffer::new(buffer), table, - user_buffer_half: BufferHalf::First, } } @@ -217,7 +198,6 @@ pub struct WritableRingBuffer<'a, W: Word> { channel: PeripheralRef<'a, AnyChannel>, ringbuf: WritableDmaRingBuffer<'a, W>, table: Table<2>, - user_buffer_half: BufferHalf, } impl<'a, W: Word> WritableRingBuffer<'a, W> { @@ -246,7 +226,6 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { channel, ringbuf: WritableDmaRingBuffer::new(buffer), table, - user_buffer_half: BufferHalf::First, }; this @@ -280,62 +259,21 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { - return self - .ringbuf - .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) - .await; + // return self + // .ringbuf + // .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + // .await; + let mut remaining_cap = 0; let mut written_len = 0; - let len = buffer.len(); - let mut remaining_cap = 0; - let cap = self.ringbuf.cap(); - - let dma = &mut DmaCtrlImpl(self.channel.reborrow()); - let user_buffer_half = &mut self.user_buffer_half; - let ringbuf = &mut self.ringbuf; - let table = &mut self.table; - - while written_len != len { - // info!( - // "read {}, write {}, cap {}", - // ringbuf.read_index(0), - // ringbuf.write_index(0), - // ringbuf.cap() - // ); - - let dma_buffer_half = if ringbuf.read_index(0) < ringbuf.cap() / 2 { - BufferHalf::First - } else { - BufferHalf::Second - }; - - // if dma_buffer_half == *user_buffer_half { - // info!("swap user from {}", user_buffer_half); - // table.unlink(); - - // match user_buffer_half { - // BufferHalf::First => table.link_indices(1, 0), - // BufferHalf::Second => table.link_indices(0, 1), - // } - - // user_buffer_half.toggle(); - // } - - let index = match dma_buffer_half { - BufferHalf::First => { - // Fill up second buffer half when DMA reads the first. - cap - 1 - } - BufferHalf::Second => { - // Fill up first buffer half when DMA reads the second. - cap / 2 - 1 - } - }; - - (written_len, remaining_cap) = ringbuf.write_until(dma, &buffer, index).await?; + while written_len < buffer.len() { + (written_len, remaining_cap) = self + .ringbuf + .write_half(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await?; + // info!("Written: {}/{}", written_len, buffer.len()); } - info!("done"); Ok(remaining_cap) } diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index c4bf4dd60..8d00d822d 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -3,6 +3,14 @@ use core::task::{Poll, Waker}; use crate::dma::word::Word; +/// The current buffer half (e.g. for DMA or the user application). +#[derive(Debug, PartialEq, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum BufferHalf { + First, + Second, +} + pub trait DmaCtrl { /// Get the NDTR register value, i.e. the space left in the underlying /// buffer until the dma writer wraps. @@ -92,16 +100,6 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } } - /// The current ring-buffer read index. - pub fn read_index(&self, offset: usize) -> usize { - self.read_index.as_index(self.cap(), offset) - } - - /// The current ring-buffer write index. - pub fn write_index(&self, offset: usize) -> usize { - self.write_index.as_index(self.cap(), offset) - } - /// Reset the ring buffer to its initial state. pub fn reset(&mut self, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); @@ -218,14 +216,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } } - /// The current ring-buffer read index. - pub fn read_index(&self, offset: usize) -> usize { - self.read_index.as_index(self.cap(), offset) - } - - /// The current ring-buffer write index. - pub fn write_index(&self, offset: usize) -> usize { - self.write_index.as_index(self.cap(), offset) + /// The buffer half that is in use by the DMA. + fn dma_half(&self) -> BufferHalf { + if self.read_index.as_index(self.cap(), 0) < self.cap() / 2 { + BufferHalf::First + } else { + BufferHalf::Second + } } /// Reset the ring buffer to its initial state. The buffer after the reset will be full. @@ -305,6 +302,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Write an exact number of elements to the ringbuffer. /// /// Returns the remaining write capacity in the buffer. + #[allow(dead_code)] pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { let mut written_len = 0; let buffer_len = buffer.len(); @@ -327,31 +325,41 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { .await } - /// Write until a given write index. + /// Write the user's current buffer half - not used by the DMA. /// /// Returns a tuple of the written length, and the remaining write capacity in the buffer. - pub async fn write_until( - &mut self, - dma: &mut impl DmaCtrl, - buffer: &[W], - index: usize, - ) -> Result<(usize, usize), Error> { + #[allow(dead_code)] + pub async fn write_half(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<(usize, usize), Error> { let mut written_len = 0; - let write_len = index - .saturating_sub(self.write_index.as_index(self.cap(), 0)) - .min(buffer.len()); - - if write_len == 0 { - return Err(Error::Overrun); - } + let buffer_len = buffer.len(); poll_fn(|cx| { dma.set_waker(cx.waker()); - match self.write(dma, &buffer[written_len..write_len]) { + let dma_half = self.dma_half(); + // let user_half = self.user_half(); + + // if dma_half == user_half { + // info!("ups"); + // return Poll::Ready(Err(Error::Overrun)); + // } + + let write_index = self.write_index.as_index(self.cap(), 0); + let target_write_len = match dma_half { + BufferHalf::First => self.cap().saturating_sub(write_index), + BufferHalf::Second => (self.cap() / 2).saturating_sub(write_index), + }; + let write_end_index = (target_write_len + written_len).min(buffer_len); + + // info!( + // "buf_len: {}, write_len: {}, write_index: {}", + // buffer_len, target_write_len, write_index + // ); + + match self.write(dma, &buffer[written_len..write_end_index]) { Ok((len, remaining)) => { written_len += len; - if written_len == write_len { + if written_len == write_end_index { Poll::Ready(Ok((written_len, remaining))) } else { Poll::Pending diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 1d4a44896..5f4e87834 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -381,7 +381,7 @@ impl ReadReady for RingBufferedUartRx<'_> { crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, crate::dma::ringbuffer::Error::DmaUnsynced => { error!( - "Ringbuffer error: DmaUNsynced, driver implementation is + "Ringbuffer error: DmaUNsynced, driver implementation is probably bugged please open an issue" ); // we report this as overrun since its recoverable in the same way -- cgit