From 2f24568de08e846d4bfafff90a5b9ba352d86431 Mon Sep 17 00:00:00 2001 From: etiennecollin Date: Mon, 25 Aug 2025 21:10:59 +0200 Subject: feat: custom dma configuration using RegisterUpdaters struct See this PR comment: https://github.com/embassy-rs/embassy/pull/3923#issuecomment-2889283939 --- embassy-stm32/src/dma/gpdma/linked_list.rs | 20 ++++++++++++++++++-- embassy-stm32/src/dma/gpdma/mod.rs | 22 ++++++++++++++++++++++ embassy-stm32/src/dma/gpdma/ringbuffered.rs | 16 +++++++++++----- embassy-stm32/src/sai/mod.rs | 5 +++-- embassy-stm32/src/usart/ringbuffered.rs | 3 ++- 5 files changed, 56 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma/linked_list.rs b/embassy-stm32/src/dma/gpdma/linked_list.rs index a95e5590e..76381def3 100644 --- a/embassy-stm32/src/dma/gpdma/linked_list.rs +++ b/embassy-stm32/src/dma/gpdma/linked_list.rs @@ -4,6 +4,7 @@ use stm32_metapac::gpdma::regs; use stm32_metapac::gpdma::vals::Dreq; +use super::RegisterUpdaters; use crate::dma::word::{Word, WordSize}; use crate::dma::{Dir, Request}; @@ -41,7 +42,12 @@ pub struct LinearItem { impl LinearItem { /// Create a new read DMA transfer (peripheral to memory). - pub unsafe fn new_read<'d, W: Word>(request: Request, peri_addr: *mut W, buf: &'d mut [W]) -> Self { + pub unsafe fn new_read<'d, W: Word>( + request: Request, + peri_addr: *mut W, + buf: &'d mut [W], + register_updaters: &RegisterUpdaters, + ) -> Self { Self::new_inner( request, Dir::PeripheralToMemory, @@ -51,11 +57,17 @@ impl LinearItem { true, W::size(), W::size(), + register_updaters, ) } /// Create a new write DMA transfer (memory to peripheral). - pub unsafe fn new_write<'d, MW: Word, PW: Word>(request: Request, buf: &'d [MW], peri_addr: *mut PW) -> Self { + pub unsafe fn new_write<'d, MW: Word, PW: Word>( + request: Request, + buf: &'d [MW], + peri_addr: *mut PW, + register_updaters: &RegisterUpdaters, + ) -> Self { Self::new_inner( request, Dir::MemoryToPeripheral, @@ -65,6 +77,7 @@ impl LinearItem { true, MW::size(), PW::size(), + register_updaters, ) } @@ -77,6 +90,7 @@ impl LinearItem { incr_mem: bool, data_size: WordSize, dst_size: WordSize, + register_updaters: &RegisterUpdaters, ) -> Self { // BNDT is specified as bytes, not as number of transfers. let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { @@ -91,6 +105,7 @@ impl LinearItem { tr1.set_ddw(dst_size.into()); tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); + (register_updaters.tr1)(&mut tr1); let mut tr2 = regs::ChTr2(0); tr2.set_dreq(match dir { @@ -98,6 +113,7 @@ impl LinearItem { Dir::PeripheralToMemory => Dreq::SOURCE_PERIPHERAL, }); tr2.set_reqsel(request); + (register_updaters.tr2)(&mut tr2); let (sar, dar) = match dir { Dir::MemoryToPeripheral => (mem_addr as _, peri_addr as _), diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 604db2852..58f93ffb0 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs @@ -73,6 +73,28 @@ impl Default for TransferOptions { } } +/// GPDMA linked-list item register updater functions. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct RegisterUpdaters { + /// Function used to overwrite transfer register 1. + pub tr1: fn(&mut pac::gpdma::regs::ChTr1), + /// Function used to overwrite transfer register 2. + pub tr2: fn(&mut pac::gpdma::regs::ChTr2), + /// Function used to overwrite transfer register 3. + pub tr3: fn(&mut pac::gpdma::regs::ChTr3), +} + +impl Default for RegisterUpdaters { + fn default() -> Self { + Self { + tr1: |_| {}, + tr2: |_| {}, + tr3: |_| {}, + } + } +} + impl From for vals::Dw { fn from(raw: WordSize) -> Self { match raw { diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index c74c7bd2b..dfc031627 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs @@ -8,7 +8,7 @@ use core::task::Waker; use embassy_hal_internal::Peri; -use super::{AnyChannel, TransferOptions, STATE}; +use super::{AnyChannel, RegisterUpdaters, TransferOptions, STATE}; use crate::dma::gpdma::linked_list::{LinearItem, RunMode, Table}; use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use crate::dma::word::Word; @@ -52,6 +52,7 @@ pub struct ReadableRingBuffer<'a, W: Word> { channel: Peri<'a, AnyChannel>, ringbuf: ReadableDmaRingBuffer<'a, W>, table: Table<2>, + options: TransferOptions, } impl<'a, W: Word> ReadableRingBuffer<'a, W> { @@ -64,6 +65,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { peri_addr: *mut W, buffer: &'a mut [W], options: TransferOptions, + register_updaters: RegisterUpdaters, ) -> Self { let channel: Peri<'a, AnyChannel> = channel.into(); @@ -72,8 +74,8 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { assert_eq!(half_len * 2, buffer.len()); let items = [ - LinearItem::new_read(request, peri_addr, &mut buffer[..half_len]), - LinearItem::new_read(request, peri_addr, &mut buffer[half_len..]), + LinearItem::new_read(request, peri_addr, &mut buffer[..half_len], ®ister_updaters), + LinearItem::new_read(request, peri_addr, &mut buffer[half_len..], ®ister_updaters), ]; let table = Table::new(items); @@ -84,6 +86,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { channel, ringbuf: ReadableDmaRingBuffer::new(buffer), table, + options, } } @@ -211,6 +214,7 @@ pub struct WritableRingBuffer<'a, W: Word> { channel: Peri<'a, AnyChannel>, ringbuf: WritableDmaRingBuffer<'a, W>, table: Table<2>, + options: TransferOptions, } impl<'a, W: Word> WritableRingBuffer<'a, W> { @@ -221,6 +225,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { peri_addr: *mut W, buffer: &'a mut [W], options: TransferOptions, + register_updaters: RegisterUpdaters, ) -> Self { let channel: Peri<'a, AnyChannel> = channel.into(); @@ -229,8 +234,8 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { assert_eq!(half_len * 2, buffer.len()); let items = [ - LinearItem::new_write(request, &mut buffer[..half_len], peri_addr), - LinearItem::new_write(request, &mut buffer[half_len..], peri_addr), + LinearItem::new_write(request, &mut buffer[..half_len], peri_addr, ®ister_updaters), + LinearItem::new_write(request, &mut buffer[half_len..], peri_addr, ®ister_updaters), ]; let table = Table::new(items); @@ -241,6 +246,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { channel, ringbuf: WritableDmaRingBuffer::new(buffer), table, + options, }; this diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 88cc225dd..ac1ab2505 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -687,12 +687,13 @@ fn get_ring_buffer<'d, T: Instance, W: word::Word>( //the new_write() and new_read() always use circular mode ..Default::default() }; + let updaters = Default::default(); match tx_rx { TxRx::Transmitter => RingBuffer::Writable(unsafe { - WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts) + WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts, updaters) }), TxRx::Receiver => RingBuffer::Readable(unsafe { - ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts) + ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts, updaters) }), } } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index bea56c991..78bf4b72f 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -103,6 +103,7 @@ impl<'d> UartRx<'d, Async> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); let opts = Default::default(); + let updaters = Default::default(); // Safety: we forget the struct before this function returns. let rx_dma = self.rx_dma.as_mut().unwrap(); @@ -112,7 +113,7 @@ impl<'d> UartRx<'d, Async> { let info = self.info; let state = self.state; let kernel_clock = self.kernel_clock; - let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(info.regs), dma_buf, opts) }; + let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(info.regs), dma_buf, opts, updaters) }; let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; -- cgit