stm32: add stm32u5 GPDMA, SPIv4 support, add HIL tests.
This commit is contained in:
parent
a39d796c3d
commit
009bb8e4e1
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 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 {
|
||||
|
@ -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 {
|
||||
|
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;
|
||||
#[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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
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 channel: Option<String>,
|
||||
pub dmamux: Option<String>,
|
||||
pub dma: Option<String>,
|
||||
pub request: Option<u32>,
|
||||
}
|
||||
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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"] }
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user