From 009bb8e4e1b7afbe9d9d7d89135f8d4dd3c4e808 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Apr 2022 23:57:26 +0200 Subject: stm32: add stm32u5 GPDMA, SPIv4 support, add HIL tests. --- embassy-stm32/src/dma/gpdma.rs | 293 +++++++++++++++++++++++++++++++++++++++++ embassy-stm32/src/dma/mod.rs | 29 +++- embassy-stm32/src/spi/mod.rs | 87 ++++++------ embassy-stm32/src/traits.rs | 14 +- 4 files changed, 374 insertions(+), 49 deletions(-) create mode 100644 embassy-stm32/src/dma/gpdma.rs (limited to 'embassy-stm32/src') diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs new file mode 100644 index 000000000..0cb986b32 --- /dev/null +++ b/embassy-stm32/src/dma/gpdma.rs @@ -0,0 +1,293 @@ +use core::sync::atomic::{fence, Ordering}; +use core::task::Waker; + +use embassy::interrupt::{Interrupt, InterruptExt}; +use embassy::waitqueue::AtomicWaker; + +use crate::_generated::GPDMA_CHANNEL_COUNT; +use crate::interrupt; +use crate::pac; +use crate::pac::gpdma::{vals, Gpdma}; + +use super::{Request, TransferOptions, Word, WordSize}; + +impl From for vals::ChTr1Dw { + fn from(raw: WordSize) -> Self { + match raw { + WordSize::OneByte => Self::BYTE, + WordSize::TwoBytes => Self::HALFWORD, + WordSize::FourBytes => Self::WORD, + } + } +} + +struct ChannelState { + waker: AtomicWaker, +} + +impl ChannelState { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } +} + +struct State { + channels: [ChannelState; GPDMA_CHANNEL_COUNT], +} + +impl State { + const fn new() -> Self { + const CH: ChannelState = ChannelState::new(); + Self { + channels: [CH; GPDMA_CHANNEL_COUNT], + } + } +} + +static STATE: State = State::new(); + +/// safety: must be called only once +pub(crate) unsafe fn init() { + foreach_interrupt! { + ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { + interrupt::$irq::steal().enable(); + }; + } + crate::_generated::init_gpdma(); +} + +foreach_dma_channel! { + ($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => { + impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { + unsafe fn start_write(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { + let (ptr, len) = super::slice_ptr_parts(buf); + low_level_api::start_transfer( + pac::$dma_peri, + $channel_num, + request, + low_level_api::Dir::MemoryToPeripheral, + reg_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { + let buf = [repeated]; + low_level_api::start_transfer( + pac::$dma_peri, + $channel_num, + request, + low_level_api::Dir::MemoryToPeripheral, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn start_read(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { + let (ptr, len) = super::slice_ptr_parts_mut(buf); + low_level_api::start_transfer( + pac::$dma_peri, + $channel_num, + request, + low_level_api::Dir::PeripheralToMemory, + reg_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ); + } + + unsafe fn start_double_buffered_read( + &mut self, + _request: Request, + _reg_addr: *const W, + _buffer0: *mut W, + _buffer1: *mut W, + _buffer_len: usize, + _options: TransferOptions, + ) { + panic!("Unsafe double buffered mode is unavailable on GPBDMA"); + } + + unsafe fn set_buffer0(&mut self, _buffer: *mut W) { + panic!("Unsafe double buffered mode is unavailable on GPBDMA"); + } + + unsafe fn set_buffer1(&mut self, _buffer: *mut W) { + panic!("Unsafe double buffered mode is unavailable on GPBDMA"); + } + + unsafe fn is_buffer0_accessible(&mut self) -> bool { + panic!("Unsafe double buffered mode is unavailable on GPBDMA"); + } + + fn request_stop(&mut self) { + unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} + } + + fn is_running(&self) -> bool { + unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} + } + + fn remaining_transfers(&mut self) -> u16 { + unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} + } + + fn set_waker(&mut self, waker: &Waker) { + unsafe {low_level_api::set_waker($index, waker )} + } + + fn on_irq() { + unsafe { + low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); + } + } + } + impl crate::dma::Channel for crate::peripherals::$channel_peri { } + }; +} + +mod low_level_api { + use super::*; + + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum Dir { + MemoryToPeripheral, + PeripheralToMemory, + } + + pub unsafe fn start_transfer( + dma: Gpdma, + channel_number: u8, + request: Request, + dir: Dir, + peri_addr: *const u32, + mem_addr: *mut u32, + mem_len: usize, + incr_mem: bool, + data_size: WordSize, + _options: TransferOptions, + ) { + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let ch = dma.ch(channel_number as _); + ch.llr().write(|_| {}); // no linked list + ch.tr1().write(|w| { + w.set_sdw(data_size.into()); + w.set_ddw(data_size.into()); + w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); + w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); + }); + ch.tr2().write(|w| { + w.set_dreq(match dir { + Dir::MemoryToPeripheral => vals::ChTr2Dreq::DESTINATIONPERIPHERAL, + Dir::PeripheralToMemory => vals::ChTr2Dreq::SOURCEPERIPHERAL, + }); + w.set_reqsel(request); + }); + ch.br1().write(|w| { + // BNDT is specified as bytes, not as number of transfers. + w.set_bndt((mem_len * data_size.bytes()) as u16) + }); + + match dir { + Dir::MemoryToPeripheral => { + ch.sar().write_value(mem_addr as _); + ch.dar().write_value(peri_addr as _); + } + Dir::PeripheralToMemory => { + ch.sar().write_value(peri_addr as _); + ch.dar().write_value(mem_addr as _); + } + } + + ch.cr().write(|w| { + // Enable interrupts + w.set_tcie(true); + w.set_useie(true); + w.set_dteie(true); + w.set_suspie(true); + + // Start it + w.set_en(true); + }); + } + + /// Stops the DMA channel. + pub unsafe fn request_stop(dma: Gpdma, channel_number: u8) { + // get a handle on the channel itself + let ch = dma.ch(channel_number as _); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + ch.cr().write(|w| { + w.set_tcie(true); + w.set_useie(true); + w.set_dteie(true); + w.set_suspie(true); + }); + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } + + /// Gets the running status of the channel + pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { + let ch = dma.ch(ch as _); + !ch.sr().read().idlef() + } + + /// Gets the total remaining transfers for the channel + /// Note: this will be zero for transfers that completed without cancellation. + pub unsafe fn get_remaining_transfers(dma: Gpdma, ch: u8) -> u16 { + // get a handle on the channel itself + let ch = dma.ch(ch as _); + // read the remaining transfer count. If this is zero, the transfer completed fully. + ch.br1().read().bndt() + } + + /// Sets the waker for the specified DMA channel + pub unsafe fn set_waker(state_number: usize, waker: &Waker) { + STATE.channels[state_number].waker.register(waker); + } + + /// Safety: Must be called with a matching set of parameters for a valid dma channel + pub unsafe fn on_irq_inner(dma: Gpdma, channel_num: u8, state_index: u8) { + let channel_num = channel_num as usize; + let state_index = state_index as usize; + + let ch = dma.ch(channel_num); + let sr = ch.sr().read(); + + if sr.dtef() { + panic!( + "DMA: data transfer error on DMA@{:08x} channel {}", + dma.0 as u32, channel_num + ); + } + if sr.usef() { + panic!( + "DMA: user settings error on DMA@{:08x} channel {}", + dma.0 as u32, channel_num + ); + } + + if sr.suspf() || sr.tcf() { + ch.cr().write(|w| w.set_reset(true)); + STATE.channels[state_index].waker.wake(); + } + } +} diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index f96ccbf6e..c19f7b3c7 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -4,6 +4,8 @@ pub(crate) mod bdma; pub(crate) mod dma; #[cfg(dmamux)] mod dmamux; +#[cfg(gpdma)] +mod gpdma; #[cfg(dmamux)] pub use dmamux::*; @@ -24,9 +26,9 @@ pub mod low_level { pub(crate) use transfers::*; -#[cfg(any(bdma_v2, dma_v2, dmamux))] +#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))] pub type Request = u8; -#[cfg(not(any(bdma_v2, dma_v2, dmamux)))] +#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))] pub type Request = (); pub(crate) mod sealed { @@ -118,11 +120,24 @@ pub(crate) mod sealed { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum WordSize { OneByte, TwoBytes, FourBytes, } + +impl WordSize { + pub fn bytes(&self) -> usize { + match self { + Self::OneByte => 1, + Self::TwoBytes => 2, + Self::FourBytes => 4, + } + } +} + pub trait Word: sealed::Word { fn bits() -> WordSize; } @@ -148,7 +163,8 @@ impl Word for u32 { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Burst { /// Single transfer Single, @@ -160,7 +176,8 @@ pub enum Burst { Incr16, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FlowControl { /// Flow control by DMA Dma, @@ -168,6 +185,8 @@ pub enum FlowControl { Peripheral, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TransferOptions { /// Peripheral burst transfer configuration pub pburst: Burst, @@ -299,6 +318,8 @@ pub(crate) unsafe fn init() { dma::init(); #[cfg(dmamux)] dmamux::init(); + #[cfg(gpdma)] + gpdma::init(); } // TODO: replace transmutes with core::ptr::metadata once it's stable diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index ea0c496db..23240ad82 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -100,13 +100,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { unborrow!(sck, mosi, miso); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] mosi.set_speed(crate::gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] miso.set_speed(crate::gpio::Speed::VeryHigh); } @@ -137,10 +137,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { unborrow!(sck, miso); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] miso.set_speed(crate::gpio::Speed::VeryHigh); } @@ -171,10 +171,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { unborrow!(sck, mosi); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v2, spi_v3, spi_v4))] mosi.set_speed(crate::gpio::Speed::VeryHigh); } @@ -260,7 +260,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(true); }); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] unsafe { T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff); T::REGS.cfg2().modify(|w| { @@ -285,7 +285,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); T::REGS.cr2().modify(|w| { w.set_tsize(0); - w.set_tser(0); }); T::REGS.cr1().modify(|w| { w.set_ssi(false); @@ -320,7 +319,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] unsafe { T::REGS.cfg2().modify(|w| { w.set_cpha(cpha); @@ -333,7 +332,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn get_current_config(&self) -> Config { #[cfg(any(spi_v1, spi_f1, spi_v2))] let cfg = unsafe { T::REGS.cr1().read() }; - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] let cfg = unsafe { T::REGS.cfg2().read() }; let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow @@ -386,7 +385,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(true); }); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] unsafe { T::REGS.cr1().modify(|w| { w.set_csusp(true); @@ -435,7 +434,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -466,7 +465,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] flush_rx_fifo(T::REGS); let clock_byte_count = data.len(); @@ -495,7 +494,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -533,7 +532,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] flush_rx_fifo(T::REGS); let rx_request = self.rxdma.request(); @@ -557,7 +556,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -642,9 +641,9 @@ impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { } } -#[cfg(not(spi_v3))] +#[cfg(not(any(spi_v3, spi_v4)))] use vals::Br; -#[cfg(spi_v3)] +#[cfg(any(spi_v3, spi_v4))] use vals::Mbr as Br; fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { @@ -670,17 +669,17 @@ trait RegsExt { impl RegsExt for Regs { fn tx_ptr(&self) -> *mut W { - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] let dr = self.dr(); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] let dr = self.txdr(); dr.ptr() as *mut W } fn rx_ptr(&self) -> *mut W { - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] let dr = self.dr(); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] let dr = self.rxdr(); dr.ptr() as *mut W } @@ -690,22 +689,22 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { if sr.ovr() { return Err(Error::Overrun); } - #[cfg(not(any(spi_f1, spi_v3)))] + #[cfg(not(any(spi_f1, spi_v3, spi_v4)))] if sr.fre() { return Err(Error::Framing); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] if sr.tifre() { return Err(Error::Framing); } if sr.modf() { return Err(Error::ModeFault); } - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] if sr.crcerr() { return Err(Error::Crc); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] if sr.crce() { return Err(Error::Crc); } @@ -719,11 +718,11 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { check_error_flags(sr)?; - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] if sr.txe() { return Ok(()); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] if sr.txp() { return Ok(()); } @@ -736,11 +735,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { check_error_flags(sr)?; - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] if sr.rxne() { return Ok(()); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] if sr.rxp() { return Ok(()); } @@ -749,11 +748,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { fn flush_rx_fifo(regs: Regs) { unsafe { - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] while regs.sr().read().rxne() { let _ = regs.dr().read(); } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] while regs.sr().read().rxp() { let _ = regs.rxdr().read(); } @@ -762,11 +761,11 @@ fn flush_rx_fifo(regs: Regs) { fn set_txdmaen(regs: Regs, val: bool) { unsafe { - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] regs.cr2().modify(|reg| { reg.set_txdmaen(val); }); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] regs.cfg1().modify(|reg| { reg.set_txdmaen(val); }); @@ -775,11 +774,11 @@ fn set_txdmaen(regs: Regs, val: bool) { fn set_rxdmaen(regs: Regs, val: bool) { unsafe { - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] regs.cr2().modify(|reg| { reg.set_rxdmaen(val); }); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] regs.cfg1().modify(|reg| { reg.set_rxdmaen(val); }); @@ -791,21 +790,21 @@ fn finish_dma(regs: Regs) { #[cfg(spi_v2)] while regs.sr().read().ftlvl() > 0 {} - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] while !regs.sr().read().txc() {} - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] while regs.sr().read().bsy() {} regs.cr1().modify(|w| { w.set_spe(false); }); - #[cfg(not(spi_v3))] + #[cfg(not(any(spi_v3, spi_v4)))] regs.cr2().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); }); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] regs.cfg1().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); @@ -819,7 +818,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { unsafe { ptr::write_volatile(regs.tx_ptr(), tx_word); - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] regs.cr1().modify(|reg| reg.set_cstart(true)); } @@ -1018,7 +1017,7 @@ pub(crate) mod sealed { } } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] pub fn dsize(&self) -> u8 { match self { WordSize::EightBit => 0b0111, @@ -1026,7 +1025,7 @@ pub(crate) mod sealed { } } - #[cfg(spi_v3)] + #[cfg(any(spi_v3, spi_v4))] pub fn _frxth(&self) -> vals::Fthlv { match self { WordSize::EightBit => vals::Fthlv::ONEFRAME, diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/traits.rs index c733f4226..45cc4e725 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/traits.rs @@ -42,7 +42,19 @@ macro_rules! dma_trait_impl { } }; - // No DMAMUX + // DMAMUX + (crate::$mod:ident::$trait:ident, $instance:ident, {dma: $dma:ident}, $request:expr) => { + impl crate::$mod::$trait for T + where + T: crate::dma::Channel, + { + fn request(&self) -> crate::dma::Request { + $request + } + } + }; + + // DMA/GPDMA, without DMAMUX (crate::$mod:ident::$trait:ident, $instance:ident, {channel: $channel:ident}, $request:expr) => { impl crate::$mod::$trait for crate::peripherals::$channel { fn request(&self) -> crate::dma::Request { -- cgit