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:
commit
df814f9bbd
1
ci.sh
1
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 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 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 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 {
|
function run_elf {
|
||||||
|
@ -109,7 +109,7 @@ fn main() {
|
|||||||
|
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
if let Some(r) = &p.registers {
|
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" {
|
if p.name == "BDMA1" {
|
||||||
// BDMA1 in H7 doesn't use DMAMUX, which breaks
|
// BDMA1 in H7 doesn't use DMAMUX, which breaks
|
||||||
continue;
|
continue;
|
||||||
@ -214,7 +214,7 @@ fn main() {
|
|||||||
// ========
|
// ========
|
||||||
// Generate fns to enable GPIO, DMA in RCC
|
// 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();
|
let mut gg = TokenStream::new();
|
||||||
|
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
@ -528,11 +528,17 @@ fn main() {
|
|||||||
let peri = format_ident!("{}", p.name);
|
let peri = format_ident!("{}", p.name);
|
||||||
|
|
||||||
let channel = if let Some(channel) = &ch.channel {
|
let channel = if let Some(channel) = &ch.channel {
|
||||||
|
// Chip with DMA/BDMA, without DMAMUX
|
||||||
let channel = format_ident!("{}", channel);
|
let channel = format_ident!("{}", channel);
|
||||||
quote!({channel: #channel})
|
quote!({channel: #channel})
|
||||||
} else if let Some(dmamux) = &ch.dmamux {
|
} else if let Some(dmamux) = &ch.dmamux {
|
||||||
|
// Chip with DMAMUX
|
||||||
let dmamux = format_ident!("{}", dmamux);
|
let dmamux = format_ident!("{}", dmamux);
|
||||||
quote!({dmamux: #dmamux})
|
quote!({dmamux: #dmamux})
|
||||||
|
} else if let Some(dma) = &ch.dma {
|
||||||
|
// Chip with GPDMA
|
||||||
|
let dma = format_ident!("{}", dma);
|
||||||
|
quote!({dma: #dma})
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
@ -606,6 +612,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut dma_channel_count: usize = 0;
|
let mut dma_channel_count: usize = 0;
|
||||||
let mut bdma_channel_count: usize = 0;
|
let mut bdma_channel_count: usize = 0;
|
||||||
|
let mut gpdma_channel_count: usize = 0;
|
||||||
|
|
||||||
for ch in METADATA.dma_channels {
|
for ch in METADATA.dma_channels {
|
||||||
let mut row = Vec::new();
|
let mut row = Vec::new();
|
||||||
@ -626,6 +633,10 @@ fn main() {
|
|||||||
num = bdma_channel_count;
|
num = bdma_channel_count;
|
||||||
bdma_channel_count += 1;
|
bdma_channel_count += 1;
|
||||||
}
|
}
|
||||||
|
"gpdma" => {
|
||||||
|
num = gpdma_channel_count;
|
||||||
|
gpdma_channel_count += 1;
|
||||||
|
}
|
||||||
_ => panic!("bad dma channel kind {}", bi.kind),
|
_ => panic!("bad dma channel kind {}", bi.kind),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,6 +661,7 @@ fn main() {
|
|||||||
g.extend(quote! {
|
g.extend(quote! {
|
||||||
pub(crate) const DMA_CHANNEL_COUNT: usize = #dma_channel_count;
|
pub(crate) const DMA_CHANNEL_COUNT: usize = #dma_channel_count;
|
||||||
pub(crate) const BDMA_CHANNEL_COUNT: usize = #bdma_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 {
|
for irq in METADATA.interrupts {
|
||||||
|
293
embassy-stm32/src/dma/gpdma.rs
Normal file
293
embassy-stm32/src/dma/gpdma.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,8 @@ pub(crate) mod bdma;
|
|||||||
pub(crate) mod dma;
|
pub(crate) mod dma;
|
||||||
#[cfg(dmamux)]
|
#[cfg(dmamux)]
|
||||||
mod dmamux;
|
mod dmamux;
|
||||||
|
#[cfg(gpdma)]
|
||||||
|
mod gpdma;
|
||||||
|
|
||||||
#[cfg(dmamux)]
|
#[cfg(dmamux)]
|
||||||
pub use dmamux::*;
|
pub use dmamux::*;
|
||||||
@ -24,9 +26,9 @@ pub mod low_level {
|
|||||||
|
|
||||||
pub(crate) use transfers::*;
|
pub(crate) use transfers::*;
|
||||||
|
|
||||||
#[cfg(any(bdma_v2, dma_v2, dmamux))]
|
#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))]
|
||||||
pub type Request = u8;
|
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 type Request = ();
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
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 {
|
pub enum WordSize {
|
||||||
OneByte,
|
OneByte,
|
||||||
TwoBytes,
|
TwoBytes,
|
||||||
FourBytes,
|
FourBytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WordSize {
|
||||||
|
pub fn bytes(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::OneByte => 1,
|
||||||
|
Self::TwoBytes => 2,
|
||||||
|
Self::FourBytes => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Word: sealed::Word {
|
pub trait Word: sealed::Word {
|
||||||
fn bits() -> WordSize;
|
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 {
|
pub enum Burst {
|
||||||
/// Single transfer
|
/// Single transfer
|
||||||
Single,
|
Single,
|
||||||
@ -160,7 +176,8 @@ pub enum Burst {
|
|||||||
Incr16,
|
Incr16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum FlowControl {
|
pub enum FlowControl {
|
||||||
/// Flow control by DMA
|
/// Flow control by DMA
|
||||||
Dma,
|
Dma,
|
||||||
@ -168,6 +185,8 @@ pub enum FlowControl {
|
|||||||
Peripheral,
|
Peripheral,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct TransferOptions {
|
pub struct TransferOptions {
|
||||||
/// Peripheral burst transfer configuration
|
/// Peripheral burst transfer configuration
|
||||||
pub pburst: Burst,
|
pub pburst: Burst,
|
||||||
@ -299,6 +318,8 @@ pub(crate) unsafe fn init() {
|
|||||||
dma::init();
|
dma::init();
|
||||||
#[cfg(dmamux)]
|
#[cfg(dmamux)]
|
||||||
dmamux::init();
|
dmamux::init();
|
||||||
|
#[cfg(gpdma)]
|
||||||
|
gpdma::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace transmutes with core::ptr::metadata once it's stable
|
// TODO: replace transmutes with core::ptr::metadata once it's stable
|
||||||
|
@ -100,13 +100,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
unborrow!(sck, mosi, miso);
|
unborrow!(sck, mosi, miso);
|
||||||
unsafe {
|
unsafe {
|
||||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
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);
|
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
|
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);
|
mosi.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
miso.set_as_af(miso.af_num(), AFType::Input);
|
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);
|
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);
|
unborrow!(sck, miso);
|
||||||
unsafe {
|
unsafe {
|
||||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
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);
|
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
miso.set_as_af(miso.af_num(), AFType::Input);
|
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);
|
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);
|
unborrow!(sck, mosi);
|
||||||
unsafe {
|
unsafe {
|
||||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
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);
|
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
|
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);
|
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);
|
w.set_spe(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
unsafe {
|
unsafe {
|
||||||
T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff);
|
T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff);
|
||||||
T::REGS.cfg2().modify(|w| {
|
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| {
|
T::REGS.cr2().modify(|w| {
|
||||||
w.set_tsize(0);
|
w.set_tsize(0);
|
||||||
w.set_tser(0);
|
|
||||||
});
|
});
|
||||||
T::REGS.cr1().modify(|w| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_ssi(false);
|
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 {
|
unsafe {
|
||||||
T::REGS.cfg2().modify(|w| {
|
T::REGS.cfg2().modify(|w| {
|
||||||
w.set_cpha(cpha);
|
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 {
|
pub fn get_current_config(&self) -> Config {
|
||||||
#[cfg(any(spi_v1, spi_f1, spi_v2))]
|
#[cfg(any(spi_v1, spi_f1, spi_v2))]
|
||||||
let cfg = unsafe { T::REGS.cr1().read() };
|
let cfg = unsafe { T::REGS.cr1().read() };
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
let cfg = unsafe { T::REGS.cfg2().read() };
|
let cfg = unsafe { T::REGS.cfg2().read() };
|
||||||
let polarity = if cfg.cpol() == vals::Cpol::IDLELOW {
|
let polarity = if cfg.cpol() == vals::Cpol::IDLELOW {
|
||||||
Polarity::IdleLow
|
Polarity::IdleLow
|
||||||
@ -386,7 +385,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
w.set_spe(true);
|
w.set_spe(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
unsafe {
|
unsafe {
|
||||||
T::REGS.cr1().modify(|w| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_csusp(true);
|
w.set_csusp(true);
|
||||||
@ -435,7 +434,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
|||||||
T::REGS.cr1().modify(|w| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_spe(true);
|
w.set_spe(true);
|
||||||
});
|
});
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
T::REGS.cr1().modify(|w| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_cstart(true);
|
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
|
// SPIv3 clears rxfifo on SPE=0
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
flush_rx_fifo(T::REGS);
|
flush_rx_fifo(T::REGS);
|
||||||
|
|
||||||
let clock_byte_count = data.len();
|
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| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_spe(true);
|
w.set_spe(true);
|
||||||
});
|
});
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
T::REGS.cr1().modify(|w| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_cstart(true);
|
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
|
// SPIv3 clears rxfifo on SPE=0
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
flush_rx_fifo(T::REGS);
|
flush_rx_fifo(T::REGS);
|
||||||
|
|
||||||
let rx_request = self.rxdma.request();
|
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| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_spe(true);
|
w.set_spe(true);
|
||||||
});
|
});
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
T::REGS.cr1().modify(|w| {
|
T::REGS.cr1().modify(|w| {
|
||||||
w.set_cstart(true);
|
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;
|
use vals::Br;
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
use vals::Mbr as Br;
|
use vals::Mbr as Br;
|
||||||
|
|
||||||
fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br {
|
fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br {
|
||||||
@ -670,17 +669,17 @@ trait RegsExt {
|
|||||||
|
|
||||||
impl RegsExt for Regs {
|
impl RegsExt for Regs {
|
||||||
fn tx_ptr<W>(&self) -> *mut W {
|
fn tx_ptr<W>(&self) -> *mut W {
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
let dr = self.dr();
|
let dr = self.dr();
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
let dr = self.txdr();
|
let dr = self.txdr();
|
||||||
dr.ptr() as *mut W
|
dr.ptr() as *mut W
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rx_ptr<W>(&self) -> *mut W {
|
fn rx_ptr<W>(&self) -> *mut W {
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
let dr = self.dr();
|
let dr = self.dr();
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
let dr = self.rxdr();
|
let dr = self.rxdr();
|
||||||
dr.ptr() as *mut W
|
dr.ptr() as *mut W
|
||||||
}
|
}
|
||||||
@ -690,22 +689,22 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> {
|
|||||||
if sr.ovr() {
|
if sr.ovr() {
|
||||||
return Err(Error::Overrun);
|
return Err(Error::Overrun);
|
||||||
}
|
}
|
||||||
#[cfg(not(any(spi_f1, spi_v3)))]
|
#[cfg(not(any(spi_f1, spi_v3, spi_v4)))]
|
||||||
if sr.fre() {
|
if sr.fre() {
|
||||||
return Err(Error::Framing);
|
return Err(Error::Framing);
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
if sr.tifre() {
|
if sr.tifre() {
|
||||||
return Err(Error::Framing);
|
return Err(Error::Framing);
|
||||||
}
|
}
|
||||||
if sr.modf() {
|
if sr.modf() {
|
||||||
return Err(Error::ModeFault);
|
return Err(Error::ModeFault);
|
||||||
}
|
}
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
if sr.crcerr() {
|
if sr.crcerr() {
|
||||||
return Err(Error::Crc);
|
return Err(Error::Crc);
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
if sr.crce() {
|
if sr.crce() {
|
||||||
return Err(Error::Crc);
|
return Err(Error::Crc);
|
||||||
}
|
}
|
||||||
@ -719,11 +718,11 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> {
|
|||||||
|
|
||||||
check_error_flags(sr)?;
|
check_error_flags(sr)?;
|
||||||
|
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
if sr.txe() {
|
if sr.txe() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
if sr.txp() {
|
if sr.txp() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -736,11 +735,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
|
|||||||
|
|
||||||
check_error_flags(sr)?;
|
check_error_flags(sr)?;
|
||||||
|
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
if sr.rxne() {
|
if sr.rxne() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
if sr.rxp() {
|
if sr.rxp() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -749,11 +748,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
|
|||||||
|
|
||||||
fn flush_rx_fifo(regs: Regs) {
|
fn flush_rx_fifo(regs: Regs) {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
while regs.sr().read().rxne() {
|
while regs.sr().read().rxne() {
|
||||||
let _ = regs.dr().read();
|
let _ = regs.dr().read();
|
||||||
}
|
}
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
while regs.sr().read().rxp() {
|
while regs.sr().read().rxp() {
|
||||||
let _ = regs.rxdr().read();
|
let _ = regs.rxdr().read();
|
||||||
}
|
}
|
||||||
@ -762,11 +761,11 @@ fn flush_rx_fifo(regs: Regs) {
|
|||||||
|
|
||||||
fn set_txdmaen(regs: Regs, val: bool) {
|
fn set_txdmaen(regs: Regs, val: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
regs.cr2().modify(|reg| {
|
regs.cr2().modify(|reg| {
|
||||||
reg.set_txdmaen(val);
|
reg.set_txdmaen(val);
|
||||||
});
|
});
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
regs.cfg1().modify(|reg| {
|
regs.cfg1().modify(|reg| {
|
||||||
reg.set_txdmaen(val);
|
reg.set_txdmaen(val);
|
||||||
});
|
});
|
||||||
@ -775,11 +774,11 @@ fn set_txdmaen(regs: Regs, val: bool) {
|
|||||||
|
|
||||||
fn set_rxdmaen(regs: Regs, val: bool) {
|
fn set_rxdmaen(regs: Regs, val: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
regs.cr2().modify(|reg| {
|
regs.cr2().modify(|reg| {
|
||||||
reg.set_rxdmaen(val);
|
reg.set_rxdmaen(val);
|
||||||
});
|
});
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
regs.cfg1().modify(|reg| {
|
regs.cfg1().modify(|reg| {
|
||||||
reg.set_rxdmaen(val);
|
reg.set_rxdmaen(val);
|
||||||
});
|
});
|
||||||
@ -791,21 +790,21 @@ fn finish_dma(regs: Regs) {
|
|||||||
#[cfg(spi_v2)]
|
#[cfg(spi_v2)]
|
||||||
while regs.sr().read().ftlvl() > 0 {}
|
while regs.sr().read().ftlvl() > 0 {}
|
||||||
|
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
while !regs.sr().read().txc() {}
|
while !regs.sr().read().txc() {}
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
while regs.sr().read().bsy() {}
|
while regs.sr().read().bsy() {}
|
||||||
|
|
||||||
regs.cr1().modify(|w| {
|
regs.cr1().modify(|w| {
|
||||||
w.set_spe(false);
|
w.set_spe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(spi_v3))]
|
#[cfg(not(any(spi_v3, spi_v4)))]
|
||||||
regs.cr2().modify(|reg| {
|
regs.cr2().modify(|reg| {
|
||||||
reg.set_txdmaen(false);
|
reg.set_txdmaen(false);
|
||||||
reg.set_rxdmaen(false);
|
reg.set_rxdmaen(false);
|
||||||
});
|
});
|
||||||
#[cfg(spi_v3)]
|
#[cfg(any(spi_v3, spi_v4))]
|
||||||
regs.cfg1().modify(|reg| {
|
regs.cfg1().modify(|reg| {
|
||||||
reg.set_txdmaen(false);
|
reg.set_txdmaen(false);
|
||||||
reg.set_rxdmaen(false);
|
reg.set_rxdmaen(false);
|
||||||
@ -819,7 +818,7 @@ fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
ptr::write_volatile(regs.tx_ptr(), tx_word);
|
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));
|
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 {
|
pub fn dsize(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
WordSize::EightBit => 0b0111,
|
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 {
|
pub fn _frxth(&self) -> vals::Fthlv {
|
||||||
match self {
|
match self {
|
||||||
WordSize::EightBit => vals::Fthlv::ONEFRAME,
|
WordSize::EightBit => vals::Fthlv::ONEFRAME,
|
||||||
|
@ -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) => {
|
(crate::$mod:ident::$trait:ident, $instance:ident, {channel: $channel:ident}, $request:expr) => {
|
||||||
impl crate::$mod::$trait<crate::peripherals::$instance> for crate::peripherals::$channel {
|
impl crate::$mod::$trait<crate::peripherals::$instance> for crate::peripherals::$channel {
|
||||||
fn request(&self) -> crate::dma::Request {
|
fn request(&self) -> crate::dma::Request {
|
||||||
|
@ -115,6 +115,9 @@ impl<I: Interrupt> InterruptExecutor<I> {
|
|||||||
/// different "thread" (the interrupt), so spawning tasks on it is effectively
|
/// different "thread" (the interrupt), so spawning tasks on it is effectively
|
||||||
/// sending them.
|
/// 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
|
/// 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
|
/// Executor instance in a place where it'll live forever and grants you mutable
|
||||||
/// access. There's a few ways to do this:
|
/// access. There's a few ways to do this:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
use core::task::Poll;
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
use super::raw;
|
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.
|
/// Spawn a task into an executor.
|
||||||
///
|
///
|
||||||
/// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`).
|
/// 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
|
// Used by the `embassy_macros::main!` macro to throw an error when spawn
|
||||||
/// fails. This is here to allow conditional use of `defmt::unwrap!`
|
// fails. This is here to allow conditional use of `defmt::unwrap!`
|
||||||
/// without introducing a `defmt` feature in the `embassy_macros` package,
|
// without introducing a `defmt` feature in the `embassy_macros` package,
|
||||||
/// which would require use of `-Z namespaced-features`.
|
// 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>) {
|
pub fn must_spawn<F>(&self, token: SpawnToken<F>) {
|
||||||
unwrap!(self.spawn(token));
|
unwrap!(self.spawn(token));
|
||||||
}
|
}
|
||||||
@ -125,6 +149,27 @@ unsafe impl Send for SendSpawner {}
|
|||||||
unsafe impl Sync for SendSpawner {}
|
unsafe impl Sync for SendSpawner {}
|
||||||
|
|
||||||
impl 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.
|
/// Spawn a task into an executor.
|
||||||
///
|
///
|
||||||
/// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`).
|
/// 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),
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
24
examples/nrf/src/bin/self_spawn_current_executor.rs
Normal file
24
examples/nrf/src/bin/self_spawn_current_executor.rs
Normal 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)));
|
||||||
|
}
|
29
examples/stm32u5/src/bin/blinky.rs
Normal file
29
examples/stm32u5/src/bin/blinky.rs
Normal 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
|
@ -104,6 +104,7 @@ pub struct PeripheralDmaChannel {
|
|||||||
pub signal: String,
|
pub signal: String,
|
||||||
pub channel: Option<String>,
|
pub channel: Option<String>,
|
||||||
pub dmamux: Option<String>,
|
pub dmamux: Option<String>,
|
||||||
|
pub dma: Option<String>,
|
||||||
pub request: Option<u32>,
|
pub request: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,5 +93,6 @@ pub struct PeripheralDmaChannel {
|
|||||||
pub signal: &'static str,
|
pub signal: &'static str,
|
||||||
pub channel: Option<&'static str>,
|
pub channel: Option<&'static str>,
|
||||||
pub dmamux: Option<&'static str>,
|
pub dmamux: Option<&'static str>,
|
||||||
|
pub dma: Option<&'static str>,
|
||||||
pub request: Option<u32>,
|
pub request: Option<u32>,
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,13 @@ version = "0.1.0"
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
stm32f103c8 = ["embassy-stm32/stm32f103c8"]
|
stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill
|
||||||
stm32f429zi = ["embassy-stm32/stm32f429zi"]
|
stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo
|
||||||
stm32g071rb = ["embassy-stm32/stm32g071rb"]
|
stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo
|
||||||
stm32g491re = ["embassy-stm32/stm32g491re"]
|
stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo
|
||||||
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"]
|
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo
|
||||||
stm32wb55rg = ["embassy-stm32/stm32wb55rg"]
|
stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo
|
||||||
|
stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
|
||||||
|
@ -28,6 +28,8 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
|||||||
let (mut a, mut b) = (p.PA3, p.PA2);
|
let (mut a, mut b) = (p.PA3, p.PA2);
|
||||||
#[cfg(feature = "stm32h755zi")]
|
#[cfg(feature = "stm32h755zi")]
|
||||||
let (mut a, mut b) = (p.PB6, p.PB7);
|
let (mut a, mut b) = (p.PB6, p.PB7);
|
||||||
|
#[cfg(feature = "stm32u585ai")]
|
||||||
|
let (mut a, mut b) = (p.PD9, p.PD8);
|
||||||
|
|
||||||
// Test initial output
|
// Test initial output
|
||||||
{
|
{
|
||||||
|
@ -16,10 +16,20 @@ use example_common::*;
|
|||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
info!("Hello World!");
|
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);
|
let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6);
|
||||||
#[cfg(feature = "stm32h755zi")]
|
#[cfg(feature = "stm32h755zi")]
|
||||||
let (sck, mosi, miso) = (p.PA5, p.PB5, p.PA6);
|
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(
|
let mut spi = Spi::new(
|
||||||
p.SPI1,
|
p.SPI1,
|
||||||
|
@ -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);
|
let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2);
|
||||||
#[cfg(feature = "stm32wb55rg")]
|
#[cfg(feature = "stm32wb55rg")]
|
||||||
let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2);
|
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(
|
let mut spi = Spi::new(
|
||||||
p.SPI1,
|
p.SPI1,
|
||||||
|
@ -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
|
let (tx, rx, usart) = (p.PA9, p.PA10, p.USART1); // TODO this is wrong
|
||||||
#[cfg(feature = "stm32h755zi")]
|
#[cfg(feature = "stm32h755zi")]
|
||||||
let (tx, rx, usart) = (p.PB6, p.PB7, p.USART1);
|
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 config = Config::default();
|
||||||
let mut usart = Uart::new(usart, rx, tx, NoDma, NoDma, config);
|
let mut usart = Uart::new(usart, rx, tx, NoDma, NoDma, config);
|
||||||
|
@ -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
|
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")]
|
#[cfg(feature = "stm32h755zi")]
|
||||||
let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1);
|
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 config = Config::default();
|
||||||
let mut usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, config);
|
let mut usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, config);
|
||||||
|
Loading…
Reference in New Issue
Block a user