stm32: add stm32u5 GPDMA, SPIv4 support, add HIL tests.

This commit is contained in:
Dario Nieuwenhuis 2022-04-26 23:57:26 +02:00
parent a39d796c3d
commit 009bb8e4e1
16 changed files with 447 additions and 59 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 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 {

View file

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

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

View file

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

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) => { (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 {

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

View file

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

View file

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

View file

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

View file

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

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

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

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