734: executor: Add `Spawner::for_current_executor`. r=Dirbaio a=Dirbaio

This is needed to spawn non-Send tasks in an InterruptExecutor, after the fixes in #730 .

`@matoushybl` could you check if this works for your use case?

735: stm32: add stm32u5 GPDMA, SPIv4 support, add HIL tests. r=Dirbaio a=Dirbaio



Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
bors[bot] 2022-04-26 23:32:30 +00:00 committed by GitHub
commit df814f9bbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 532 additions and 63 deletions

1
ci.sh
View File

@ -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 {

View File

@ -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 {

View File

@ -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<WordSize> 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<W: Word>(&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<W: Word>(&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<W: Word>(&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<W: Word>(
&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<W: Word>(&mut self, _buffer: *mut W) {
panic!("Unsafe double buffered mode is unavailable on GPBDMA");
}
unsafe fn set_buffer1<W: Word>(&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();
}
}
}

View File

@ -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

View File

@ -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<W>(&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<W>(&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<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
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,

View File

@ -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<T> crate::$mod::$trait<crate::peripherals::$instance> 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<crate::peripherals::$instance> for crate::peripherals::$channel {
fn request(&self) -> crate::dma::Request {

View File

@ -115,6 +115,9 @@ impl<I: Interrupt> InterruptExecutor<I> {
/// 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:

View File

@ -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<F>(&self, token: SpawnToken<F>) {
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<F: Send>(&self, token: SpawnToken<F>) {
unwrap!(self.spawn(token));
}
}

View File

@ -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)));
}

View File

@ -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;
}
}

@ -1 +1 @@
Subproject commit 8fb98d74620ab71fb9d0be2d800c427e0b77c46f
Subproject commit 472ee98e8fdb11312392e47b16568c9d02fe6549

View File

@ -104,6 +104,7 @@ pub struct PeripheralDmaChannel {
pub signal: String,
pub channel: Option<String>,
pub dmamux: Option<String>,
pub dma: Option<String>,
pub request: Option<u32>,
}

View File

@ -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<u32>,
}

View File

@ -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"] }

View File

@ -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
{

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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);