diff --git a/ci.sh b/ci.sh index 116b6a31..69fa0804 100755 --- a/ci.sh +++ b/ci.sh @@ -88,6 +88,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ function run_elf { diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9125155c..aa3e6413 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -109,7 +109,7 @@ fn main() { for p in METADATA.peripherals { if let Some(r) = &p.registers { - if r.kind == "dma" || r.kind == "bdma" { + if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" { if p.name == "BDMA1" { // BDMA1 in H7 doesn't use DMAMUX, which breaks continue; @@ -214,7 +214,7 @@ fn main() { // ======== // Generate fns to enable GPIO, DMA in RCC - for kind in ["dma", "bdma", "dmamux", "gpio"] { + for kind in ["dma", "bdma", "dmamux", "gpdma", "gpio"] { let mut gg = TokenStream::new(); for p in METADATA.peripherals { @@ -528,11 +528,17 @@ fn main() { let peri = format_ident!("{}", p.name); let channel = if let Some(channel) = &ch.channel { + // Chip with DMA/BDMA, without DMAMUX let channel = format_ident!("{}", channel); quote!({channel: #channel}) } else if let Some(dmamux) = &ch.dmamux { + // Chip with DMAMUX let dmamux = format_ident!("{}", dmamux); quote!({dmamux: #dmamux}) + } else if let Some(dma) = &ch.dma { + // Chip with GPDMA + let dma = format_ident!("{}", dma); + quote!({dma: #dma}) } else { unreachable!(); }; @@ -606,6 +612,7 @@ fn main() { let mut dma_channel_count: usize = 0; let mut bdma_channel_count: usize = 0; + let mut gpdma_channel_count: usize = 0; for ch in METADATA.dma_channels { let mut row = Vec::new(); @@ -626,6 +633,10 @@ fn main() { num = bdma_channel_count; bdma_channel_count += 1; } + "gpdma" => { + num = gpdma_channel_count; + gpdma_channel_count += 1; + } _ => panic!("bad dma channel kind {}", bi.kind), } @@ -650,6 +661,7 @@ fn main() { g.extend(quote! { pub(crate) const DMA_CHANNEL_COUNT: usize = #dma_channel_count; pub(crate) const BDMA_CHANNEL_COUNT: usize = #bdma_channel_count; + pub(crate) const GPDMA_CHANNEL_COUNT: usize = #gpdma_channel_count; }); for irq in METADATA.interrupts { diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs new file mode 100644 index 00000000..0cb986b3 --- /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 f96ccbf6..c19f7b3c 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 ea0c496d..23240ad8 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 c733f422..45cc4e72 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 { diff --git a/embassy/src/executor/arch/cortex_m.rs b/embassy/src/executor/arch/cortex_m.rs index 87c9c3c8..16f29008 100644 --- a/embassy/src/executor/arch/cortex_m.rs +++ b/embassy/src/executor/arch/cortex_m.rs @@ -115,6 +115,9 @@ impl InterruptExecutor { /// different "thread" (the interrupt), so spawning tasks on it is effectively /// sending them. /// + /// To obtain a [`Spawner`] for this executor, use [`Spawner::for_current_executor`] from + /// a task running in it. + /// /// This function requires `&'static mut self`. This means you have to store the /// Executor instance in a place where it'll live forever and grants you mutable /// access. There's a few ways to do this: diff --git a/embassy/src/executor/spawner.rs b/embassy/src/executor/spawner.rs index d1e4ff17..e6770e29 100644 --- a/embassy/src/executor/spawner.rs +++ b/embassy/src/executor/spawner.rs @@ -1,6 +1,8 @@ use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; +use core::task::Poll; +use futures::future::poll_fn; use super::raw; @@ -75,6 +77,23 @@ impl Spawner { } } + /// Get a Spawner for the current executor. + /// + /// This function is `async` just to get access to the current async + /// context. It returns instantly, it does not block/yield. + /// + /// # Panics + /// + /// Panics if the current executor is not an Embassy executor. + pub async fn for_current_executor() -> Self { + poll_fn(|cx| unsafe { + let task = raw::task_from_waker(cx.waker()); + let executor = (&*task.as_ptr()).executor.get(); + Poll::Ready(Self::new(&*executor)) + }) + .await + } + /// Spawn a task into an executor. /// /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`). @@ -91,10 +110,15 @@ impl Spawner { } } - /// Used by the `embassy_macros::main!` macro to throw an error when spawn - /// fails. This is here to allow conditional use of `defmt::unwrap!` - /// without introducing a `defmt` feature in the `embassy_macros` package, - /// which would require use of `-Z namespaced-features`. + // Used by the `embassy_macros::main!` macro to throw an error when spawn + // fails. This is here to allow conditional use of `defmt::unwrap!` + // without introducing a `defmt` feature in the `embassy_macros` package, + // which would require use of `-Z namespaced-features`. + /// Spawn a task into an executor, panicking on failure. + /// + /// # Panics + /// + /// Panics if the spawning fails. pub fn must_spawn(&self, token: SpawnToken) { unwrap!(self.spawn(token)); } @@ -125,6 +149,27 @@ unsafe impl Send for SendSpawner {} unsafe impl Sync for SendSpawner {} impl SendSpawner { + pub(crate) fn new(executor: &'static raw::Executor) -> Self { + Self { executor } + } + + /// Get a Spawner for the current executor. + /// + /// This function is `async` just to get access to the current async + /// context. It returns instantly, it does not block/yield. + /// + /// # Panics + /// + /// Panics if the current executor is not an Embassy executor. + pub async fn for_current_executor() -> Self { + poll_fn(|cx| unsafe { + let task = raw::task_from_waker(cx.waker()); + let executor = (&*task.as_ptr()).executor.get(); + Poll::Ready(Self::new(&*executor)) + }) + .await + } + /// Spawn a task into an executor. /// /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`). @@ -140,4 +185,13 @@ impl SendSpawner { None => Err(SpawnError::Busy), } } + + /// Spawn a task into an executor, panicking on failure. + /// + /// # Panics + /// + /// Panics if the spawning fails. + pub fn must_spawn(&self, token: SpawnToken) { + unwrap!(self.spawn(token)); + } } diff --git a/examples/nrf/src/bin/self_spawn_current_executor.rs b/examples/nrf/src/bin/self_spawn_current_executor.rs new file mode 100644 index 00000000..4850d295 --- /dev/null +++ b/examples/nrf/src/bin/self_spawn_current_executor.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_nrf::Peripherals; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +#[embassy::task(pool_size = 2)] +async fn my_task(n: u32) { + Timer::after(Duration::from_secs(1)).await; + info!("Spawning self! {}", n); + unwrap!(Spawner::for_current_executor().await.spawn(my_task(n + 1))); +} + +#[embassy::main] +async fn main(spawner: Spawner, _p: Peripherals) { + info!("Hello World!"); + unwrap!(spawner.spawn(my_task(0))); +} diff --git a/examples/stm32u5/src/bin/blinky.rs b/examples/stm32u5/src/bin/blinky.rs new file mode 100644 index 00000000..e1bcccf5 --- /dev/null +++ b/examples/stm32u5/src/bin/blinky.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use defmt_rtt as _; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::Peripherals; +// global logger +use panic_probe as _; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) -> ! { + info!("Hello World!"); + + let mut led = Output::new(p.PH7, Level::Low, Speed::Medium); + + loop { + defmt::info!("on!"); + led.set_low(); + Timer::after(Duration::from_millis(200)).await; + + defmt::info!("off!"); + led.set_high(); + Timer::after(Duration::from_millis(200)).await; + } +} diff --git a/stm32-data b/stm32-data index 8fb98d74..472ee98e 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 8fb98d74620ab71fb9d0be2d800c427e0b77c46f +Subproject commit 472ee98e8fdb11312392e47b16568c9d02fe6549 diff --git a/stm32-metapac-gen/src/data.rs b/stm32-metapac-gen/src/data.rs index 24f0bcf0..a74c60ac 100644 --- a/stm32-metapac-gen/src/data.rs +++ b/stm32-metapac-gen/src/data.rs @@ -104,6 +104,7 @@ pub struct PeripheralDmaChannel { pub signal: String, pub channel: Option, pub dmamux: Option, + pub dma: Option, pub request: Option, } diff --git a/stm32-metapac/src/metadata.rs b/stm32-metapac/src/metadata.rs index 150aa595..23b759f6 100644 --- a/stm32-metapac/src/metadata.rs +++ b/stm32-metapac/src/metadata.rs @@ -93,5 +93,6 @@ pub struct PeripheralDmaChannel { pub signal: &'static str, pub channel: Option<&'static str>, pub dmamux: Option<&'static str>, + pub dma: Option<&'static str>, pub request: Option, } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 260ac223..9bd08761 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -6,12 +6,13 @@ version = "0.1.0" resolver = "2" [features] -stm32f103c8 = ["embassy-stm32/stm32f103c8"] -stm32f429zi = ["embassy-stm32/stm32f429zi"] -stm32g071rb = ["embassy-stm32/stm32g071rb"] -stm32g491re = ["embassy-stm32/stm32g491re"] -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] -stm32wb55rg = ["embassy-stm32/stm32wb55rg"] +stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill +stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo +stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo +stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo +stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 41c10d45..c7991953 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -28,6 +28,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { let (mut a, mut b) = (p.PA3, p.PA2); #[cfg(feature = "stm32h755zi")] let (mut a, mut b) = (p.PB6, p.PB7); + #[cfg(feature = "stm32u585ai")] + let (mut a, mut b) = (p.PD9, p.PD8); // Test initial output { diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index b079472d..049ba1e9 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -16,10 +16,20 @@ use example_common::*; async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); - #[cfg(not(feature = "stm32h755zi"))] + #[cfg(feature = "stm32f103c8")] + let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + #[cfg(feature = "stm32f429zi")] let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32h755zi")] let (sck, mosi, miso) = (p.PA5, p.PB5, p.PA6); + #[cfg(feature = "stm32g491re")] + let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + #[cfg(feature = "stm32g071rb")] + let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + #[cfg(feature = "stm32wb55rg")] + let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + #[cfg(feature = "stm32u585ai")] + let (sck, mosi, miso) = (p.PE13, p.PE15, p.PE14); let mut spi = Spi::new( p.SPI1, diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index f4f1994c..64337886 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -27,6 +27,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32wb55rg")] let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + #[cfg(feature = "stm32u585ai")] + let (sck, mosi, miso, tx_dma, rx_dma) = (p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); let mut spi = Spi::new( p.SPI1, diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 87a593ca..c3468290 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -35,6 +35,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { let (tx, rx, usart) = (p.PA9, p.PA10, p.USART1); // TODO this is wrong #[cfg(feature = "stm32h755zi")] let (tx, rx, usart) = (p.PB6, p.PB7, p.USART1); + #[cfg(feature = "stm32u585ai")] + let (tx, rx, usart) = (p.PD8, p.PD9, p.USART3); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, NoDma, NoDma, config); diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 2565409e..9946f4a5 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -34,6 +34,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { let (tx, rx, usart, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, p.DMA1_CH1, p.DMA1_CH2); // TODO this is wrong #[cfg(feature = "stm32h755zi")] let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1); + #[cfg(feature = "stm32u585ai")] + let (tx, rx, usart, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, p.GPDMA1_CH0, p.GPDMA1_CH1); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, config);