Merge commit 'cbe076d763d97f715605d25d8f8815e299c45d46'

This commit is contained in:
sander 2023-04-19 15:41:00 +02:00
commit 7d64de153f
93 changed files with 4128 additions and 2452 deletions

View file

@ -16,10 +16,11 @@
// "embassy-executor/Cargo.toml",
// "embassy-sync/Cargo.toml",
"examples/nrf52840/Cargo.toml",
//"examples/nrf5340/Cargo.toml",
// "examples/nrf5340/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml",
// "examples/std/Cargo.toml",
// "examples/stm32c0/Cargo.toml",
// "examples/stm32f0/Cargo.toml",
// "examples/stm32f1/Cargo.toml",
// "examples/stm32f2/Cargo.toml",
@ -28,6 +29,7 @@
// "examples/stm32f7/Cargo.toml",
// "examples/stm32g0/Cargo.toml",
// "examples/stm32g4/Cargo.toml",
// "examples/stm32h5/Cargo.toml",
// "examples/stm32h7/Cargo.toml",
// "examples/stm32l0/Cargo.toml",
// "examples/stm32l1/Cargo.toml",
@ -35,9 +37,7 @@
// "examples/stm32l5/Cargo.toml",
// "examples/stm32u5/Cargo.toml",
// "examples/stm32wb/Cargo.toml",
// "examples/stm32wb55/Cargo.toml",
// "examples/stm32wl/Cargo.toml",
// "examples/stm32wl55/Cargo.toml",
// "examples/wasm/Cargo.toml",
],
}

1
ci.sh
View file

@ -22,6 +22,7 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \

View file

@ -13,6 +13,7 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \

View file

@ -131,48 +131,6 @@ where
type Error = E;
}
#[cfg(feature = "_todo_embedded_hal_serial")]
impl<T, E> embedded_hal_async::serial::Read for BlockingAsync<T>
where
T: serial::Read<u8, Error = E>,
E: embedded_hal_1::serial::Error + 'static,
{
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
async move {
let mut pos = 0;
while pos < buf.len() {
match self.wrapped.read() {
Err(nb::Error::WouldBlock) => {}
Err(nb::Error::Other(e)) => return Err(e),
Ok(b) => {
buf[pos] = b;
pos += 1;
}
}
}
Ok(())
}
}
}
#[cfg(feature = "_todo_embedded_hal_serial")]
impl<T, E> embedded_hal_async::serial::Write for BlockingAsync<T>
where
T: blocking::serial::Write<u8, Error = E> + serial::Read<u8, Error = E>,
E: embedded_hal_1::serial::Error + 'static,
{
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
async move { self.wrapped.bwrite_all(buf) }
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
fn flush(&mut self) -> Result<(), Self::Error> {
async move { self.wrapped.bflush() }
}
}
/// NOR flash wrapper
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};

View file

@ -4,15 +4,16 @@ use core::ptr::NonNull;
use atomic_polyfill::{AtomicPtr, Ordering};
use super::{TaskHeader, TaskRef};
use crate::raw::util::SyncUnsafeCell;
pub(crate) struct RunQueueItem {
next: AtomicPtr<TaskHeader>,
next: SyncUnsafeCell<Option<TaskRef>>,
}
impl RunQueueItem {
pub const fn new() -> Self {
Self {
next: AtomicPtr::new(ptr::null_mut()),
next: SyncUnsafeCell::new(None),
}
}
}
@ -51,7 +52,12 @@ impl RunQueue {
self.head
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
was_empty = prev.is_null();
task.header().run_queue_item.next.store(prev, Ordering::Relaxed);
unsafe {
// safety: the pointer is either null or valid
let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr()));
// safety: there are no concurrent accesses to `next`
task.header().run_queue_item.next.set(prev);
}
Some(task.as_ptr() as *mut _)
})
.ok();
@ -64,18 +70,19 @@ impl RunQueue {
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
// Atomically empty the queue.
let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
// safety: the pointer is either null or valid
let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) };
// Iterate the linked list of tasks that were previously in the queue.
while let Some(task) = NonNull::new(ptr) {
let task = unsafe { TaskRef::from_ptr(task.as_ptr()) };
while let Some(task) = next {
// If the task re-enqueues itself, the `next` pointer will get overwritten.
// Therefore, first read the next pointer, and only then process the task.
let next = task.header().run_queue_item.next.load(Ordering::Relaxed);
// safety: there are no concurrent accesses to `next`
next = unsafe { task.header().run_queue_item.next.get() };
on_task(task);
ptr = next
}
}
}

View file

@ -1,5 +1,5 @@
#[macro_export]
macro_rules! peripherals {
macro_rules! peripherals_definition {
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
/// Types for the peripheral singletons.
pub mod peripherals {
@ -26,7 +26,12 @@ macro_rules! peripherals {
$crate::impl_peripheral!($name);
)*
}
};
}
#[macro_export]
macro_rules! peripherals_struct {
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
/// Struct containing all the peripheral singletons.
///
/// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`].
@ -76,6 +81,24 @@ macro_rules! peripherals {
};
}
#[macro_export]
macro_rules! peripherals {
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
$crate::peripherals_definition!(
$(
$(#[$cfg])?
$name,
)*
);
$crate::peripherals_struct!(
$(
$(#[$cfg])?
$name,
)*
);
};
}
#[macro_export]
macro_rules! into_ref {
($($name:ident),*) => {

View file

@ -38,5 +38,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw
embedded-hal = { version = "0.2", features = ["unproven"] }
bit_field = { version = "0.10" }
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] }
lorawan = { version = "0.7.1", default-features = false }
lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] }
lorawan = { version = "0.7.2", default-features = false }

View file

@ -1,5 +1,6 @@
#![no_std]
#![feature(type_alias_impl_trait)]
#![feature(async_fn_in_trait, impl_trait_projections)]
#![allow(incomplete_features)]
//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
//! crate's async LoRaWAN MAC implementation.
@ -34,13 +35,11 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer {
self.start = Instant::now();
}
type AtFuture<'m> = impl core::future::Future<Output = ()> + 'm;
fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> {
Timer::at(self.start + Duration::from_millis(millis))
async fn at(&mut self, millis: u64) {
Timer::at(self.start + Duration::from_millis(millis)).await
}
type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm;
fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> {
Timer::after(Duration::from_millis(millis))
async fn delay_ms(&mut self, millis: u64) {
Timer::after(Duration::from_millis(millis)).await
}
}

View file

@ -1,5 +1,5 @@
//! A radio driver integration for the radio found on STM32WL family devices.
use core::future::{poll_fn, Future};
use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
@ -241,14 +241,12 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf
impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
type PhyError = RadioError;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where Self: 'm;
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
async move { self.do_tx(config, buf).await }
async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
self.do_tx(config, buf).await
}
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where Self: 'm;
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
async move { self.do_rx(config, buf).await }
async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
self.do_rx(config, buf).await
}
}

View file

@ -1,5 +1,3 @@
use core::future::Future;
use defmt::Format;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
@ -71,16 +69,8 @@ where
{
type PhyError = RadioError<BUS>;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
where
SPI: 'm,
CTRL: 'm,
WAIT: 'm,
BUS: 'm;
fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result<u32, Self::PhyError> {
trace!("TX START");
async move {
self.lora
.set_tx_config(
config.pw,
@ -102,18 +92,13 @@ where
trace!("TX DONE");
return Ok(0);
}
}
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
where
SPI: 'm,
CTRL: 'm,
WAIT: 'm,
BUS: 'm;
fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
async fn rx(
&mut self,
config: RfConfig,
receiving_buffer: &mut [u8],
) -> Result<(usize, RxQuality), Self::PhyError> {
trace!("RX START");
async move {
self.lora
.set_rx_config(
config.spreading_factor.into(),
@ -149,5 +134,4 @@ where
Ok((received_len as usize, RxQuality::new(rssi, snr)))
}
}
}

View file

@ -1,5 +1,3 @@
use core::future::Future;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::*;
@ -88,18 +86,8 @@ where
{
type PhyError = Sx127xError;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
where
SPI: 'm,
CS: 'm,
RESET: 'm,
E: 'm,
I: 'm,
RFS: 'm;
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
trace!("TX START");
async move {
self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
self.rfs.set_tx();
self.radio.set_tx_power(14, 0).await?;
@ -133,20 +121,8 @@ where
}
}
}
}
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
where
SPI: 'm,
CS: 'm,
RESET: 'm,
E: 'm,
I: 'm,
RFS: 'm;
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
trace!("RX START");
async move {
async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
self.rfs.set_rx();
self.radio.reset_payload_length().await?;
self.radio.set_frequency(config.frequency).await?;
@ -185,7 +161,6 @@ where
}
}
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]

View file

@ -27,12 +27,10 @@ use embassy_sync::waitqueue::WakerRegistration;
use embassy_time::{Instant, Timer};
use futures::pin_mut;
use heapless::Vec;
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
#[cfg(feature = "dhcpv4")]
use smoltcp::iface::SocketHandle;
use smoltcp::iface::{Interface, SocketSet, SocketStorage};
use smoltcp::socket::dhcpv4::{self, RetryConfig};
#[cfg(feature = "dhcpv4")]
use smoltcp::socket::dhcpv4;
use smoltcp::socket::dhcpv4::RetryConfig;
use smoltcp::time::Duration;
// smoltcp reexports
pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant};
@ -76,6 +74,7 @@ pub struct StaticConfig {
pub dns_servers: Vec<Ipv4Address, 3>,
}
#[cfg(feature = "dhcpv4")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DhcpConfig {
pub max_lease_duration: Option<Duration>,
@ -88,6 +87,7 @@ pub struct DhcpConfig {
pub client_port: u16,
}
#[cfg(feature = "dhcpv4")]
impl Default for DhcpConfig {
fn default() -> Self {
Self {
@ -384,6 +384,7 @@ impl<D: Driver + 'static> Inner<D> {
self.config = Some(config)
}
#[cfg(feature = "dhcpv4")]
fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) {
socket.set_ignore_naks(config.ignore_naks);
socket.set_max_lease_duration(config.max_lease_duration);

View file

@ -992,80 +992,3 @@ mod eh1 {
type Error = Error;
}
}
#[cfg(all(
feature = "unstable-traits",
feature = "nightly",
feature = "_todo_embedded_hal_serial"
))]
mod eha {
use core::future::Future;
use super::*;
impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush(&mut self) -> Result<(), Self::Error> {
async move { Ok(()) }
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush(&mut self) -> Result<(), Self::Error> {
async move { Ok(()) }
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush(&mut self) -> Result<(), Self::Error> {
async move { Ok(()) }
}
}
}

View file

@ -61,8 +61,7 @@ embedded-io = { version = "0.4.0", features = ["async"], optional = true }
embedded-storage = { version = "0.3" }
rand_core = "0.6.4"
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
rp-pac = { version = "1", features = ["rt"] }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}

View file

@ -274,3 +274,201 @@ macro_rules! intrinsics {
intrinsics!($($rest)*);
};
}
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/sio.rs
// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers
// by packing it into r0[0:31] and r1[32:63]. So all we need to do is put
// the remainder in the high order 32 bits of a 64 bit result. We can also
// alias the division operators to these for a similar reason r0 is the
// result either way and r1 a scratch register, so the caller can't assume it
// retains the argument value.
#[cfg(target_arch = "arm")]
core::arch::global_asm!(
".macro hwdivider_head",
"ldr r2, =(0xd0000000)", // SIO_BASE
// Check the DIRTY state of the divider by shifting it into the C
// status bit.
"ldr r3, [r2, #0x078]", // DIV_CSR
"lsrs r3, #2", // DIRTY = 1, so shift 2 down
// We only need to save the state when DIRTY, otherwise we can just do the
// division directly.
"bcs 2f",
"1:",
// Do the actual division now, we're either not DIRTY, or we've saved the
// state and branched back here so it's safe now.
".endm",
".macro hwdivider_tail",
// 8 cycle delay to wait for the result. Each branch takes two cycles
// and fits into a 2-byte Thumb instruction, so this is smaller than
// 8 NOPs.
"b 3f",
"3: b 3f",
"3: b 3f",
"3: b 3f",
"3:",
// Read the quotient last, since that's what clears the dirty flag.
"ldr r1, [r2, #0x074]", // DIV_REMAINDER
"ldr r0, [r2, #0x070]", // DIV_QUOTIENT
// Either return to the caller or back to the state restore.
"bx lr",
"2:",
// Since we can't save the signed-ness of the calculation, we have to make
// sure that there's at least an 8 cycle delay before we read the result.
// The push takes 5 cycles, and we've already spent at least 7 checking
// the DIRTY state to get here.
"push {{r4-r6, lr}}",
// Read the quotient last, since that's what clears the dirty flag.
"ldr r3, [r2, #0x060]", // DIV_UDIVIDEND
"ldr r4, [r2, #0x064]", // DIV_UDIVISOR
"ldr r5, [r2, #0x074]", // DIV_REMAINDER
"ldr r6, [r2, #0x070]", // DIV_QUOTIENT
// If we get interrupted here (before a write sets the DIRTY flag) it's
// fine, since we have the full state, so the interruptor doesn't have to
// restore it. Once the write happens and the DIRTY flag is set, the
// interruptor becomes responsible for restoring our state.
"bl 1b",
// If we are interrupted here, then the interruptor will start an incorrect
// calculation using a wrong divisor, but we'll restore the divisor and
// result ourselves correctly. This sets DIRTY, so any interruptor will
// save the state.
"str r3, [r2, #0x060]", // DIV_UDIVIDEND
// If we are interrupted here, the the interruptor may start the
// calculation using incorrectly signed inputs, but we'll restore the
// result ourselves. This sets DIRTY, so any interruptor will save the
// state.
"str r4, [r2, #0x064]", // DIV_UDIVISOR
// If we are interrupted here, the interruptor will have restored
// everything but the quotient may be wrongly signed. If the calculation
// started by the above writes is still ongoing it is stopped, so it won't
// replace the result we're restoring. DIRTY and READY set, but only
// DIRTY matters to make the interruptor save the state.
"str r5, [r2, #0x074]", // DIV_REMAINDER
// State fully restored after the quotient write. This sets both DIRTY
// and READY, so whatever we may have interrupted can read the result.
"str r6, [r2, #0x070]", // DIV_QUOTIENT
"pop {{r4-r6, pc}}",
".endm",
);
macro_rules! division_function {
(
$name:ident $($intrinsic:ident)* ( $argty:ty ) {
$($begin:literal),+
}
) => {
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
core::arch::global_asm!(
// Mangle the name slightly, since this is a global symbol.
concat!(".global _rphal_", stringify!($name)),
concat!(".type _rphal_", stringify!($name), ", %function"),
".align 2",
concat!("_rphal_", stringify!($name), ":"),
$(
concat!(".global ", stringify!($intrinsic)),
concat!(".type ", stringify!($intrinsic), ", %function"),
concat!(stringify!($intrinsic), ":"),
)*
"hwdivider_head",
$($begin),+ ,
"hwdivider_tail",
);
#[cfg(all(target_arch = "arm", not(feature = "intrinsics")))]
core::arch::global_asm!(
// Mangle the name slightly, since this is a global symbol.
concat!(".global _rphal_", stringify!($name)),
concat!(".type _rphal_", stringify!($name), ", %function"),
".align 2",
concat!("_rphal_", stringify!($name), ":"),
"hwdivider_head",
$($begin),+ ,
"hwdivider_tail",
);
#[cfg(target_arch = "arm")]
extern "aapcs" {
// Connect a local name to global symbol above through FFI.
#[link_name = concat!("_rphal_", stringify!($name)) ]
fn $name(n: $argty, d: $argty) -> u64;
}
#[cfg(not(target_arch = "arm"))]
#[allow(unused_variables)]
unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }
};
}
division_function! {
unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {
"str r0, [r2, #0x060]", // DIV_UDIVIDEND
"str r1, [r2, #0x064]" // DIV_UDIVISOR
}
}
division_function! {
signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {
"str r0, [r2, #0x068]", // DIV_SDIVIDEND
"str r1, [r2, #0x06c]" // DIV_SDIVISOR
}
}
fn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {
let packed = unsafe { unsigned_divmod(n, d) };
DivResult {
quotient: packed as u32,
remainder: (packed >> 32) as u32,
}
}
fn divider_signed(n: i32, d: i32) -> DivResult<i32> {
let packed = unsafe { signed_divmod(n, d) };
// Double casts to avoid sign extension
DivResult {
quotient: packed as u32 as i32,
remainder: (packed >> 32) as u32 as i32,
}
}
/// Result of divide/modulo operation
struct DivResult<T> {
/// The quotient of divide/modulo operation
pub quotient: T,
/// The remainder of divide/modulo operation
pub remainder: T,
}
intrinsics! {
extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
divider_unsigned(n, d).quotient
}
extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
divider_unsigned(n, d).remainder
}
extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
let quo_rem = divider_unsigned(n, d);
if let Some(rem) = rem {
*rem = quo_rem.remainder;
}
quo_rem.quotient
}
extern "C" fn __divsi3(n: i32, d: i32) -> i32 {
divider_signed(n, d).quotient
}
extern "C" fn __modsi3(n: i32, d: i32) -> i32 {
divider_signed(n, d).remainder
}
extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {
let quo_rem = divider_signed(n, d);
*rem = quo_rem.remainder;
quo_rem.quotient
}
}

View file

@ -44,9 +44,9 @@ pub use embassy_cortex_m::executor;
pub use embassy_cortex_m::interrupt::_export::interrupt;
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
#[cfg(feature = "unstable-pac")]
pub use rp2040_pac2 as pac;
pub use rp_pac as pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use rp2040_pac2 as pac;
pub(crate) use rp_pac as pac;
embassy_hal_common::peripherals! {
PIN_0,

View file

@ -726,58 +726,3 @@ mod eh1 {
}
}
}
#[cfg(all(
feature = "unstable-traits",
feature = "nightly",
feature = "_todo_embedded_hal_serial"
))]
mod eha {
use core::future::Future;
use super::*;
impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
Self::write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
Self::flush()
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
Self::read(buf)
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUart<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
BufferedUartTx::<'d, T>::write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
BufferedUartTx::<'d, T>::flush()
}
}
impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUart<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
BufferedUartRx::<'d, T>::read(buf)
}
}
}

View file

@ -5,6 +5,7 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use crate::dma::{AnyChannel, Channel};
use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin;
use crate::pac::io::vals::{Inover, Outover};
use crate::{pac, peripherals, Peripheral};
#[cfg(feature = "nightly")]
@ -53,6 +54,14 @@ pub struct Config {
pub data_bits: DataBits,
pub stop_bits: StopBits,
pub parity: Parity,
/// Invert the tx pin output
pub invert_tx: bool,
/// Invert the rx pin input
pub invert_rx: bool,
// Invert the rts pin
pub invert_rts: bool,
// Invert the cts pin
pub invert_cts: bool,
}
impl Default for Config {
@ -62,6 +71,10 @@ impl Default for Config {
data_bits: DataBits::DataBits8,
stop_bits: StopBits::STOP1,
parity: Parity::ParityNone,
invert_rx: false,
invert_tx: false,
invert_rts: false,
invert_cts: false,
}
}
}
@ -167,7 +180,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> {
}
impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
/// Create a new DMA-enabled UART which can only send data
/// Create a new DMA-enabled UART which can only recieve data
pub fn new(
_uart: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
@ -175,7 +188,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
config: Config,
) -> Self {
into_ref!(rx, rx_dma);
Uart::<T, M>::init(Some(rx.map_into()), None, None, None, config);
Uart::<T, M>::init(None, Some(rx.map_into()), None, None, config);
Self::new_inner(Some(rx_dma.map_into()))
}
@ -381,19 +394,47 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
let r = T::regs();
unsafe {
if let Some(pin) = &tx {
pin.io().ctrl().write(|w| w.set_funcsel(2));
pin.io().ctrl().write(|w| {
w.set_funcsel(2);
w.set_outover(if config.invert_tx {
Outover::INVERT
} else {
Outover::NORMAL
});
});
pin.pad_ctrl().write(|w| w.set_ie(true));
}
if let Some(pin) = &rx {
pin.io().ctrl().write(|w| w.set_funcsel(2));
pin.io().ctrl().write(|w| {
w.set_funcsel(2);
w.set_inover(if config.invert_rx {
Inover::INVERT
} else {
Inover::NORMAL
});
});
pin.pad_ctrl().write(|w| w.set_ie(true));
}
if let Some(pin) = &cts {
pin.io().ctrl().write(|w| w.set_funcsel(2));
pin.io().ctrl().write(|w| {
w.set_funcsel(2);
w.set_inover(if config.invert_cts {
Inover::INVERT
} else {
Inover::NORMAL
});
});
pin.pad_ctrl().write(|w| w.set_ie(true));
}
if let Some(pin) = &rts {
pin.io().ctrl().write(|w| w.set_funcsel(2));
pin.io().ctrl().write(|w| {
w.set_funcsel(2);
w.set_outover(if config.invert_rts {
Outover::INVERT
} else {
Outover::NORMAL
});
});
pin.pad_ctrl().write(|w| w.set_ie(true));
}
@ -651,61 +692,6 @@ mod eh1 {
}
}
#[cfg(all(
feature = "unstable-traits",
feature = "nightly",
feature = "_todo_embedded_hal_serial"
))]
mod eha {
use core::future::Future;
use super::*;
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buf)
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buf)
}
}
}
mod sealed {
use super::*;

View file

@ -55,7 +55,7 @@ cortex-m = "0.7.6"
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
rand_core = "0.6.3"
sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true }
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1"
atomic-polyfill = "1.0.1"
stm32-metapac = "6"
@ -66,6 +66,7 @@ stm32-fmc = "0.2.4"
seq-macro = "0.3.0"
cfg-if = "1.0.0"
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
chrono = { version = "^0.4", default-features = false, optional = true}
[dev-dependencies]
critical-section = { version = "1.1", features = ["std"] }

View file

@ -81,11 +81,74 @@ fn main() {
singletons.push(c.name.to_string());
}
// ========
// Handle time-driver-XXXX features.
let time_driver = match env::vars()
.map(|(a, _)| a)
.filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
.get_one()
{
Ok(x) => Some(
x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
.unwrap()
.to_ascii_lowercase(),
),
Err(GetOneError::None) => None,
Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
};
let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
None => "",
Some("tim2") => "TIM2",
Some("tim3") => "TIM3",
Some("tim4") => "TIM4",
Some("tim5") => "TIM5",
Some("tim12") => "TIM12",
Some("tim15") => "TIM15",
Some("any") => {
if singletons.contains(&"TIM2".to_string()) {
"TIM2"
} else if singletons.contains(&"TIM3".to_string()) {
"TIM3"
} else if singletons.contains(&"TIM4".to_string()) {
"TIM4"
} else if singletons.contains(&"TIM5".to_string()) {
"TIM5"
} else if singletons.contains(&"TIM12".to_string()) {
"TIM12"
} else if singletons.contains(&"TIM15".to_string()) {
"TIM15"
} else {
panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
}
}
_ => panic!("unknown time_driver {:?}", time_driver),
};
if time_driver_singleton != "" {
println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase());
}
// ========
// Write singletons
let mut g = TokenStream::new();
let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
g.extend(quote! {
embassy_hal_common::peripherals!(#(#singleton_tokens),*);
embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*);
});
let singleton_tokens: Vec<_> = singletons
.iter()
.filter(|s| *s != &time_driver_singleton.to_string())
.map(|s| format_ident!("{}", s))
.collect();
g.extend(quote! {
embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*);
});
// ========
@ -192,12 +255,20 @@ fn main() {
];
});
let max_erase_size = flash_memory_regions
.iter()
.map(|region| region.settings.as_ref().unwrap().erase_size)
.max()
.unwrap();
g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; });
g.extend(quote! { pub mod flash_regions { #flash_regions } });
// ========
// Generate DMA IRQs.
let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new();
let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new();
for p in METADATA.peripherals {
if let Some(r) = &p.registers {
@ -207,7 +278,10 @@ fn main() {
continue;
}
for irq in p.interrupts {
dma_irqs.entry(irq.interrupt).or_default().push((p.name, irq.signal));
dma_irqs
.entry(irq.interrupt)
.or_default()
.push((r.kind, p.name, irq.signal));
}
}
}
@ -216,13 +290,14 @@ fn main() {
for (irq, channels) in dma_irqs {
let irq = format_ident!("{}", irq);
let channels = channels.iter().map(|(dma, ch)| format_ident!("{}_{}", dma, ch));
let xdma = format_ident!("{}", channels[0].0);
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
g.extend(quote! {
#[crate::interrupt]
unsafe fn #irq () {
#(
<crate::peripherals::#channels as crate::dma::sealed::Channel>::on_irq();
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
)*
}
});
@ -838,51 +913,6 @@ fn main() {
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
// ========
// Handle time-driver-XXXX features.
let time_driver = match env::vars()
.map(|(a, _)| a)
.filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
.get_one()
{
Ok(x) => Some(
x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
.unwrap()
.to_ascii_lowercase(),
),
Err(GetOneError::None) => None,
Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
};
match time_driver.as_ref().map(|x| x.as_ref()) {
None => {}
Some("tim2") => println!("cargo:rustc-cfg=time_driver_tim2"),
Some("tim3") => println!("cargo:rustc-cfg=time_driver_tim3"),
Some("tim4") => println!("cargo:rustc-cfg=time_driver_tim4"),
Some("tim5") => println!("cargo:rustc-cfg=time_driver_tim5"),
Some("tim12") => println!("cargo:rustc-cfg=time_driver_tim12"),
Some("tim15") => println!("cargo:rustc-cfg=time_driver_tim15"),
Some("any") => {
if singletons.contains(&"TIM2".to_string()) {
println!("cargo:rustc-cfg=time_driver_tim2");
} else if singletons.contains(&"TIM3".to_string()) {
println!("cargo:rustc-cfg=time_driver_tim3");
} else if singletons.contains(&"TIM4".to_string()) {
println!("cargo:rustc-cfg=time_driver_tim4");
} else if singletons.contains(&"TIM5".to_string()) {
println!("cargo:rustc-cfg=time_driver_tim5");
} else if singletons.contains(&"TIM12".to_string()) {
println!("cargo:rustc-cfg=time_driver_tim12");
} else if singletons.contains(&"TIM15".to_string()) {
println!("cargo:rustc-cfg=time_driver_tim15");
} else {
panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
}
}
_ => panic!("unknown time_driver {:?}", time_driver),
}
// Handle time-driver-XXXX features.
if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);

View file

@ -4,6 +4,7 @@ use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::dma::Transfer;
use crate::gpio::sealed::AFType;
use crate::gpio::Speed;
use crate::interrupt::{Interrupt, InterruptExt};
@ -385,14 +386,11 @@ where
return self.capture_giant(buffer).await;
}
}
async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
let channel = &mut self.dma;
let request = channel.request();
let r = self.inner.regs();
let src = r.dr().ptr() as *mut u32;
let dma_read = crate::dma::read(channel, request, src, buffer);
let request = self.dma.request();
let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) };
Self::clear_interrupt_flags();
Self::enable_irqs();
@ -436,6 +434,12 @@ where
result
}
#[cfg(not(dma))]
async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> {
panic!("capturing to buffers larger than 0xffff is only supported on DMA for now, not on BDMA or GPDMA.");
}
#[cfg(dma)]
async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
use crate::dma::TransferOptions;
@ -460,16 +464,24 @@ where
let r = self.inner.regs();
let src = r.dr().ptr() as *mut u32;
unsafe {
channel.start_double_buffered_read(request, src, m0ar, m1ar, chunk_size, TransferOptions::default());
}
let mut transfer = unsafe {
crate::dma::DoubleBuffered::new_read(
&mut self.dma,
request,
src,
m0ar,
m1ar,
chunk_size,
TransferOptions::default(),
)
};
let mut last_chunk_set_for_transfer = false;
let mut buffer0_last_accessible = false;
let dma_result = poll_fn(|cx| {
channel.set_waker(cx.waker());
transfer.set_waker(cx.waker());
let buffer0_currently_accessible = unsafe { channel.is_buffer0_accessible() };
let buffer0_currently_accessible = transfer.is_buffer0_accessible();
// check if the accessible buffer changed since last poll
if buffer0_last_accessible == buffer0_currently_accessible {
@ -480,21 +492,21 @@ where
if remaining_chunks != 0 {
if remaining_chunks % 2 == 0 && buffer0_currently_accessible {
m0ar = unsafe { m0ar.add(2 * chunk_size) };
unsafe { channel.set_buffer0(m0ar) }
unsafe { transfer.set_buffer0(m0ar) }
remaining_chunks -= 1;
} else if !buffer0_currently_accessible {
m1ar = unsafe { m1ar.add(2 * chunk_size) };
unsafe { channel.set_buffer1(m1ar) };
unsafe { transfer.set_buffer1(m1ar) };
remaining_chunks -= 1;
}
} else {
if buffer0_currently_accessible {
unsafe { channel.set_buffer0(buffer.as_mut_ptr()) }
unsafe { transfer.set_buffer0(buffer.as_mut_ptr()) }
} else {
unsafe { channel.set_buffer1(buffer.as_mut_ptr()) }
unsafe { transfer.set_buffer1(buffer.as_mut_ptr()) }
}
if last_chunk_set_for_transfer {
channel.request_stop();
transfer.request_stop();
return Poll::Ready(());
}
last_chunk_set_for_transfer = true;

View file

@ -1,18 +1,32 @@
#![macro_use]
use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{fence, Ordering};
use core::task::Waker;
use core::task::{Context, Poll};
use embassy_cortex_m::interrupt::Priority;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use super::{TransferOptions, Word, WordSize};
use super::word::{Word, WordSize};
use super::Dir;
use crate::_generated::BDMA_CHANNEL_COUNT;
use crate::dma::Request;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::pac;
use crate::pac::bdma::vals;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct TransferOptions {}
impl Default for TransferOptions {
fn default() -> Self {
Self {}
}
}
impl From<WordSize> for vals::Size {
fn from(raw: WordSize) -> Self {
match raw {
@ -23,6 +37,15 @@ impl From<WordSize> for vals::Size {
}
}
impl From<Dir> for vals::Dir {
fn from(raw: Dir) -> Self {
match raw {
Dir::MemoryToPeripheral => Self::FROMMEMORY,
Dir::PeripheralToMemory => Self::FROMPERIPHERAL,
}
}
}
struct State {
ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT],
}
@ -55,219 +78,27 @@ foreach_dma_channel! {
// BDMA1 in H7 doesn't use DMAMUX, which breaks
};
($channel_peri:ident, $dma_peri:ident, bdma, $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,
#[cfg(any(bdma_v2, dmamux))]
_request,
vals::Dir::FROMMEMORY,
reg_addr as *const u32,
ptr as *mut u32,
len,
true,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
);
impl sealed::Channel for crate::peripherals::$channel_peri {
fn regs(&self) -> pac::bdma::Dma {
pac::$dma_peri
}
unsafe fn start_write_repeated<W: Word>(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
low_level_api::start_transfer(
pac::$dma_peri,
$channel_num,
#[cfg(any(bdma_v2, dmamux))]
_request,
vals::Dir::FROMMEMORY,
reg_addr as *const u32,
repeated as *mut u32,
count,
false,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
fn num(&self) -> usize {
$channel_num
}
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,
#[cfg(any(bdma_v2, dmamux))]
_request,
vals::Dir::FROMPERIPHERAL,
reg_addr as *const u32,
ptr as *mut u32,
len,
true,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
);
fn index(&self) -> usize {
$index
}
unsafe fn start_double_buffered_read<W: super::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 BDMA");
}
unsafe fn set_buffer0<W: super::Word>(&mut self, _buffer: *mut W) {
panic!("Unsafe double buffered mode is unavailable on BDMA");
}
unsafe fn set_buffer1<W: super::Word>(&mut self, _buffer: *mut W) {
panic!("Unsafe double buffered mode is unavailable on BDMA");
}
unsafe fn is_buffer0_accessible(&mut self) -> bool {
panic!("Unsafe double buffered mode is unavailable on BDMA");
}
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);
}
unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
}
}
impl crate::dma::Channel for crate::peripherals::$channel_peri {}
impl Channel for crate::peripherals::$channel_peri {}
};
}
mod low_level_api {
use super::*;
pub unsafe fn start_transfer(
dma: pac::bdma::Dma,
channel_number: u8,
#[cfg(any(bdma_v2, dmamux))] request: Request,
dir: vals::Dir,
peri_addr: *const u32,
mem_addr: *mut u32,
mem_len: usize,
incr_mem: bool,
data_size: vals::Size,
options: TransferOptions,
#[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux,
#[cfg(dmamux)] dmamux_ch_num: u8,
) {
assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported");
assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported");
assert!(
options.flow_ctrl == crate::dma::FlowControl::Dma,
"Peripheral flow control not supported"
);
assert!(options.fifo_threshold.is_none(), "FIFO mode not supported");
let ch = dma.ch(channel_number as _);
reset_status(dma, channel_number);
#[cfg(dmamux)]
super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
#[cfg(bdma_v2)]
critical_section::with(|_| dma.cselr().modify(|w| w.set_cs(channel_number as _, request)));
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::SeqCst);
ch.par().write_value(peri_addr as u32);
ch.mar().write_value(mem_addr as u32);
ch.ndtr().write(|w| w.set_ndt(mem_len as u16));
ch.cr().write(|w| {
w.set_psize(data_size);
w.set_msize(data_size);
if incr_mem {
w.set_minc(vals::Inc::ENABLED);
} else {
w.set_minc(vals::Inc::DISABLED);
}
w.set_dir(dir);
w.set_teie(true);
w.set_tcie(true);
w.set_en(true);
});
}
pub unsafe fn request_stop(dma: pac::bdma::Dma, channel_number: u8) {
reset_status(dma, channel_number);
let ch = dma.ch(channel_number as _);
// Disable the channel and interrupts with the default value.
ch.cr().write(|_| ());
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
}
pub unsafe fn is_running(dma: pac::bdma::Dma, ch: u8) -> bool {
let ch = dma.ch(ch as _);
ch.cr().read().en()
}
/// 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: pac::bdma::Dma, 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.ndtr().read().ndt() as u16
}
/// Sets the waker for the specified DMA channel
pub unsafe fn set_waker(state_number: usize, waker: &Waker) {
STATE.ch_wakers[state_number].register(waker);
}
pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) {
dma.ifcr().write(|w| {
w.set_tcif(channel_number as _, true);
w.set_teif(channel_number as _, true);
});
}
/// Safety: Must be called with a matching set of parameters for a valid dma channel
pub unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: u8, index: u8) {
let channel_num = channel_num as usize;
let index = index as usize;
/// Safety: Must be called with a matching set of parameters for a valid dma channel
pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index: usize) {
let isr = dma.isr().read();
let cr = dma.ch(channel_num).cr();
@ -278,5 +109,236 @@ mod low_level_api {
cr.write(|_| ()); // Disable channel interrupts with the default value.
STATE.ch_wakers[index].wake();
}
}
#[cfg(any(bdma_v2, dmamux))]
pub type Request = u8;
#[cfg(not(any(bdma_v2, dmamux)))]
pub type Request = ();
#[cfg(dmamux)]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
#[cfg(not(dmamux))]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
pub(crate) mod sealed {
use super::*;
pub trait Channel {
fn regs(&self) -> pac::bdma::Dma;
fn num(&self) -> usize;
fn index(&self) -> usize;
fn on_irq();
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
pub unsafe fn new_read<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
peri_addr: *mut W,
buf: &'a mut [W],
options: TransferOptions,
) -> Self {
Self::new_read_raw(channel, request, peri_addr, buf, options)
}
pub unsafe fn new_read_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
peri_addr: *mut W,
buf: *mut [W],
options: TransferOptions,
) -> Self {
into_ref!(channel);
let (ptr, len) = super::slice_ptr_parts_mut(buf);
assert!(len > 0 && len <= 0xFFFF);
Self::new_inner(
channel,
request,
Dir::PeripheralToMemory,
peri_addr as *const u32,
ptr as *mut u32,
len,
true,
W::size(),
options,
)
}
pub unsafe fn new_write<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
buf: &'a [W],
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
Self::new_write_raw(channel, request, buf, peri_addr, options)
}
pub unsafe fn new_write_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
buf: *const [W],
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
into_ref!(channel);
let (ptr, len) = super::slice_ptr_parts(buf);
assert!(len > 0 && len <= 0xFFFF);
Self::new_inner(
channel,
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
ptr as *mut u32,
len,
true,
W::size(),
options,
)
}
pub unsafe fn new_write_repeated<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
repeated: &'a W,
count: usize,
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
into_ref!(channel);
Self::new_inner(
channel,
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
repeated as *const W as *mut u32,
count,
false,
W::size(),
options,
)
}
unsafe fn new_inner(
channel: PeripheralRef<'a, C>,
_request: Request,
dir: Dir,
peri_addr: *const u32,
mem_addr: *mut u32,
mem_len: usize,
incr_mem: bool,
data_size: WordSize,
_options: TransferOptions,
) -> Self {
let ch = channel.regs().ch(channel.num());
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::SeqCst);
#[cfg(bdma_v2)]
critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request)));
let mut this = Self { channel };
this.clear_irqs();
#[cfg(dmamux)]
super::dmamux::configure_dmamux(&mut *this.channel, _request);
ch.par().write_value(peri_addr as u32);
ch.mar().write_value(mem_addr as u32);
ch.ndtr().write(|w| w.set_ndt(mem_len as u16));
ch.cr().write(|w| {
w.set_psize(data_size.into());
w.set_msize(data_size.into());
if incr_mem {
w.set_minc(vals::Inc::ENABLED);
} else {
w.set_minc(vals::Inc::DISABLED);
}
w.set_dir(dir.into());
w.set_teie(true);
w.set_tcie(true);
w.set_en(true);
});
this
}
fn clear_irqs(&mut self) {
unsafe {
self.channel.regs().ifcr().write(|w| {
w.set_tcif(self.channel.num(), true);
w.set_teif(self.channel.num(), true);
})
}
}
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
// Disable the channel. Keep the IEs enabled so the irqs still fire.
unsafe {
ch.cr().write(|w| {
w.set_teie(true);
w.set_tcie(true);
})
}
}
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
unsafe { ch.cr().read() }.en()
}
/// Gets the total remaining transfers for the channel
/// Note: this will be zero for transfers that completed without cancellation.
pub fn get_remaining_transfers(&self) -> u16 {
let ch = self.channel.regs().ch(self.channel.num());
unsafe { ch.ndtr().read() }.ndt()
}
pub fn blocking_wait(mut self) {
while self.is_running() {}
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
core::mem::forget(self);
}
}
impl<'a, C: Channel> Drop for Transfer<'a, C> {
fn drop(&mut self) {
self.request_stop();
while self.is_running() {}
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
}
}
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
impl<'a, C: Channel> Future for Transfer<'a, C> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
STATE.ch_wakers[self.channel.index()].register(cx.waker());
if self.is_running() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}

View file

@ -1,15 +1,46 @@
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::sync::atomic::{fence, Ordering};
use core::task::Waker;
use core::task::{Context, Poll, Waker};
use embassy_cortex_m::interrupt::Priority;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use pac::dma::regs;
use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize};
use super::word::{Word, WordSize};
use super::Dir;
use crate::_generated::DMA_CHANNEL_COUNT;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::pac::dma::{regs, vals};
use crate::pac::dma::vals;
use crate::{interrupt, pac};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct TransferOptions {
/// Peripheral burst transfer configuration
pub pburst: Burst,
/// Memory burst transfer configuration
pub mburst: Burst,
/// Flow control configuration
pub flow_ctrl: FlowControl,
/// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
pub fifo_threshold: Option<FifoThreshold>,
}
impl Default for TransferOptions {
fn default() -> Self {
Self {
pburst: Burst::Single,
mburst: Burst::Single,
flow_ctrl: FlowControl::Dma,
fifo_threshold: None,
}
}
}
impl From<WordSize> for vals::Size {
fn from(raw: WordSize) -> Self {
match raw {
@ -20,6 +51,28 @@ impl From<WordSize> for vals::Size {
}
}
impl From<Dir> for vals::Dir {
fn from(raw: Dir) -> Self {
match raw {
Dir::MemoryToPeripheral => Self::MEMORYTOPERIPHERAL,
Dir::PeripheralToMemory => Self::PERIPHERALTOMEMORY,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Burst {
/// Single transfer
Single,
/// Incremental burst of 4 beats
Incr4,
/// Incremental burst of 8 beats
Incr8,
/// Incremental burst of 16 beats
Incr16,
}
impl From<Burst> for vals::Burst {
fn from(burst: Burst) -> Self {
match burst {
@ -31,6 +84,15 @@ impl From<Burst> for vals::Burst {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FlowControl {
/// Flow control by DMA
Dma,
/// Flow control by peripheral
Peripheral,
}
impl From<FlowControl> for vals::Pfctrl {
fn from(flow: FlowControl) -> Self {
match flow {
@ -40,6 +102,19 @@ impl From<FlowControl> for vals::Pfctrl {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoThreshold {
/// 1/4 full FIFO
Quarter,
/// 1/2 full FIFO
Half,
/// 3/4 full FIFO
ThreeQuarters,
/// Full FIFO
Full,
}
impl From<FifoThreshold> for vals::Fth {
fn from(value: FifoThreshold) -> Self {
match value {
@ -51,27 +126,15 @@ impl From<FifoThreshold> for vals::Fth {
}
}
struct ChannelState {
waker: AtomicWaker,
}
impl ChannelState {
const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
struct State {
channels: [ChannelState; DMA_CHANNEL_COUNT],
ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT],
}
impl State {
const fn new() -> Self {
const CH: ChannelState = ChannelState::new();
const AW: AtomicWaker = AtomicWaker::new();
Self {
channels: [CH; DMA_CHANNEL_COUNT],
ch_wakers: [AW; DMA_CHANNEL_COUNT],
}
}
}
@ -92,158 +155,183 @@ pub(crate) unsafe fn init(irq_priority: Priority) {
foreach_dma_channel! {
($channel_peri:ident, $dma_peri:ident, dma, $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,
vals::Dir::MEMORYTOPERIPHERAL,
reg_addr as *const u32,
ptr as *mut u32,
len,
true,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
impl sealed::Channel for crate::peripherals::$channel_peri {
fn regs(&self) -> pac::dma::Dma {
pac::$dma_peri
}
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
low_level_api::start_transfer(
pac::$dma_peri,
$channel_num,
request,
vals::Dir::MEMORYTOPERIPHERAL,
reg_addr as *const u32,
repeated as *mut u32,
count,
false,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
fn num(&self) -> usize {
$channel_num
}
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,
vals::Dir::PERIPHERALTOMEMORY,
reg_addr as *const u32,
ptr as *mut u32,
len,
true,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
);
fn index(&self) -> usize {
$index
}
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,
) {
low_level_api::start_dbm_transfer(
pac::$dma_peri,
$channel_num,
request,
vals::Dir::PERIPHERALTOMEMORY,
reg_addr as *const u32,
buffer0 as *mut u32,
buffer1 as *mut u32,
buffer_len,
true,
vals::Size::from(W::bits()),
options,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
);
}
unsafe fn set_buffer0<W: Word>(&mut self, buffer: *mut W) {
low_level_api::set_dbm_buffer0(pac::$dma_peri, $channel_num, buffer as *mut u32);
}
unsafe fn set_buffer1<W: Word>(&mut self, buffer: *mut W) {
low_level_api::set_dbm_buffer1(pac::$dma_peri, $channel_num, buffer as *mut u32);
}
unsafe fn is_buffer0_accessible(&mut self) -> bool {
low_level_api::is_buffer0_accessible(pac::$dma_peri, $channel_num)
}
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);
unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
}
}
}
impl crate::dma::Channel for crate::peripherals::$channel_peri { }
impl Channel for crate::peripherals::$channel_peri {}
};
}
mod low_level_api {
/// Safety: Must be called with a matching set of parameters for a valid dma channel
pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: usize) {
let cr = dma.st(channel_num).cr();
let isr = dma.isr(channel_num / 4).read();
if isr.teif(channel_num % 4) {
panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
}
if isr.tcif(channel_num % 4) && cr.read().tcie() {
/* acknowledge transfer complete interrupt */
dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
STATE.ch_wakers[index].wake();
}
}
#[cfg(any(dma_v2, dmamux))]
pub type Request = u8;
#[cfg(not(any(dma_v2, dmamux)))]
pub type Request = ();
#[cfg(dmamux)]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
#[cfg(not(dmamux))]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
pub(crate) mod sealed {
use super::*;
pub unsafe fn start_transfer(
dma: pac::dma::Dma,
channel_number: u8,
pub trait Channel {
fn regs(&self) -> pac::dma::Dma;
fn num(&self) -> usize;
fn index(&self) -> usize;
fn on_irq();
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
pub unsafe fn new_read<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
dir: vals::Dir,
peri_addr: *mut W,
buf: &'a mut [W],
options: TransferOptions,
) -> Self {
Self::new_read_raw(channel, request, peri_addr, buf, options)
}
pub unsafe fn new_read_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
peri_addr: *mut W,
buf: *mut [W],
options: TransferOptions,
) -> Self {
into_ref!(channel);
let (ptr, len) = super::slice_ptr_parts_mut(buf);
assert!(len > 0 && len <= 0xFFFF);
Self::new_inner(
channel,
request,
Dir::PeripheralToMemory,
peri_addr as *const u32,
ptr as *mut u32,
len,
true,
W::size(),
options,
)
}
pub unsafe fn new_write<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
buf: &'a [W],
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
Self::new_write_raw(channel, request, buf, peri_addr, options)
}
pub unsafe fn new_write_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
buf: *const [W],
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
into_ref!(channel);
let (ptr, len) = super::slice_ptr_parts(buf);
assert!(len > 0 && len <= 0xFFFF);
Self::new_inner(
channel,
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
ptr as *mut u32,
len,
true,
W::size(),
options,
)
}
pub unsafe fn new_write_repeated<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
repeated: &'a W,
count: usize,
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
into_ref!(channel);
Self::new_inner(
channel,
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
repeated as *const W as *mut u32,
count,
false,
W::size(),
options,
)
}
unsafe fn new_inner(
channel: PeripheralRef<'a, C>,
_request: Request,
dir: Dir,
peri_addr: *const u32,
mem_addr: *mut u32,
mem_len: usize,
incr_mem: bool,
data_size: vals::Size,
data_size: WordSize,
options: TransferOptions,
#[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux,
#[cfg(dmamux)] dmamux_ch_num: u8,
) {
#[cfg(dmamux)]
super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
) -> Self {
let ch = channel.regs().st(channel.num());
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::SeqCst);
reset_status(dma, channel_number);
let mut this = Self { channel };
this.clear_irqs();
#[cfg(dmamux)]
super::dmamux::configure_dmamux(&mut *this.channel, _request);
let ch = dma.st(channel_number as _);
ch.par().write_value(peri_addr as u32);
ch.m0ar().write_value(mem_addr as u32);
ch.ndtr().write_value(regs::Ndtr(mem_len as _));
@ -258,15 +346,14 @@ mod low_level_api {
}
});
ch.cr().write(|w| {
w.set_dir(dir);
w.set_msize(data_size);
w.set_psize(data_size);
w.set_dir(dir.into());
w.set_msize(data_size.into());
w.set_psize(data_size.into());
w.set_pl(vals::Pl::VERYHIGH);
if incr_mem {
w.set_minc(vals::Inc::INCREMENTED);
} else {
w.set_minc(vals::Inc::FIXED);
}
w.set_minc(match incr_mem {
true => vals::Inc::INCREMENTED,
false => vals::Inc::FIXED,
});
w.set_pinc(vals::Inc::FIXED);
w.set_teie(true);
w.set_tcie(true);
@ -274,7 +361,7 @@ mod low_level_api {
w.set_trbuff(true);
#[cfg(dma_v2)]
w.set_chsel(request);
w.set_chsel(_request);
w.set_pburst(options.pburst.into());
w.set_mburst(options.mburst.into());
@ -282,159 +369,232 @@ mod low_level_api {
w.set_en(true);
});
this
}
pub unsafe fn start_dbm_transfer(
dma: pac::dma::Dma,
channel_number: u8,
request: Request,
dir: vals::Dir,
peri_addr: *const u32,
mem0_addr: *mut u32,
mem1_addr: *mut u32,
mem_len: usize,
incr_mem: bool,
data_size: vals::Size,
options: TransferOptions,
#[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux,
#[cfg(dmamux)] dmamux_ch_num: u8,
) {
#[cfg(dmamux)]
super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
fn clear_irqs(&mut self) {
let isrn = self.channel.num() / 4;
let isrbit = self.channel.num() % 4;
trace!(
"Starting DBM transfer with 0: 0x{:x}, 1: 0x{:x}, len: 0x{:x}",
mem0_addr as u32,
mem1_addr as u32,
mem_len
);
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::SeqCst);
reset_status(dma, channel_number);
let ch = dma.st(channel_number as _);
ch.par().write_value(peri_addr as u32);
ch.m0ar().write_value(mem0_addr as u32);
// configures the second buffer for DBM
ch.m1ar().write_value(mem1_addr as u32);
ch.ndtr().write_value(regs::Ndtr(mem_len as _));
ch.cr().write(|w| {
w.set_dir(dir);
w.set_msize(data_size);
w.set_psize(data_size);
w.set_pl(vals::Pl::VERYHIGH);
if incr_mem {
w.set_minc(vals::Inc::INCREMENTED);
} else {
w.set_minc(vals::Inc::FIXED);
unsafe {
self.channel.regs().ifcr(isrn).write(|w| {
w.set_tcif(isrbit, true);
w.set_teif(isrbit, true);
})
}
w.set_pinc(vals::Inc::FIXED);
w.set_teie(true);
w.set_tcie(true);
#[cfg(dma_v1)]
w.set_trbuff(true);
#[cfg(dma_v2)]
w.set_chsel(request);
// enable double buffered mode
w.set_dbm(vals::Dbm::ENABLED);
w.set_pburst(options.pburst.into());
w.set_mburst(options.mburst.into());
w.set_pfctrl(options.flow_ctrl.into());
w.set_en(true);
});
}
pub unsafe fn set_dbm_buffer0(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) {
// get a handle on the channel itself
let ch = dma.st(channel_number as _);
// change M0AR to the new address
ch.m0ar().write_value(mem_addr as _);
}
pub unsafe fn set_dbm_buffer1(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) {
// get a handle on the channel itself
let ch = dma.st(channel_number as _);
// change M1AR to the new address
ch.m1ar().write_value(mem_addr as _);
}
pub unsafe fn is_buffer0_accessible(dma: pac::dma::Dma, channel_number: u8) -> bool {
// get a handle on the channel itself
let ch = dma.st(channel_number as _);
// check the current target register value
ch.cr().read().ct() == vals::Ct::MEMORY1
}
/// Stops the DMA channel.
pub unsafe fn request_stop(dma: pac::dma::Dma, channel_number: u8) {
// get a handle on the channel itself
let ch = dma.st(channel_number as _);
pub fn request_stop(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
// Disable the channel. Keep the IEs enabled so the irqs still fire.
unsafe {
ch.cr().write(|w| {
w.set_teie(true);
w.set_tcie(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: pac::dma::Dma, ch: u8) -> bool {
// get a handle on the channel itself
let ch = dma.st(ch as _);
// Get whether it's enabled (running)
ch.cr().read().en()
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
unsafe { ch.cr().read() }.en()
}
/// 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: pac::dma::Dma, ch: u8) -> u16 {
// get a handle on the channel itself
let ch = dma.st(ch as _);
// read the remaining transfer count. If this is zero, the transfer completed fully.
ch.ndtr().read().ndt()
pub fn get_remaining_transfers(&self) -> u16 {
let ch = self.channel.regs().st(self.channel.num());
unsafe { ch.ndtr().read() }.ndt()
}
/// 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);
pub fn blocking_wait(mut self) {
while self.is_running() {}
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
core::mem::forget(self);
}
}
pub unsafe fn reset_status(dma: pac::dma::Dma, channel_number: u8) {
let isrn = channel_number as usize / 4;
let isrbit = channel_number as usize % 4;
impl<'a, C: Channel> Drop for Transfer<'a, C> {
fn drop(&mut self) {
self.request_stop();
while self.is_running() {}
dma.ifcr(isrn).write(|w| {
w.set_tcif(isrbit, true);
w.set_teif(isrbit, true);
});
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
}
}
/// Safety: Must be called with a matching set of parameters for a valid dma channel
pub unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: u8, state_index: u8) {
let channel_num = channel_num as usize;
let state_index = state_index as usize;
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
impl<'a, C: Channel> Future for Transfer<'a, C> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
STATE.ch_wakers[self.channel.index()].register(cx.waker());
let cr = dma.st(channel_num).cr();
let isr = dma.isr(channel_num / 4).read();
if isr.teif(channel_num % 4) {
panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
}
if isr.tcif(channel_num % 4) && cr.read().tcie() {
/* acknowledge transfer complete interrupt */
dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
STATE.channels[state_index].waker.wake();
if self.is_running() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}
// ==================================
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct DoubleBuffered<'a, C: Channel, W: Word> {
channel: PeripheralRef<'a, C>,
_phantom: PhantomData<W>,
}
impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
pub unsafe fn new_read(
channel: impl Peripheral<P = C> + 'a,
_request: Request,
peri_addr: *mut W,
buf0: *mut W,
buf1: *mut W,
len: usize,
options: TransferOptions,
) -> Self {
into_ref!(channel);
assert!(len > 0 && len <= 0xFFFF);
let dir = Dir::PeripheralToMemory;
let data_size = W::size();
let channel_number = channel.num();
let dma = channel.regs();
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::SeqCst);
let mut this = Self {
channel,
_phantom: PhantomData,
};
this.clear_irqs();
#[cfg(dmamux)]
super::dmamux::configure_dmamux(&mut *this.channel, _request);
let ch = dma.st(channel_number);
ch.par().write_value(peri_addr as u32);
ch.m0ar().write_value(buf0 as u32);
ch.m1ar().write_value(buf1 as u32);
ch.ndtr().write_value(regs::Ndtr(len as _));
ch.fcr().write(|w| {
if let Some(fth) = options.fifo_threshold {
// FIFO mode
w.set_dmdis(vals::Dmdis::DISABLED);
w.set_fth(fth.into());
} else {
// Direct mode
w.set_dmdis(vals::Dmdis::ENABLED);
}
});
ch.cr().write(|w| {
w.set_dir(dir.into());
w.set_msize(data_size.into());
w.set_psize(data_size.into());
w.set_pl(vals::Pl::VERYHIGH);
w.set_minc(vals::Inc::INCREMENTED);
w.set_pinc(vals::Inc::FIXED);
w.set_teie(true);
w.set_tcie(true);
#[cfg(dma_v1)]
w.set_trbuff(true);
#[cfg(dma_v2)]
w.set_chsel(_request);
w.set_pburst(options.pburst.into());
w.set_mburst(options.mburst.into());
w.set_pfctrl(options.flow_ctrl.into());
w.set_en(true);
});
this
}
fn clear_irqs(&mut self) {
let channel_number = self.channel.num();
let dma = self.channel.regs();
let isrn = channel_number / 4;
let isrbit = channel_number % 4;
unsafe {
dma.ifcr(isrn).write(|w| {
w.set_tcif(isrbit, true);
w.set_teif(isrbit, true);
})
}
}
pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
let ch = self.channel.regs().st(self.channel.num());
ch.m0ar().write_value(buffer as _);
}
pub unsafe fn set_buffer1(&mut self, buffer: *mut W) {
let ch = self.channel.regs().st(self.channel.num());
ch.m1ar().write_value(buffer as _);
}
pub fn is_buffer0_accessible(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1
}
pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker);
}
pub fn request_stop(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
// Disable the channel. Keep the IEs enabled so the irqs still fire.
unsafe {
ch.cr().write(|w| {
w.set_teie(true);
w.set_tcie(true);
})
}
}
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().st(self.channel.num());
unsafe { ch.cr().read() }.en()
}
/// Gets the total remaining transfers for the channel
/// Note: this will be zero for transfers that completed without cancellation.
pub fn get_remaining_transfers(&self) -> u16 {
let ch = self.channel.regs().st(self.channel.num());
unsafe { ch.ndtr().read() }.ndt()
}
pub fn blocking_wait(mut self) {
while self.is_running() {}
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
core::mem::forget(self);
}
}
impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> {
fn drop(&mut self) {
self.request_stop();
while self.is_running() {}
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
}
}

View file

@ -2,8 +2,8 @@
use crate::{pac, peripherals};
pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_ch_num: u8, request: u8) {
let ch_mux_regs = dmamux_regs.ccr(dmamux_ch_num as _);
pub(crate) unsafe fn configure_dmamux<M: MuxChannel>(channel: &mut M, request: u8) {
let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num());
ch_mux_regs.write(|reg| {
reg.set_nbreq(0);
reg.set_dmareq_id(request);
@ -14,11 +14,11 @@ pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_c
});
}
pub(crate) mod sealed {
pub(crate) mod dmamux_sealed {
use super::*;
pub trait MuxChannel {
const DMAMUX_CH_NUM: u8;
const DMAMUX_REGS: pac::dmamux::Dmamux;
fn mux_regs(&self) -> pac::dmamux::Dmamux;
fn mux_num(&self) -> usize;
}
}
@ -26,15 +26,19 @@ pub struct DMAMUX1;
#[cfg(stm32h7)]
pub struct DMAMUX2;
pub trait MuxChannel: sealed::MuxChannel + super::Channel {
pub trait MuxChannel: dmamux_sealed::MuxChannel {
type Mux;
}
foreach_dma_channel! {
($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $index:expr, {dmamux: $dmamux:ident, dmamux_channel: $dmamux_channel:expr}) => {
impl sealed::MuxChannel for peripherals::$channel_peri {
const DMAMUX_CH_NUM: u8 = $dmamux_channel;
const DMAMUX_REGS: pac::dmamux::Dmamux = pac::$dmamux;
impl dmamux_sealed::MuxChannel for peripherals::$channel_peri {
fn mux_regs(&self) -> pac::dmamux::Dmamux {
pac::$dmamux
}
fn mux_num(&self) -> usize {
$dmamux_channel
}
}
impl MuxChannel for peripherals::$channel_peri {
type Mux = $dmamux;

View file

@ -1,13 +1,31 @@
use core::sync::atomic::{fence, Ordering};
use core::task::Waker;
#![macro_use]
use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{fence, Ordering};
use core::task::{Context, Poll};
use embassy_cortex_m::interrupt::Priority;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use super::{Request, TransferOptions, Word, WordSize};
use super::word::{Word, WordSize};
use super::Dir;
use crate::_generated::GPDMA_CHANNEL_COUNT;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::pac::gpdma::{vals, Gpdma};
use crate::{interrupt, pac};
use crate::pac;
use crate::pac::gpdma::vals;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct TransferOptions {}
impl Default for TransferOptions {
fn default() -> Self {
Self {}
}
}
impl From<WordSize> for vals::ChTr1Dw {
fn from(raw: WordSize) -> Self {
@ -19,27 +37,15 @@ impl From<WordSize> for vals::ChTr1Dw {
}
}
struct ChannelState {
waker: AtomicWaker,
}
impl ChannelState {
const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
struct State {
channels: [ChannelState; GPDMA_CHANNEL_COUNT],
ch_wakers: [AtomicWaker; GPDMA_CHANNEL_COUNT],
}
impl State {
const fn new() -> Self {
const CH: ChannelState = ChannelState::new();
const AW: AtomicWaker = AtomicWaker::new();
Self {
channels: [CH; GPDMA_CHANNEL_COUNT],
ch_wakers: [AW; GPDMA_CHANNEL_COUNT],
}
}
}
@ -47,10 +53,12 @@ impl State {
static STATE: State = State::new();
/// safety: must be called only once
pub(crate) unsafe fn init() {
pub(crate) unsafe fn init(irq_priority: Priority) {
foreach_interrupt! {
($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
interrupt::$irq::steal().enable();
let irq = crate::interrupt::$irq::steal();
irq.set_priority(irq_priority);
irq.enable();
};
}
crate::_generated::init_gpdma();
@ -58,117 +66,171 @@ pub(crate) unsafe fn init() {
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,
)
impl sealed::Channel for crate::peripherals::$channel_peri {
fn regs(&self) -> pac::gpdma::Gpdma {
pac::$dma_peri
}
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
low_level_api::start_transfer(
pac::$dma_peri,
$channel_num,
request,
low_level_api::Dir::MemoryToPeripheral,
reg_addr as *const u32,
repeated as *mut u32,
count,
false,
W::bits(),
options,
)
fn num(&self) -> usize {
$channel_num
}
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,
);
fn index(&self) -> usize {
$index
}
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);
unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
}
}
}
impl crate::dma::Channel for crate::peripherals::$channel_peri { }
impl Channel for crate::peripherals::$channel_peri {}
};
}
mod low_level_api {
use super::*;
/// Safety: Must be called with a matching set of parameters for a valid dma channel
pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, index: usize) {
let ch = dma.ch(channel_num);
let sr = ch.sr().read();
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Dir {
MemoryToPeripheral,
PeripheralToMemory,
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
);
}
pub unsafe fn start_transfer(
dma: Gpdma,
channel_number: u8,
if sr.suspf() || sr.tcf() {
// disable all xxIEs to prevent the irq from firing again.
ch.cr().write(|_| {});
// Wake the future. It'll look at tcf and see it's set.
STATE.ch_wakers[index].wake();
}
}
pub type Request = u8;
#[cfg(dmamux)]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
#[cfg(not(dmamux))]
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
pub(crate) mod sealed {
use super::*;
pub trait Channel {
fn regs(&self) -> pac::gpdma::Gpdma;
fn num(&self) -> usize;
fn index(&self) -> usize;
fn on_irq();
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
pub unsafe fn new_read<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
peri_addr: *mut W,
buf: &'a mut [W],
options: TransferOptions,
) -> Self {
Self::new_read_raw(channel, request, peri_addr, buf, options)
}
pub unsafe fn new_read_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
peri_addr: *mut W,
buf: *mut [W],
options: TransferOptions,
) -> Self {
into_ref!(channel);
let (ptr, len) = super::slice_ptr_parts_mut(buf);
assert!(len > 0 && len <= 0xFFFF);
Self::new_inner(
channel,
request,
Dir::PeripheralToMemory,
peri_addr as *const u32,
ptr as *mut u32,
len,
true,
W::size(),
options,
)
}
pub unsafe fn new_write<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
buf: &'a [W],
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
Self::new_write_raw(channel, request, buf, peri_addr, options)
}
pub unsafe fn new_write_raw<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
buf: *const [W],
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
into_ref!(channel);
let (ptr, len) = super::slice_ptr_parts(buf);
assert!(len > 0 && len <= 0xFFFF);
Self::new_inner(
channel,
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
ptr as *mut u32,
len,
true,
W::size(),
options,
)
}
pub unsafe fn new_write_repeated<W: Word>(
channel: impl Peripheral<P = C> + 'a,
request: Request,
repeated: &'a W,
count: usize,
peri_addr: *mut W,
options: TransferOptions,
) -> Self {
into_ref!(channel);
Self::new_inner(
channel,
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
repeated as *const W as *mut u32,
count,
false,
W::size(),
options,
)
}
unsafe fn new_inner(
channel: PeripheralRef<'a, C>,
request: Request,
dir: Dir,
peri_addr: *const u32,
@ -176,24 +238,19 @@ mod low_level_api {
mem_len: usize,
incr_mem: bool,
data_size: WordSize,
options: TransferOptions,
) {
assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported");
assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported");
assert!(
options.flow_ctrl == crate::dma::FlowControl::Dma,
"Peripheral flow control not supported"
);
assert!(options.fifo_threshold.is_none(), "FIFO mode not supported");
_options: TransferOptions,
) -> Self {
let ch = channel.regs().ch(channel.num());
// "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::SeqCst);
let ch = dma.ch(channel_number as _);
let this = Self { channel };
#[cfg(dmamux)]
super::dmamux::configure_dmamux(&mut *this.channel, request);
// Reset ch
ch.cr().write(|w| w.set_reset(true));
ch.llr().write(|_| {}); // no linked list
ch.tr1().write(|w| {
w.set_sdw(data_size.into());
@ -234,72 +291,66 @@ mod low_level_api {
// Start it
w.set_en(true);
});
this
}
/// 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 _);
pub fn request_stop(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
// Disable the channel. Keep the IEs enabled so the irqs still fire.
unsafe {
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().tcf()
pub fn is_running(&mut self) -> bool {
let ch = self.channel.regs().ch(self.channel.num());
!unsafe { ch.sr().read() }.tcf()
}
/// 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()
pub fn get_remaining_transfers(&self) -> u16 {
let ch = self.channel.regs().ch(self.channel.num());
unsafe { 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);
pub fn blocking_wait(mut self) {
while self.is_running() {}
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
core::mem::forget(self);
}
}
/// 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;
impl<'a, C: Channel> Drop for Transfer<'a, C> {
fn drop(&mut self) {
self.request_stop();
while self.is_running() {}
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
);
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
fence(Ordering::SeqCst);
}
}
if sr.suspf() || sr.tcf() {
// disable all xxIEs to prevent the irq from firing again.
ch.cr().write(|_| {});
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
impl<'a, C: Channel> Future for Transfer<'a, C> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
STATE.ch_wakers[self.channel.index()].register(cx.waker());
// Wake the future. It'll look at tcf and see it's set.
STATE.channels[state_index].waker.wake();
if self.is_running() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}

View file

@ -1,329 +1,47 @@
#[cfg(bdma)]
pub(crate) mod bdma;
#[cfg(dma)]
pub(crate) mod dma;
#[cfg(dma)]
pub use dma::*;
// stm32h7 has both dma and bdma. In that case, we export dma as "main" dma,
// and bdma as "secondary", under `embassy_stm32::dma::bdma`.
#[cfg(all(bdma, dma))]
pub mod bdma;
#[cfg(all(bdma, not(dma)))]
pub(crate) mod bdma;
#[cfg(all(bdma, not(dma)))]
pub use bdma::*;
#[cfg(gpdma)]
pub(crate) mod gpdma;
#[cfg(gpdma)]
pub use gpdma::*;
#[cfg(dmamux)]
mod dmamux;
#[cfg(gpdma)]
mod gpdma;
use core::future::Future;
pub mod word;
use core::mem;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
#[cfg(any(dma, bdma))]
use embassy_cortex_m::interrupt::Priority;
use embassy_hal_common::{impl_peripheral, into_ref};
use embassy_hal_common::impl_peripheral;
#[cfg(dmamux)]
pub use self::dmamux::*;
use crate::Peripheral;
#[cfg(feature = "unstable-pac")]
pub mod low_level {
pub use super::transfers::*;
}
pub(crate) use transfers::*;
#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))]
pub type Request = u8;
#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))]
pub type Request = ();
pub(crate) mod sealed {
use super::*;
pub trait Word {}
pub trait Channel {
/// Starts this channel for writing a stream of words.
///
/// Safety:
/// - `buf` must point to a valid buffer for DMA reading.
/// - `buf` must be alive for the entire duration of the DMA transfer.
/// - `reg_addr` must be a valid peripheral register address to write to.
unsafe fn start_write<W: super::Word>(
&mut self,
request: Request,
buf: *const [W],
reg_addr: *mut W,
options: TransferOptions,
);
/// Starts this channel for writing a word repeatedly.
///
/// Safety:
/// - `reg_addr` must be a valid peripheral register address to write to.
unsafe fn start_write_repeated<W: super::Word>(
&mut self,
request: Request,
repeated: *const W,
count: usize,
reg_addr: *mut W,
options: TransferOptions,
);
/// Starts this channel for reading a stream of words.
///
/// Safety:
/// - `buf` must point to a valid buffer for DMA writing.
/// - `buf` must be alive for the entire duration of the DMA transfer.
/// - `reg_addr` must be a valid peripheral register address to read from.
unsafe fn start_read<W: super::Word>(
&mut self,
request: Request,
reg_addr: *const W,
buf: *mut [W],
options: TransferOptions,
);
/// DMA double-buffered mode is unsafe as UB can happen when the hardware writes to a buffer currently owned by the software
/// more information can be found here: https://github.com/embassy-rs/embassy/issues/702
/// This feature is now used solely for the purposes of implementing giant DMA transfers required for DCMI
unsafe fn start_double_buffered_read<W: super::Word>(
&mut self,
request: Request,
reg_addr: *const W,
buffer0: *mut W,
buffer1: *mut W,
buffer_len: usize,
options: TransferOptions,
);
unsafe fn set_buffer0<W: super::Word>(&mut self, buffer: *mut W);
unsafe fn set_buffer1<W: super::Word>(&mut self, buffer: *mut W);
unsafe fn is_buffer0_accessible(&mut self) -> bool;
/// Requests the channel to stop.
/// NOTE: The channel does not immediately stop, you have to wait
/// for `is_running() = false`.
fn request_stop(&mut self);
/// Returns whether this channel is running or stopped.
///
/// The channel stops running when it either completes or is manually stopped.
fn is_running(&self) -> bool;
/// Returns the total number of remaining transfers.
fn remaining_transfers(&mut self) -> u16;
/// Sets the waker that is called when this channel stops (either completed or manually stopped)
fn set_waker(&mut self, waker: &Waker);
/// This is called when this channel triggers an interrupt.
/// Note: Because some channels share an interrupt, this function might be
/// called for a channel that didn't trigger an interrupt.
fn on_irq();
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
OneByte,
TwoBytes,
FourBytes,
enum Dir {
MemoryToPeripheral,
PeripheralToMemory,
}
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;
}
impl sealed::Word for u8 {}
impl Word for u8 {
fn bits() -> WordSize {
WordSize::OneByte
}
}
impl sealed::Word for u16 {}
impl Word for u16 {
fn bits() -> WordSize {
WordSize::TwoBytes
}
}
impl sealed::Word for u32 {}
impl Word for u32 {
fn bits() -> WordSize {
WordSize::FourBytes
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Burst {
/// Single transfer
Single,
/// Incremental burst of 4 beats
Incr4,
/// Incremental burst of 8 beats
Incr8,
/// Incremental burst of 16 beats
Incr16,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FlowControl {
/// Flow control by DMA
Dma,
/// Flow control by peripheral
Peripheral,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoThreshold {
/// 1/4 full FIFO
Quarter,
/// 1/2 full FIFO
Half,
/// 3/4 full FIFO
ThreeQuarters,
/// Full FIFO
Full,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferOptions {
/// Peripheral burst transfer configuration
pub pburst: Burst,
/// Memory burst transfer configuration
pub mburst: Burst,
/// Flow control configuration
pub flow_ctrl: FlowControl,
/// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
pub fifo_threshold: Option<FifoThreshold>,
}
impl Default for TransferOptions {
fn default() -> Self {
Self {
pburst: Burst::Single,
mburst: Burst::Single,
flow_ctrl: FlowControl::Dma,
fifo_threshold: None,
}
}
}
mod transfers {
use embassy_hal_common::PeripheralRef;
use super::*;
#[allow(unused)]
pub fn read<'a, W: Word>(
channel: impl Peripheral<P = impl Channel> + 'a,
request: Request,
reg_addr: *mut W,
buf: &'a mut [W],
) -> impl Future<Output = ()> + 'a {
assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
into_ref!(channel);
unsafe { channel.start_read::<W>(request, reg_addr, buf, Default::default()) };
Transfer::new(channel)
}
#[allow(unused)]
pub fn write<'a, W: Word>(
channel: impl Peripheral<P = impl Channel> + 'a,
request: Request,
buf: &'a [W],
reg_addr: *mut W,
) -> impl Future<Output = ()> + 'a {
assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
into_ref!(channel);
unsafe { channel.start_write::<W>(request, buf, reg_addr, Default::default()) };
Transfer::new(channel)
}
#[allow(unused)]
pub fn write_repeated<'a, W: Word>(
channel: impl Peripheral<P = impl Channel> + 'a,
request: Request,
repeated: *const W,
count: usize,
reg_addr: *mut W,
) -> impl Future<Output = ()> + 'a {
into_ref!(channel);
unsafe { channel.start_write_repeated::<W>(request, repeated, count, reg_addr, Default::default()) };
Transfer::new(channel)
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub(crate) struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
pub(crate) fn new(channel: impl Peripheral<P = C> + 'a) -> Self {
into_ref!(channel);
Self { channel }
}
}
impl<'a, C: Channel> Drop for Transfer<'a, C> {
fn drop(&mut self) {
self.channel.request_stop();
while self.channel.is_running() {}
}
}
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
impl<'a, C: Channel> Future for Transfer<'a, C> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.channel.set_waker(cx.waker());
if self.channel.is_running() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}
}
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
pub struct NoDma;
impl_peripheral!(NoDma);
// safety: must be called only once at startup
pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) {
#[cfg(bdma)]
bdma::init(bdma_priority);
#[cfg(dma)]
dma::init(dma_priority);
#[cfg(dmamux)]
dmamux::init();
#[cfg(gpdma)]
gpdma::init();
}
// TODO: replace transmutes with core::ptr::metadata once it's stable
#[allow(unused)]
pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) {
@ -334,3 +52,19 @@ pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) {
pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) {
unsafe { mem::transmute(slice) }
}
// safety: must be called only once at startup
pub(crate) unsafe fn init(
#[cfg(bdma)] bdma_priority: Priority,
#[cfg(dma)] dma_priority: Priority,
#[cfg(gpdma)] gpdma_priority: Priority,
) {
#[cfg(bdma)]
bdma::init(bdma_priority);
#[cfg(dma)]
dma::init(dma_priority);
#[cfg(gpdma)]
gpdma::init(gpdma_priority);
#[cfg(dmamux)]
dmamux::init();
}

View file

@ -0,0 +1,79 @@
#[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,
}
}
}
mod sealed {
pub trait Word {}
}
pub trait Word: sealed::Word + Default + Copy + 'static {
fn size() -> WordSize;
fn bits() -> usize;
}
macro_rules! impl_word {
(_, $T:ident, $bits:literal, $size:ident) => {
impl sealed::Word for $T {}
impl Word for $T {
fn bits() -> usize {
$bits
}
fn size() -> WordSize {
WordSize::$size
}
}
};
($T:ident, $uX:ident, $bits:literal, $size:ident) => {
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct $T(pub $uX);
impl_word!(_, $T, $bits, $size);
};
}
impl_word!(U1, u8, 1, OneByte);
impl_word!(U2, u8, 2, OneByte);
impl_word!(U3, u8, 3, OneByte);
impl_word!(U4, u8, 4, OneByte);
impl_word!(U5, u8, 5, OneByte);
impl_word!(U6, u8, 6, OneByte);
impl_word!(U7, u8, 7, OneByte);
impl_word!(_, u8, 8, OneByte);
impl_word!(U9, u16, 9, TwoBytes);
impl_word!(U10, u16, 10, TwoBytes);
impl_word!(U11, u16, 11, TwoBytes);
impl_word!(U12, u16, 12, TwoBytes);
impl_word!(U13, u16, 13, TwoBytes);
impl_word!(U14, u16, 14, TwoBytes);
impl_word!(U15, u16, 15, TwoBytes);
impl_word!(_, u16, 16, TwoBytes);
impl_word!(U17, u32, 17, FourBytes);
impl_word!(U18, u32, 18, FourBytes);
impl_word!(U19, u32, 19, FourBytes);
impl_word!(U20, u32, 20, FourBytes);
impl_word!(U21, u32, 21, FourBytes);
impl_word!(U22, u32, 22, FourBytes);
impl_word!(U23, u32, 23, FourBytes);
impl_word!(U24, u32, 24, FourBytes);
impl_word!(U25, u32, 25, FourBytes);
impl_word!(U26, u32, 26, FourBytes);
impl_word!(U27, u32, 27, FourBytes);
impl_word!(U28, u32, 28, FourBytes);
impl_word!(U29, u32, 29, FourBytes);
impl_word!(U30, u32, 30, FourBytes);
impl_word!(U31, u32, 31, FourBytes);
impl_word!(_, u32, 32, FourBytes);

View file

@ -2,7 +2,7 @@ use atomic_polyfill::{fence, Ordering};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE};
use crate::flash::FlashBank;
use crate::Peripheral;
@ -162,6 +162,35 @@ impl FlashRegion {
}
}
impl embedded_storage::nor_flash::ErrorType for Flash<'_> {
type Error = Error;
}
impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> {
const READ_SIZE: usize = 1;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(offset, bytes)
}
fn capacity(&self) -> usize {
FLASH_SIZE
}
}
impl embedded_storage::nor_flash::NorFlash for Flash<'_> {
const WRITE_SIZE: usize = WRITE_SIZE;
const ERASE_SIZE: usize = MAX_ERASE_SIZE;
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(offset, bytes)
}
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.blocking_erase(from, to)
}
}
foreach_flash_region! {
($type_name:ident, $write_size:literal, $erase_size:literal) => {
impl crate::_generated::flash_regions::$type_name<'_> {

View file

@ -7,6 +7,7 @@ mod common;
pub use common::*;
pub use crate::_generated::flash_regions::*;
pub use crate::_generated::MAX_ERASE_SIZE;
pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
#[derive(Debug)]

View file

@ -8,7 +8,7 @@ use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::dma::NoDma;
use crate::dma::{NoDma, Transfer};
use crate::gpio::sealed::AFType;
use crate::gpio::Pull;
use crate::i2c::{Error, Instance, SclPin, SdaPin};
@ -476,7 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let ch = &mut self.tx_dma;
let request = ch.request();
crate::dma::write(ch, request, write, dst)
Transfer::new_write(ch, request, write, dst, Default::default())
};
let state = T::state();
@ -576,7 +576,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let ch = &mut self.rx_dma;
let request = ch.request();
crate::dma::read(ch, request, src, buffer)
Transfer::new_read(ch, request, src, buffer, Default::default())
};
let state = T::state();

View file

@ -49,6 +49,8 @@ pub mod pwm;
pub mod qspi;
#[cfg(rng)]
pub mod rng;
#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))]
pub mod rtc;
#[cfg(sdmmc)]
pub mod sdmmc;
#[cfg(spi)]
@ -76,7 +78,6 @@ pub(crate) mod _generated {
// Reexports
pub use _generated::{peripherals, Peripherals};
pub use embassy_cortex_m::executor;
#[cfg(any(dma, bdma))]
use embassy_cortex_m::interrupt::Priority;
pub use embassy_cortex_m::interrupt::_export::interrupt;
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
@ -94,6 +95,8 @@ pub struct Config {
pub bdma_interrupt_priority: Priority,
#[cfg(dma)]
pub dma_interrupt_priority: Priority,
#[cfg(gpdma)]
pub gpdma_interrupt_priority: Priority,
}
impl Default for Config {
@ -106,6 +109,8 @@ impl Default for Config {
bdma_interrupt_priority: Priority::P0,
#[cfg(dma)]
dma_interrupt_priority: Priority::P0,
#[cfg(gpdma)]
gpdma_interrupt_priority: Priority::P0,
}
}
}
@ -149,6 +154,8 @@ pub fn init(config: Config) -> Peripherals {
config.bdma_interrupt_priority,
#[cfg(dma)]
config.dma_interrupt_priority,
#[cfg(gpdma)]
config.gpdma_interrupt_priority,
);
#[cfg(feature = "exti")]
exti::init();

View file

@ -5,7 +5,7 @@ pub mod enums;
use embassy_hal_common::{into_ref, PeripheralRef};
use enums::*;
use crate::dma::TransferOptions;
use crate::dma::Transfer;
use crate::gpio::sealed::AFType;
use crate::gpio::AnyPin;
use crate::pac::quadspi::Quadspi as Regs;
@ -230,9 +230,6 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
unsafe {
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
let request = self.dma.request();
let options = TransferOptions::default();
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
@ -241,12 +238,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
v.set_address(current_ar);
});
self.dma
.start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options);
let request = self.dma.request();
let transfer = Transfer::new_read(
&mut self.dma,
request,
T::REGS.dr().ptr() as *mut u8,
buf,
Default::default(),
);
T::REGS.cr().modify(|v| v.set_dmaen(true));
while self.dma.is_running() {}
transfer.blocking_wait();
}
}
@ -257,19 +260,22 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
unsafe {
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
let request = self.dma.request();
let options = TransferOptions::default();
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
self.dma
.start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options);
let request = self.dma.request();
let transfer = Transfer::new_write(
&mut self.dma,
request,
buf,
T::REGS.dr().ptr() as *mut u8,
Default::default(),
);
T::REGS.cr().modify(|v| v.set_dmaen(true));
while self.dma.is_running() {}
transfer.blocking_wait();
}
}

View file

@ -29,10 +29,66 @@ pub struct Config {
pub pclk1: Option<Hertz>,
pub pclk2: Option<Hertz>,
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
pub plli2s: Option<Hertz>,
pub pll48: bool,
}
unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
#[cfg(stm32f410)]
unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
None
}
// Not currently implemented, but will be in the future
#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
None
}
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
let min_div = 2;
let max_div = 7;
let target = match plli2s {
Some(target) => target,
None => return None,
};
// We loop through the possible divider values to find the best configuration. Looping
// through all possible "N" values would result in more iterations.
let (n, outdiv, output, _error) = (min_div..=max_div)
.filter_map(|outdiv| {
let target_vco_out = match target.checked_mul(outdiv) {
Some(x) => x,
None => return None,
};
let n = (target_vco_out + (vco_in >> 1)) / vco_in;
let vco_out = vco_in * n;
if !(100_000_000..=432_000_000).contains(&vco_out) {
return None;
}
let output = vco_out / outdiv;
let error = (output as i32 - target as i32).unsigned_abs();
Some((n, outdiv, output, error))
})
.min_by_key(|(_, _, _, error)| *error)?;
RCC.plli2scfgr().modify(|w| {
w.set_plli2sn(n as u16);
w.set_plli2sr(outdiv as u8);
});
Some(output)
}
unsafe fn setup_pll(
pllsrcclk: u32,
use_hse: bool,
pllsysclk: Option<u32>,
plli2s: Option<u32>,
pll48clk: bool,
) -> PllResults {
use crate::pac::rcc::vals::{Pllp, Pllsrc};
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
@ -43,6 +99,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
use_pll: false,
pllsysclk: None,
pll48clk: None,
plli2sclk: None,
};
}
// Input divisor from PLL source clock, must result to frequency in
@ -101,6 +158,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
use_pll: true,
pllsysclk: Some(real_pllsysclk),
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
plli2sclk: setup_i2s_pll(vco_in, plli2s),
}
}
@ -286,6 +344,10 @@ pub(crate) unsafe fn init(config: Config) {
pllsrcclk,
config.hse.is_some(),
if sysclk_on_pll { Some(sysclk) } else { None },
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
config.plli2s.map(|i2s| i2s.0),
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
None,
config.pll48,
);
@ -376,6 +438,13 @@ pub(crate) unsafe fn init(config: Config) {
while !RCC.cr().read().pllrdy() {}
}
#[cfg(not(stm32f410))]
if plls.plli2sclk.is_some() {
RCC.cr().modify(|w| w.set_plli2son(true));
while !RCC.cr().read().plli2srdy() {}
}
RCC.cfgr().modify(|w| {
w.set_ppre2(Ppre(ppre2_bits));
w.set_ppre1(Ppre(ppre1_bits));
@ -409,6 +478,12 @@ pub(crate) unsafe fn init(config: Config) {
ahb3: Hertz(hclk),
pll48: plls.pll48clk.map(Hertz),
#[cfg(not(stm32f410))]
plli2s: plls.plli2sclk.map(Hertz),
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai: None,
});
}
@ -416,6 +491,8 @@ struct PllResults {
use_pll: bool,
pllsysclk: Option<u32>,
pll48clk: Option<u32>,
#[allow(dead_code)]
plli2sclk: Option<u32>,
}
mod max {

View file

@ -60,6 +60,12 @@ pub struct Clocks {
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
pub pll48: Option<Hertz>,
#[cfg(all(rcc_f4, not(stm32f410)))]
pub plli2s: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai: Option<Hertz>,
#[cfg(stm32f1)]
pub adc: Hertz,

View file

@ -0,0 +1,203 @@
#[cfg(feature = "chrono")]
use core::convert::From;
#[cfg(feature = "chrono")]
use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
use super::byte_to_bcd2;
use crate::pac::rtc::Rtc;
/// Errors regarding the [`DateTime`] struct.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
InvalidYear,
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
InvalidMonth,
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
InvalidDay,
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
InvalidDayOfWeek(
/// The value of the DayOfWeek that was given.
u8,
),
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
InvalidHour,
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
InvalidMinute,
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
InvalidSecond,
}
/// Structure containing date and time information
pub struct DateTime {
/// 0..4095
pub year: u16,
/// 1..12, 1 is January
pub month: u8,
/// 1..28,29,30,31 depending on month
pub day: u8,
///
pub day_of_week: DayOfWeek,
/// 0..23
pub hour: u8,
/// 0..59
pub minute: u8,
/// 0..59
pub second: u8,
}
#[cfg(feature = "chrono")]
impl From<chrono::NaiveDateTime> for DateTime {
fn from(date_time: chrono::NaiveDateTime) -> Self {
Self {
year: (date_time.year() - 1970) as u16,
month: date_time.month() as u8,
day: date_time.day() as u8,
day_of_week: date_time.weekday().into(),
hour: date_time.hour() as u8,
minute: date_time.minute() as u8,
second: date_time.second() as u8,
}
}
}
#[cfg(feature = "chrono")]
impl From<DateTime> for chrono::NaiveDateTime {
fn from(date_time: DateTime) -> Self {
NaiveDate::from_ymd_opt(
(date_time.year + 1970) as i32,
date_time.month as u32,
date_time.day as u32,
)
.unwrap()
.and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32)
.unwrap()
}
}
/// A day of the week
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[allow(missing_docs)]
pub enum DayOfWeek {
Monday = 0,
Tuesday = 1,
Wednesday = 2,
Thursday = 3,
Friday = 4,
Saturday = 5,
Sunday = 6,
}
#[cfg(feature = "chrono")]
impl From<chrono::Weekday> for DayOfWeek {
fn from(weekday: Weekday) -> Self {
day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()
}
}
#[cfg(feature = "chrono")]
impl From<DayOfWeek> for chrono::Weekday {
fn from(weekday: DayOfWeek) -> Self {
match weekday {
DayOfWeek::Monday => Weekday::Mon,
DayOfWeek::Tuesday => Weekday::Tue,
DayOfWeek::Wednesday => Weekday::Wed,
DayOfWeek::Thursday => Weekday::Thu,
DayOfWeek::Friday => Weekday::Fri,
DayOfWeek::Saturday => Weekday::Sat,
DayOfWeek::Sunday => Weekday::Sun,
}
}
}
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
Ok(match v {
0 => DayOfWeek::Monday,
1 => DayOfWeek::Tuesday,
2 => DayOfWeek::Wednesday,
3 => DayOfWeek::Thursday,
4 => DayOfWeek::Friday,
5 => DayOfWeek::Saturday,
6 => DayOfWeek::Sunday,
x => return Err(Error::InvalidDayOfWeek(x)),
})
}
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
dotw as u8
}
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
if dt.year > 4095 {
Err(Error::InvalidYear)
} else if dt.month < 1 || dt.month > 12 {
Err(Error::InvalidMonth)
} else if dt.day < 1 || dt.day > 31 {
Err(Error::InvalidDay)
} else if dt.hour > 23 {
Err(Error::InvalidHour)
} else if dt.minute > 59 {
Err(Error::InvalidMinute)
} else if dt.second > 59 {
Err(Error::InvalidSecond)
} else {
Ok(())
}
}
pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
let (ht, hu) = byte_to_bcd2(t.hour as u8);
let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
let (st, su) = byte_to_bcd2(t.second as u8);
let (dt, du) = byte_to_bcd2(t.day as u8);
let (mt, mu) = byte_to_bcd2(t.month as u8);
let yr = t.year as u16;
let yr_offset = (yr - 1970_u16) as u8;
let (yt, yu) = byte_to_bcd2(yr_offset);
unsafe {
rtc.tr().write(|w| {
w.set_ht(ht);
w.set_hu(hu);
w.set_mnt(mnt);
w.set_mnu(mnu);
w.set_st(st);
w.set_su(su);
w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
});
rtc.dr().write(|w| {
w.set_dt(dt);
w.set_du(du);
w.set_mt(mt > 0);
w.set_mu(mu);
w.set_yt(yt);
w.set_yu(yu);
w.set_wdu(day_of_week_to_u8(t.day_of_week));
});
}
}
pub(super) fn datetime(
year: u16,
month: u8,
day: u8,
day_of_week: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime, Error> {
let day_of_week = day_of_week_from_u8(day_of_week)?;
Ok(DateTime {
year,
month,
day,
day_of_week,
hour,
minute,
second,
})
}

View file

@ -0,0 +1,85 @@
use chrono::{Datelike, Timelike};
use super::byte_to_bcd2;
use crate::pac::rtc::Rtc;
/// Alias for [`chrono::NaiveDateTime`]
pub type DateTime = chrono::NaiveDateTime;
/// Alias for [`chrono::Weekday`]
pub type DayOfWeek = chrono::Weekday;
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
///
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
InvalidYear,
/// The [DateTime] contains an invalid date.
InvalidDate,
/// The [DateTime] contains an invalid time.
InvalidTime,
}
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
dotw.num_days_from_monday() as u8
}
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
if dt.year() < 0 || dt.year() > 4095 {
// rp2040 can't hold these years
Err(Error::InvalidYear)
} else {
// The rest of the chrono date is assumed to be valid
Ok(())
}
}
pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
let (ht, hu) = byte_to_bcd2(t.hour() as u8);
let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
let (st, su) = byte_to_bcd2(t.second() as u8);
let (dt, du) = byte_to_bcd2(t.day() as u8);
let (mt, mu) = byte_to_bcd2(t.month() as u8);
let yr = t.year() as u16;
let yr_offset = (yr - 1970_u16) as u8;
let (yt, yu) = byte_to_bcd2(yr_offset);
unsafe {
rtc.tr().write(|w| {
w.set_ht(ht);
w.set_hu(hu);
w.set_mnt(mnt);
w.set_mnu(mnu);
w.set_st(st);
w.set_su(su);
w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
});
rtc.dr().write(|w| {
w.set_dt(dt);
w.set_du(du);
w.set_mt(mt > 0);
w.set_mu(mu);
w.set_yt(yt);
w.set_yu(yu);
w.set_wdu(day_of_week_to_u8(t.weekday()));
});
}
}
pub(super) fn datetime(
year: u16,
month: u8,
day: u8,
_day_of_week: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime, Error> {
let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into())
.ok_or(Error::InvalidDate)?;
let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?;
Ok(DateTime::new(date, time))
}

View file

@ -0,0 +1,235 @@
//! RTC peripheral abstraction
use core::marker::PhantomData;
mod datetime;
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
/// refer to AN4759 to compare features of RTC2 and RTC3
#[cfg_attr(any(rtc_v1), path = "v1.rs")]
#[cfg_attr(
any(
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
),
path = "v2/mod.rs"
)]
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
mod versions;
use embassy_hal_common::Peripheral;
pub use versions::*;
/// Errors that can occur on methods on [RtcClock]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RtcError {
/// An invalid DateTime was given or stored on the hardware.
InvalidDateTime(DateTimeError),
/// The RTC clock is not running
NotRunning,
}
/// RTC Abstraction
pub struct Rtc<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
rtc_config: RtcConfig,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum RtcClockSource {
/// 00: No clock
NoClock = 0b00,
/// 01: LSE oscillator clock used as RTC clock
LSE = 0b01,
/// 10: LSI oscillator clock used as RTC clock
LSI = 0b10,
/// 11: HSE oscillator clock divided by 32 used as RTC clock
HSE = 0b11,
}
#[derive(Copy, Clone, PartialEq)]
pub struct RtcConfig {
/// RTC clock source
clock_config: RtcClockSource,
/// Asynchronous prescaler factor
/// This is the asynchronous division factor:
/// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
/// ck_apre drives the subsecond register
async_prescaler: u8,
/// Synchronous prescaler factor
/// This is the synchronous division factor:
/// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
/// ck_spre must be 1Hz
sync_prescaler: u16,
}
impl Default for RtcConfig {
/// LSI with prescalers assuming 32.768 kHz.
/// Raw sub-seconds in 1/256.
fn default() -> Self {
RtcConfig {
clock_config: RtcClockSource::LSI,
async_prescaler: 127,
sync_prescaler: 255,
}
}
}
impl RtcConfig {
/// Sets the clock source of RTC config
pub fn clock_config(mut self, cfg: RtcClockSource) -> Self {
self.clock_config = cfg;
self
}
/// Set the asynchronous prescaler of RTC config
pub fn async_prescaler(mut self, prescaler: u8) -> Self {
self.async_prescaler = prescaler;
self
}
/// Set the synchronous prescaler of RTC config
pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
self.sync_prescaler = prescaler;
self
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum RtcCalibrationCyclePeriod {
/// 8-second calibration period
Seconds8,
/// 16-second calibration period
Seconds16,
/// 32-second calibration period
Seconds32,
}
impl Default for RtcCalibrationCyclePeriod {
fn default() -> Self {
RtcCalibrationCyclePeriod::Seconds32
}
}
impl<'d, T: Instance> Rtc<'d, T> {
pub fn new(_rtc: impl Peripheral<P = T> + 'd, rtc_config: RtcConfig) -> Self {
unsafe { enable_peripheral_clk() };
let mut rtc_struct = Self {
phantom: PhantomData,
rtc_config,
};
rtc_struct.apply_config(rtc_config);
rtc_struct
}
/// Set the datetime to a new value.
///
/// # Errors
///
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
Ok(())
}
/// Return the current datetime.
///
/// # Errors
///
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
pub fn now(&self) -> Result<DateTime, RtcError> {
let r = T::regs();
unsafe {
let tr = r.tr().read();
let second = bcd2_to_byte((tr.st(), tr.su()));
let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
let hour = bcd2_to_byte((tr.ht(), tr.hu()));
// Reading either RTC_SSR or RTC_TR locks the values in the higher-order
// calendar shadow registers until RTC_DR is read.
let dr = r.dr().read();
let weekday = dr.wdu();
let day = bcd2_to_byte((dr.dt(), dr.du()));
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
}
}
/// Check if daylight savings time is active.
pub fn get_daylight_savings(&self) -> bool {
let cr = unsafe { T::regs().cr().read() };
cr.bkp()
}
/// Enable/disable daylight savings time.
pub fn set_daylight_savings(&mut self, daylight_savings: bool) {
self.write(true, |rtc| {
unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) };
})
}
pub fn get_config(&self) -> RtcConfig {
self.rtc_config
}
pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT;
/// Read content of the backup register.
///
/// The registers retain their values during wakes from standby mode or system resets. They also
/// retain their value when Vdd is switched off as long as V_BAT is powered.
pub fn read_backup_register(&self, register: usize) -> Option<u32> {
read_backup_register(&T::regs(), register)
}
/// Set content of the backup register.
///
/// The registers retain their values during wakes from standby mode or system resets. They also
/// retain their value when Vdd is switched off as long as V_BAT is powered.
pub fn write_backup_register(&self, register: usize, value: u32) {
write_backup_register(&T::regs(), register, value)
}
}
pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {
let mut bcd_high: u8 = 0;
let mut value = byte;
while value >= 10 {
bcd_high += 1;
value -= 10;
}
(bcd_high, ((bcd_high << 4) | value) as u8)
}
pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {
let value = bcd.1 | bcd.0 << 4;
let tmp = ((value & 0xF0) >> 0x4) * 10;
tmp + (value & 0x0F)
}
pub(crate) mod sealed {
pub trait Instance {
fn regs() -> crate::pac::rtc::Rtc;
}
}
pub trait Instance: sealed::Instance + 'static {}
impl sealed::Instance for crate::peripherals::RTC {
fn regs() -> crate::pac::rtc::Rtc {
crate::pac::RTC
}
}
impl Instance for crate::peripherals::RTC {}

View file

@ -0,0 +1,171 @@
use stm32_metapac::rtc::vals::{Init, Osel, Pol};
use super::{Instance, RtcConfig};
use crate::pac::rtc::Rtc;
#[cfg_attr(rtc_v2f0, path = "v2f0.rs")]
#[cfg_attr(rtc_v2f2, path = "v2f2.rs")]
#[cfg_attr(rtc_v2f3, path = "v2f3.rs")]
#[cfg_attr(rtc_v2f4, path = "v2f4.rs")]
#[cfg_attr(rtc_v2f7, path = "v2f7.rs")]
#[cfg_attr(rtc_v2h7, path = "v2h7.rs")]
#[cfg_attr(rtc_v2l0, path = "v2l0.rs")]
#[cfg_attr(rtc_v2l1, path = "v2l1.rs")]
#[cfg_attr(rtc_v2l4, path = "v2l4.rs")]
#[cfg_attr(rtc_v2wb, path = "v2wb.rs")]
mod family;
pub use family::*;
impl<'d, T: Instance> super::Rtc<'d, T> {
/// Applies the RTC config
/// It this changes the RTC clock source the time will be reset
pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
// Unlock the backup domain
unsafe {
unlock_backup_domain(rtc_config.clock_config as u8);
}
self.write(true, |rtc| unsafe {
rtc.cr().modify(|w| {
#[cfg(rtc_v2f2)]
w.set_fmt(false);
#[cfg(not(rtc_v2f2))]
w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
w.set_osel(Osel::DISABLED);
w.set_pol(Pol::HIGH);
});
rtc.prer().modify(|w| {
w.set_prediv_s(rtc_config.sync_prescaler);
w.set_prediv_a(rtc_config.async_prescaler);
});
});
self.rtc_config = rtc_config;
}
/// Calibrate the clock drift.
///
/// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
///
/// ### Note
///
/// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
/// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
#[cfg(not(rtc_v2f2))]
pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {
const RTC_CALR_MIN_PPM: f32 = -487.1;
const RTC_CALR_MAX_PPM: f32 = 488.5;
const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
if clock_drift < RTC_CALR_MIN_PPM {
clock_drift = RTC_CALR_MIN_PPM;
} else if clock_drift > RTC_CALR_MAX_PPM {
clock_drift = RTC_CALR_MAX_PPM;
}
clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
self.write(false, |rtc| {
unsafe {
rtc.calr().write(|w| {
match period {
super::RtcCalibrationCyclePeriod::Seconds8 => {
w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
}
super::RtcCalibrationCyclePeriod::Seconds16 => {
w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
}
super::RtcCalibrationCyclePeriod::Seconds32 => {
// Set neither `calw8` nor `calw16` to use 32 seconds
}
}
// Extra pulses during calibration cycle period: CALP * 512 - CALM
//
// CALP sets whether pulses are added or omitted.
//
// CALM contains how many pulses (out of 512) are masked in a
// given calibration cycle period.
if clock_drift > 0.0 {
// Maximum (about 512.2) rounds to 512.
clock_drift += 0.5;
// When the offset is positive (0 to 512), the opposite of
// the offset (512 - offset) is masked, i.e. for the
// maximum offset (512), 0 pulses are masked.
w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
w.set_calm(512 - clock_drift as u16);
} else {
// Minimum (about -510.7) rounds to -511.
clock_drift -= 0.5;
// When the offset is negative or zero (-511 to 0),
// the absolute offset is masked, i.e. for the minimum
// offset (-511), 511 pulses are masked.
w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
w.set_calm((clock_drift * -1.0) as u16);
}
});
}
})
}
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
where
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
{
let r = T::regs();
// Disable write protection.
// This is safe, as we're only writin the correct and expected values.
unsafe {
r.wpr().write(|w| w.set_key(0xca));
r.wpr().write(|w| w.set_key(0x53));
// true if initf bit indicates RTC peripheral is in init mode
if init_mode && !r.isr().read().initf() {
// to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
r.isr().modify(|w| w.set_init(Init::INITMODE));
// wait till init state entered
// ~2 RTCCLK cycles
while !r.isr().read().initf() {}
}
}
let result = f(&r);
unsafe {
if init_mode {
r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
}
// Re-enable write protection.
// This is safe, as the field accepts the full range of 8-bit values.
r.wpr().write(|w| w.set_key(0xff));
}
result
}
}
/// Read content of the backup register.
///
/// The registers retain their values during wakes from standby mode or system resets. They also
/// retain their value when Vdd is switched off as long as V_BAT is powered.
pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
if register < BACKUP_REGISTER_COUNT {
Some(unsafe { rtc.bkpr(register).read().bkp() })
} else {
None
}
}
/// Set content of the backup register.
///
/// The registers retain their values during wakes from standby mode or system resets. They also
/// retain their value when Vdd is switched off as long as V_BAT is powered.
pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
if register < BACKUP_REGISTER_COUNT {
unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) }
}
}

View file

@ -0,0 +1,41 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
// Restore bcdr
w.set_lscosel(reg.lscosel());
w.set_lscoen(reg.lscoen());
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// enable peripheral clock for communication
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
// read to allow the pwr clock to enable
crate::pac::PWR.cr1().read();
}

View file

@ -0,0 +1,31 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
w.set_lseon(reg.lseon());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// Nothing to do
}

View file

@ -0,0 +1,31 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
w.set_lseon(reg.lseon());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// Nothing to do
}

View file

@ -0,0 +1,31 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
w.set_lseon(reg.lseon());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// Nothing to do
}

View file

@ -0,0 +1,41 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
// Restore bcdr
w.set_lscosel(reg.lscosel());
w.set_lscoen(reg.lscoen());
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// enable peripheral clock for communication
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
// read to allow the pwr clock to enable
crate::pac::PWR.cr1().read();
}

View file

@ -0,0 +1,33 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// Nothing to do
}

View file

@ -0,0 +1,26 @@
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
// TODO: Missing from PAC?
// crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
// while !crate::pac::PWR.cr().read().dbp() {}
let reg = crate::pac::RCC.csr().read();
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.csr().modify(|w| {
// Select RTC source
w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
w.set_rtcen(true);
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// Nothing to do
}

View file

@ -0,0 +1,24 @@
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr().read().dbp() {}
let reg = crate::pac::RCC.csr().read();
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.csr().modify(|w| {
// Select RTC source
w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
w.set_rtcen(true);
w.set_lseon(reg.lseon());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// Nothing to do
}

View file

@ -0,0 +1,41 @@
use stm32_metapac::rcc::vals::Rtcsel;
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(Rtcsel(clock_config));
w.set_rtcen(true);
// Restore bcdr
w.set_lscosel(reg.lscosel());
w.set_lscoen(reg.lscoen());
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// enable peripheral clock for communication
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
// read to allow the pwr clock to enable
crate::pac::PWR.cr1().read();
}

View file

@ -0,0 +1,39 @@
pub const BACKUP_REGISTER_COUNT: usize = 20;
/// Unlock the backup domain
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
let reg = crate::pac::RCC.bdcr().read();
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
if !reg.rtcen() || reg.rtcsel() != clock_config {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(clock_config);
w.set_rtcen(true);
// Restore bcdr
w.set_lscosel(reg.lscosel());
w.set_lscoen(reg.lscoen());
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
pub(crate) unsafe fn enable_peripheral_clk() {
// enable peripheral clock for communication
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
// read to allow the pwr clock to enable
crate::pac::PWR.cr1().read();
}

226
embassy-stm32/src/rtc/v3.rs Normal file
View file

@ -0,0 +1,226 @@
use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig};
use crate::pac::rtc::Rtc;
impl<'d, T: Instance> super::Rtc<'d, T> {
/// Applies the RTC config
/// It this changes the RTC clock source the time will be reset
pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
// Unlock the backup domain
unsafe {
#[cfg(feature = "stm32g0c1ve")]
{
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
while !crate::pac::PWR.cr1().read().dbp() {}
}
#[cfg(not(any(
feature = "stm32g0c1ve",
feature = "stm32g491re",
feature = "stm32u585zi",
feature = "stm32g473cc"
)))]
{
crate::pac::PWR
.cr1()
.modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED));
while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {}
}
let reg = crate::pac::RCC.bdcr().read();
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
let config_rtcsel = rtc_config.clock_config as u8;
#[cfg(not(any(
feature = "stm32wl54jc-cm0p",
feature = "stm32wle5ub",
feature = "stm32g0c1ve",
feature = "stm32wl55jc-cm4",
feature = "stm32wl55uc-cm4",
feature = "stm32g491re",
feature = "stm32g473cc",
feature = "stm32u585zi",
feature = "stm32wle5jb"
)))]
let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
#[cfg(feature = "stm32g0c1ve")]
let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel);
if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
crate::pac::RCC.bdcr().modify(|w| {
// Reset
w.set_bdrst(false);
// Select RTC source
w.set_rtcsel(config_rtcsel);
w.set_rtcen(true);
// Restore bcdr
w.set_lscosel(reg.lscosel());
w.set_lscoen(reg.lscoen());
w.set_lseon(reg.lseon());
w.set_lsedrv(reg.lsedrv());
w.set_lsebyp(reg.lsebyp());
});
}
}
self.write(true, |rtc| {
unsafe {
rtc.cr().modify(|w| {
w.set_fmt(Fmt::TWENTYFOURHOUR);
w.set_osel(Osel::DISABLED);
w.set_pol(Pol::HIGH);
});
rtc.prer().modify(|w| {
w.set_prediv_s(rtc_config.sync_prescaler);
w.set_prediv_a(rtc_config.async_prescaler);
});
// TODO: configuration for output pins
rtc.cr().modify(|w| {
w.set_out2en(false);
w.set_tampalrm_type(TampalrmType::PUSHPULL);
w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
});
}
});
self.rtc_config = rtc_config;
}
const RTC_CALR_MIN_PPM: f32 = -487.1;
const RTC_CALR_MAX_PPM: f32 = 488.5;
const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
/// Calibrate the clock drift.
///
/// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
///
/// ### Note
///
/// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
/// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {
if clock_drift < Self::RTC_CALR_MIN_PPM {
clock_drift = Self::RTC_CALR_MIN_PPM;
} else if clock_drift > Self::RTC_CALR_MAX_PPM {
clock_drift = Self::RTC_CALR_MAX_PPM;
}
clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
self.write(false, |rtc| {
unsafe {
rtc.calr().write(|w| {
match period {
RtcCalibrationCyclePeriod::Seconds8 => {
w.set_calw8(Calw8::EIGHTSECONDS);
}
RtcCalibrationCyclePeriod::Seconds16 => {
w.set_calw16(Calw16::SIXTEENSECONDS);
}
RtcCalibrationCyclePeriod::Seconds32 => {
// Set neither `calw8` nor `calw16` to use 32 seconds
}
}
// Extra pulses during calibration cycle period: CALP * 512 - CALM
//
// CALP sets whether pulses are added or omitted.
//
// CALM contains how many pulses (out of 512) are masked in a
// given calibration cycle period.
if clock_drift > 0.0 {
// Maximum (about 512.2) rounds to 512.
clock_drift += 0.5;
// When the offset is positive (0 to 512), the opposite of
// the offset (512 - offset) is masked, i.e. for the
// maximum offset (512), 0 pulses are masked.
w.set_calp(Calp::INCREASEFREQ);
w.set_calm(512 - clock_drift as u16);
} else {
// Minimum (about -510.7) rounds to -511.
clock_drift -= 0.5;
// When the offset is negative or zero (-511 to 0),
// the absolute offset is masked, i.e. for the minimum
// offset (-511), 511 pulses are masked.
w.set_calp(Calp::NOCHANGE);
w.set_calm((clock_drift * -1.0) as u16);
}
});
}
})
}
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
where
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
{
let r = T::regs();
// Disable write protection.
// This is safe, as we're only writin the correct and expected values.
unsafe {
r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
if init_mode && !r.icsr().read().initf() {
r.icsr().modify(|w| w.set_init(Init::INITMODE));
// wait till init state entered
// ~2 RTCCLK cycles
while !r.icsr().read().initf() {}
}
}
let result = f(&r);
unsafe {
if init_mode {
r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
}
// Re-enable write protection.
// This is safe, as the field accepts the full range of 8-bit values.
r.wpr().write(|w| w.set_key(Key::ACTIVATE));
}
result
}
}
pub(super) unsafe fn enable_peripheral_clk() {
// Nothing to do
}
pub const BACKUP_REGISTER_COUNT: usize = 32;
/// Read content of the backup register.
///
/// The registers retain their values during wakes from standby mode or system resets. They also
/// retain their value when Vdd is switched off as long as V_BAT is powered.
pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
if register < BACKUP_REGISTER_COUNT {
//Some(rtc.bkpr()[register].read().bits())
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
} else {
None
}
}
/// Set content of the backup register.
///
/// The registers retain their values during wakes from standby mode or system resets. They also
/// retain their value when Vdd is switched off as long as V_BAT is powered.
pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
if register < BACKUP_REGISTER_COUNT {
// RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
//unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) }
}
}

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,7 @@ use embassy_futures::join::join;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use self::sealed::WordSize;
use crate::dma::{slice_ptr_parts, Transfer};
use crate::dma::{slice_ptr_parts, word, Transfer};
use crate::gpio::sealed::{AFType, Pin as _};
use crate::gpio::{AnyPin, Pull};
use crate::pac::spi::{regs, vals, Spi as Regs};
@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> {
miso: Option<PeripheralRef<'d, AnyPin>>,
txdma: PeripheralRef<'d, Tx>,
rxdma: PeripheralRef<'d, Rx>,
current_word_size: WordSize,
current_word_size: word_impl::Config,
}
impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
@ -178,6 +177,23 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
)
}
pub fn new_txonly_nosck(
peri: impl Peripheral<P = T> + 'd,
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
txdma: impl Peripheral<P = Tx> + 'd,
rxdma: impl Peripheral<P = Rx> + 'd, // TODO: remove
freq: Hertz,
config: Config,
) -> Self {
into_ref!(mosi);
unsafe {
mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down);
mosi.set_speed(crate::gpio::Speed::Medium);
}
Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config)
}
/// Useful for on chip peripherals like SUBGHZ which are hardwired.
/// The bus can optionally be exposed externally with `Spi::new()` still.
#[allow(dead_code)]
@ -234,14 +250,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
if mosi.is_none() {
w.set_rxonly(vals::Rxonly::OUTPUTDISABLED);
}
w.set_dff(WordSize::EightBit.dff())
w.set_dff(<u8 as sealed::Word>::CONFIG)
});
}
#[cfg(spi_v2)]
unsafe {
T::REGS.cr2().modify(|w| {
w.set_frxth(WordSize::EightBit.frxth());
w.set_ds(WordSize::EightBit.ds());
let (ds, frxth) = <u8 as sealed::Word>::CONFIG;
w.set_frxth(frxth);
w.set_ds(ds);
w.set_ssoe(false);
});
T::REGS.cr1().modify(|w| {
@ -279,7 +296,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
T::REGS.cfg1().modify(|w| {
w.set_crcen(false);
w.set_mbr(br);
w.set_dsize(WordSize::EightBit.dsize());
w.set_dsize(<u8 as sealed::Word>::CONFIG);
w.set_fthlv(vals::Fthlv::ONEFRAME);
});
T::REGS.cr2().modify(|w| {
w.set_tsize(0);
@ -297,7 +315,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
miso,
txdma,
rxdma,
current_word_size: WordSize::EightBit,
current_word_size: <u8 as sealed::Word>::CONFIG,
}
}
@ -355,7 +373,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
}
}
fn set_word_size(&mut self, word_size: WordSize) {
fn set_word_size(&mut self, word_size: word_impl::Config) {
if self.current_word_size == word_size {
return;
}
@ -364,7 +382,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
unsafe {
T::REGS.cr1().modify(|reg| {
reg.set_spe(false);
reg.set_dff(word_size.dff())
reg.set_dff(word_size)
});
T::REGS.cr1().modify(|reg| {
reg.set_spe(true);
@ -376,8 +394,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
w.set_spe(false);
});
T::REGS.cr2().modify(|w| {
w.set_frxth(word_size.frxth());
w.set_ds(word_size.ds());
w.set_frxth(word_size.1);
w.set_ds(word_size.0);
});
T::REGS.cr1().modify(|w| {
w.set_spe(true);
@ -393,7 +411,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
w.set_spe(false);
});
T::REGS.cfg1().modify(|w| {
w.set_dsize(word_size.dsize());
w.set_dsize(word_size);
});
T::REGS.cr1().modify(|w| {
w.set_csusp(false);
@ -412,7 +430,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
return Ok(());
}
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
unsafe {
T::REGS.cr1().modify(|w| {
w.set_spe(false);
@ -421,8 +439,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
let tx_request = self.txdma.request();
let tx_dst = T::REGS.tx_ptr();
unsafe { self.txdma.start_write(tx_request, data, tx_dst, Default::default()) }
let tx_f = Transfer::new(&mut self.txdma);
let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) };
unsafe {
set_txdmaen(T::REGS, true);
@ -451,7 +468,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
return Ok(());
}
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
unsafe {
T::REGS.cr1().modify(|w| {
w.set_spe(false);
@ -468,13 +485,21 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
let rx_request = self.rxdma.request();
let rx_src = T::REGS.rx_ptr();
unsafe { self.rxdma.start_read(rx_request, rx_src, data, Default::default()) };
let rx_f = Transfer::new(&mut self.rxdma);
let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) };
let tx_request = self.txdma.request();
let tx_dst = T::REGS.tx_ptr();
let clock_byte = 0x00u8;
let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst);
let tx_f = unsafe {
Transfer::new_write_repeated(
&mut self.txdma,
tx_request,
&clock_byte,
clock_byte_count,
tx_dst,
Default::default(),
)
};
unsafe {
set_txdmaen(T::REGS, true);
@ -506,7 +531,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
return Ok(());
}
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
unsafe {
T::REGS.cr1().modify(|w| {
w.set_spe(false);
@ -521,13 +546,11 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
let rx_request = self.rxdma.request();
let rx_src = T::REGS.rx_ptr();
unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) };
let rx_f = Transfer::new(&mut self.rxdma);
let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) };
let tx_request = self.txdma.request();
let tx_dst = T::REGS.tx_ptr();
unsafe { self.txdma.start_write(tx_request, write, tx_dst, Default::default()) }
let tx_f = Transfer::new(&mut self.txdma);
let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) };
unsafe {
set_txdmaen(T::REGS, true);
@ -566,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
flush_rx_fifo(T::REGS);
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
for word in words.iter() {
let _ = transfer_word(T::REGS, *word)?;
}
@ -576,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
flush_rx_fifo(T::REGS);
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
for word in words.iter_mut() {
*word = transfer_word(T::REGS, W::default())?;
}
@ -586,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
flush_rx_fifo(T::REGS);
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
for word in words.iter_mut() {
*word = transfer_word(T::REGS, *word)?;
}
@ -596,7 +619,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
flush_rx_fifo(T::REGS);
self.set_word_size(W::WORDSIZE);
self.set_word_size(W::CONFIG);
let len = read.len().max(write.len());
for i in 0..len {
let wb = write.get(i).copied().unwrap_or_default();
@ -928,70 +951,89 @@ pub(crate) mod sealed {
const REGS: Regs;
}
pub trait Word: Copy + 'static {
const WORDSIZE: WordSize;
}
impl Word for u8 {
const WORDSIZE: WordSize = WordSize::EightBit;
}
impl Word for u16 {
const WORDSIZE: WordSize = WordSize::SixteenBit;
}
#[derive(Copy, Clone, PartialOrd, PartialEq)]
pub enum WordSize {
EightBit,
SixteenBit,
}
impl WordSize {
#[cfg(any(spi_v1, spi_f1))]
pub fn dff(&self) -> vals::Dff {
match self {
WordSize::EightBit => vals::Dff::EIGHTBIT,
WordSize::SixteenBit => vals::Dff::SIXTEENBIT,
}
}
#[cfg(spi_v2)]
pub fn ds(&self) -> vals::Ds {
match self {
WordSize::EightBit => vals::Ds::EIGHTBIT,
WordSize::SixteenBit => vals::Ds::SIXTEENBIT,
}
}
#[cfg(spi_v2)]
pub fn frxth(&self) -> vals::Frxth {
match self {
WordSize::EightBit => vals::Frxth::QUARTER,
WordSize::SixteenBit => vals::Frxth::HALF,
}
}
#[cfg(any(spi_v3, spi_v4, spi_v5))]
pub fn dsize(&self) -> u8 {
match self {
WordSize::EightBit => 0b0111,
WordSize::SixteenBit => 0b1111,
}
}
#[cfg(any(spi_v3, spi_v4, spi_v5))]
pub fn _frxth(&self) -> vals::Fthlv {
match self {
WordSize::EightBit => vals::Fthlv::ONEFRAME,
WordSize::SixteenBit => vals::Fthlv::ONEFRAME,
}
}
pub trait Word {
const CONFIG: word_impl::Config;
}
}
pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {}
pub trait Word: word::Word + sealed::Word {}
impl Word for u8 {}
impl Word for u16 {}
macro_rules! impl_word {
($T:ty, $config:expr) => {
impl sealed::Word for $T {
const CONFIG: Config = $config;
}
impl Word for $T {}
};
}
#[cfg(any(spi_v1, spi_f1))]
mod word_impl {
use super::*;
pub type Config = vals::Dff;
impl_word!(u8, vals::Dff::EIGHTBIT);
impl_word!(u16, vals::Dff::SIXTEENBIT);
}
#[cfg(any(spi_v2))]
mod word_impl {
use super::*;
pub type Config = (vals::Ds, vals::Frxth);
impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER));
impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER));
impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER));
impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER));
impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER));
impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF));
impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF));
impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF));
impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF));
impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF));
impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF));
impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF));
impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF));
}
#[cfg(any(spi_v3, spi_v4, spi_v5))]
mod word_impl {
use super::*;
pub type Config = u8;
impl_word!(word::U4, 4 - 1);
impl_word!(word::U5, 5 - 1);
impl_word!(word::U6, 6 - 1);
impl_word!(word::U7, 7 - 1);
impl_word!(u8, 8 - 1);
impl_word!(word::U9, 9 - 1);
impl_word!(word::U10, 10 - 1);
impl_word!(word::U11, 11 - 1);
impl_word!(word::U12, 12 - 1);
impl_word!(word::U13, 13 - 1);
impl_word!(word::U14, 14 - 1);
impl_word!(word::U15, 15 - 1);
impl_word!(u16, 16 - 1);
impl_word!(word::U17, 17 - 1);
impl_word!(word::U18, 18 - 1);
impl_word!(word::U19, 19 - 1);
impl_word!(word::U20, 20 - 1);
impl_word!(word::U21, 21 - 1);
impl_word!(word::U22, 22 - 1);
impl_word!(word::U23, 23 - 1);
impl_word!(word::U24, 24 - 1);
impl_word!(word::U25, 25 - 1);
impl_word!(word::U26, 26 - 1);
impl_word!(word::U27, 27 - 1);
impl_word!(word::U28, 28 - 1);
impl_word!(word::U29, 29 - 1);
impl_word!(word::U30, 30 - 1);
impl_word!(word::U31, 31 - 1);
impl_word!(u32, 32 - 1);
}
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
pin_trait!(SckPin, Instance);

View file

@ -34,7 +34,7 @@ macro_rules! dma_trait_impl {
(crate::$mod:ident::$trait:ident, $instance:ident, {dmamux: $dmamux:ident}, $request:expr) => {
impl<T> crate::$mod::$trait<crate::peripherals::$instance> for T
where
T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
T: crate::dma::Channel + crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
{
fn request(&self) -> crate::dma::Request {
$request

View file

@ -6,11 +6,11 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_cortex_m::interrupt::InterruptExt;
use embassy_futures::select::{select, Either};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::{select, Either};
use crate::dma::NoDma;
use crate::dma::{NoDma, Transfer};
use crate::gpio::sealed::AFType;
#[cfg(any(lpuart_v1, lpuart_v2))]
use crate::pac::lpuart::{regs, vals, Lpuart as Regs};
@ -91,7 +91,7 @@ enum ReadCompletionEvent {
// DMA Read transfer completed first
DmaCompleted,
// Idle line detected first
Idle,
Idle(usize),
}
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
@ -183,7 +183,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
}
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
let transfer = crate::dma::write(ch, request, buffer, tdr(T::regs()));
let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) };
transfer.await;
Ok(())
}
@ -430,10 +430,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
let ch = &mut self.rx_dma;
let request = ch.request();
let buffer_len = buffer.len();
// Start USART DMA
// will not do anything yet because DMAR is not yet set
// future which will complete when DMA Read request completes
let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer);
let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) };
// SAFETY: The only way we might have a problem is using split rx and tx
// here we only modify or read Rx related flags, interrupts and DMA channel
@ -565,13 +567,15 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
// when transfer is dropped, it will stop the DMA request
let r = match select(transfer, idle).await {
// DMA transfer completed first
Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted),
Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted),
// Idle line detected first
Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle),
Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle(
buffer_len - transfer.get_remaining_transfers() as usize,
)),
// error occurred
Either::Second(Err(e)) => Err(e),
Either::Right((Err(e), _)) => Err(e),
};
drop(on_drop);
@ -594,14 +598,9 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
// wait for DMA to complete or IDLE line detection if requested
let res = self.inner_read_run(buffer, enable_idle_line_detection).await;
let ch = &mut self.rx_dma;
match res {
Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),
Ok(ReadCompletionEvent::Idle) => {
let n = buffer_len - (ch.remaining_transfers() as usize);
Ok(n)
}
Ok(ReadCompletionEvent::Idle(n)) => Ok(n),
Err(e) => Err(e),
}
}
@ -973,73 +972,6 @@ mod eio {
}
}
#[cfg(all(
feature = "unstable-traits",
feature = "nightly",
feature = "_todo_embedded_hal_serial"
))]
mod eha {
use core::future::Future;
use super::*;
impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma>
where
TxDma: crate::usart::TxDma<T>,
{
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma>
where
RxDma: crate::usart::RxDma<T>,
{
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buf)
}
}
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma>
where
TxDma: crate::usart::TxDma<T>,
{
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buf)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma>
where
RxDma: crate::usart::RxDma<T>,
{
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buf)
}
}
}
#[cfg(feature = "nightly")]
pub use buffered::*;
#[cfg(feature = "nightly")]

View file

@ -18,7 +18,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -18,7 +18,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"], optional = true }
panic-reset = { version = "0.1.1", optional = true }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]

View file

@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true }
embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] }
embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = { version = "0.7" }
cfg-if = "1.0.0"

View file

@ -13,7 +13,7 @@ embassy-rp = { path = "../../../../embassy-rp", default-features = false, featur
embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false }
embassy-time = { path = "../../../../embassy-time", features = ["nightly"] }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.0"
embedded-storage-async = "0.4.0"

View file

@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true }
embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] }
embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.0"
embedded-storage-async = "0.4.0"

View file

@ -21,7 +21,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
embassy-time = { version = "0.1.0", path = "../../embassy-time" }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "0.3" }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }

View file

@ -20,14 +20,14 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
embedded-io = "0.4.0"
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true }
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true }
lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true }
lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true }
lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true }
defmt = "0.3"
defmt-rtt = "0.4"
static_cell = "1.0"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }

View file

@ -43,7 +43,7 @@ defmt = "0.3"
defmt-rtt = "0.4"
static_cell = "1.0"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = [

View file

@ -20,7 +20,7 @@ defmt = "0.3"
defmt-rtt = "0.4"
#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6" }
cortex-m = { version = "0.7.6", features = ["inline-asm"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }

View file

@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
defmt = "0.3"
defmt-rtt = "0.4"

View file

@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -8,14 +8,14 @@ license = "MIT OR Apache-2.0"
embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
embedded-io = "0.4.0"

View file

@ -39,7 +39,18 @@ async fn main(_spawner: Spawner) {
// Should print 400kHz for initialization
info!("Configured clock: {}", sdmmc.clock().0);
unwrap!(sdmmc.init_card(mhz(48)).await);
let mut err = None;
loop {
match sdmmc.init_card(mhz(24)).await {
Ok(_) => break,
Err(e) => {
if err != Some(e) {
info!("waiting for card error, retrying: {:?}", e);
err = Some(e);
}
}
}
}
let card = unwrap!(sdmmc.card());

View file

@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -0,0 +1,101 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::dma::word::U5;
use embassy_stm32::dma::NoDma;
use embassy_stm32::spi::{Config, Spi};
use embassy_stm32::time::Hertz;
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};
const NR_PIXELS: usize = 15;
const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw
const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL;
struct RGB {
r: u8,
g: u8,
b: u8,
}
impl Default for RGB {
fn default() -> RGB {
RGB { r: 0, g: 0, b: 0 }
}
}
pub struct Ws2812 {
// Note that the U5 type controls the selection of 5 bits to output
bitbuffer: [U5; TOTAL_BITS],
}
impl Ws2812 {
pub fn new() -> Ws2812 {
Ws2812 {
bitbuffer: [U5(0); TOTAL_BITS],
}
}
fn len(&self) -> usize {
return NR_PIXELS;
}
fn set(&mut self, idx: usize, rgb: RGB) {
self.render_color(idx, 0, rgb.g);
self.render_color(idx, 8, rgb.r);
self.render_color(idx, 16, rgb.b);
}
// transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern
fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) {
let mut bits = color as usize;
let mut idx = pixel_idx * BITS_PER_PIXEL + offset;
// render one bit in one spi byte. High time first, then the low time
// clock should be 4 Mhz, 5 bits, each bit is 0.25 us.
// a one bit is send as a pulse of 0.75 high -- 0.50 low
// a zero bit is send as a pulse of 0.50 high -- 0.75 low
// clock frequency for the neopixel is exact 800 khz
// note that the mosi output should have a resistor to ground of 10k,
// to assure that between the bursts the line is low
for _i in 0..8 {
if idx >= TOTAL_BITS {
return;
}
let pattern = match bits & 0x80 {
0x80 => 0b0000_1110,
_ => 0b000_1100,
};
bits = bits << 1;
self.bitbuffer[idx] = U5(pattern);
idx += 1;
}
}
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Start test using spi as neopixel driver");
let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default());
let mut neopixels = Ws2812::new();
loop {
let mut cnt: usize = 0;
for _i in 0..10 {
for idx in 0..neopixels.len() {
let color = match (cnt + idx) % 3 {
0 => RGB { r: 0x21, g: 0, b: 0 },
1 => RGB { r: 0, g: 0x31, b: 0 },
_ => RGB { r: 0, g: 0, b: 0x41 },
};
neopixels.set(idx, color);
}
cnt += 1;
// start sending the neopixel bit patters over spi to the neopixel string
spi.write(&neopixels.bitbuffer).await.ok();
Timer::after(Duration::from_millis(500)).await;
}
Timer::after(Duration::from_millis(1000)).await;
}
}

View file

@ -15,8 +15,8 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let ch1 = PwmPin::new_ch1(p.PA5);
let mut pwm = SimplePwm::new(p.TIM2, Some(ch1), None, None, None, khz(10));
let ch1 = PwmPin::new_ch1(p.PC0);
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10));
let max = pwm.get_max_duty();
pwm.enable(Channel::Ch1);

View file

@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }

View file

@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }

View file

@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true }
lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true }
lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true }
lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true }
defmt = "0.3"
defmt-rtt = "0.4"
@ -24,7 +24,7 @@ defmt-rtt = "0.4"
embedded-storage = "0.3.0"
embedded-io = "0.4.0"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }

View file

@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) {
let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap();
let region = region::EU868::default().into();
let region = region::Configuration::new(region::Region::EU868);
let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG));
defmt::info!("Joining LoRaWAN network");

View file

@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -18,7 +18,7 @@ defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }

View file

@ -14,7 +14,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View file

@ -11,13 +11,13 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] }
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] }
lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] }
lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] }
lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] }
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
embedded-storage = "0.3.0"

View file

@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) {
radio_config.calibrate_image = CalibrateImage::ISM_863_870;
let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap();
let mut region: region::Configuration = region::EU868::default().into();
let mut region = region::Configuration::new(region::Region::EU868);
// NOTE: This is specific for TTN, as they have a special RX1 delay
region.set_receive_delay1(5000);

View file

@ -1,7 +1,7 @@
# Before upgrading check that everything is available on all tier1 targets here:
# https://rust-lang.github.io/rustup-components-history
[toolchain]
channel = "nightly-2023-04-11"
channel = "nightly-2023-04-18"
components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
targets = [
"thumbv7em-none-eabi",

View file

@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
[features]
stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill
stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo
stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo
stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo
stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo
stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo
@ -15,6 +15,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo
stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo
stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
sdmmc = []
[dependencies]
embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
@ -31,6 +33,45 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
# BEGIN TESTS
# Generated by gen_test.py. DO NOT EDIT.
[[bin]]
name = "gpio"
path = "src/bin/gpio.rs"
required-features = []
[[bin]]
name = "sdmmc"
path = "src/bin/sdmmc.rs"
required-features = [ "sdmmc",]
[[bin]]
name = "spi"
path = "src/bin/spi.rs"
required-features = []
[[bin]]
name = "spi_dma"
path = "src/bin/spi_dma.rs"
required-features = []
[[bin]]
name = "timer"
path = "src/bin/timer.rs"
required-features = []
[[bin]]
name = "usart"
path = "src/bin/usart.rs"
required-features = []
[[bin]]
name = "usart_dma"
path = "src/bin/usart_dma.rs"
required-features = []
# END TESTS
[profile.dev]
debug = 2
debug-assertions = true

44
tests/stm32/gen_test.py Normal file
View file

@ -0,0 +1,44 @@
import os
import toml
from glob import glob
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
# ======= load test list
tests = {}
for f in sorted(glob('./src/bin/*.rs')):
name = os.path.splitext(os.path.basename(f))[0]
features = []
with open(f, 'r') as f:
for line in f:
if line.startswith('// required-features:'):
features = line.split(':', 2)[1].strip().split(',')
tests[name] = features
# ========= Update Cargo.toml
things = {
'bin': [
{
'name': f'{name}',
'path': f'src/bin/{name}.rs',
'required-features': features,
}
for name, features in tests.items()
]
}
SEPARATOR_START = '# BEGIN TESTS\n'
SEPARATOR_END = '# END TESTS\n'
HELP = '# Generated by gen_test.py. DO NOT EDIT.\n'
with open('Cargo.toml', 'r') as f:
data = f.read()
before, data = data.split(SEPARATOR_START, maxsplit=1)
_, after = data.split(SEPARATOR_END, maxsplit=1)
data = before + SEPARATOR_START + HELP + \
toml.dumps(things) + SEPARATOR_END + after
with open('Cargo.toml', 'w') as f:
f.write(data)

View file

@ -0,0 +1,148 @@
// required-features: sdmmc
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::{assert_eq, *};
use embassy_executor::Spawner;
use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
use embassy_stm32::time::mhz;
use embassy_stm32::{interrupt, Config};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(48));
config.rcc.pll48 = true;
let p = embassy_stm32::init(config);
#[cfg(feature = "stm32f429zi")]
let (mut sdmmc, mut irq, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = (
p.SDIO,
interrupt::take!(SDIO),
p.DMA2_CH3,
p.PC12,
p.PD2,
p.PC8,
p.PC9,
p.PC10,
p.PC11,
);
// Arbitrary block index
let block_idx = 16;
let mut pattern1 = DataBlock([0u8; 512]);
let mut pattern2 = DataBlock([0u8; 512]);
for i in 0..512 {
pattern1[i] = i as u8;
pattern2[i] = !i as u8;
}
let mut block = DataBlock([0u8; 512]);
// ======== Try 4bit. ==============
info!("initializing in 4-bit mode...");
let mut s = Sdmmc::new_4bit(
&mut sdmmc,
&mut irq,
&mut dma,
&mut clk,
&mut cmd,
&mut d0,
&mut d1,
&mut d2,
&mut d3,
Default::default(),
);
let mut err = None;
loop {
match s.init_card(mhz(24)).await {
Ok(_) => break,
Err(e) => {
if err != Some(e) {
info!("waiting for card: {:?}", e);
err = Some(e);
}
}
}
}
let card = unwrap!(s.card());
info!("Card: {:#?}", Debug2Format(card));
info!("Clock: {}", s.clock());
info!("writing pattern1...");
s.write_block(block_idx, &pattern1).await.unwrap();
info!("reading...");
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern1);
info!("writing pattern2...");
s.write_block(block_idx, &pattern2).await.unwrap();
info!("reading...");
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern2);
drop(s);
// ======== Try 1bit. ==============
info!("initializing in 1-bit mode...");
let mut s = Sdmmc::new_1bit(
&mut sdmmc,
&mut irq,
&mut dma,
&mut clk,
&mut cmd,
&mut d0,
Default::default(),
);
let mut err = None;
loop {
match s.init_card(mhz(24)).await {
Ok(_) => break,
Err(e) => {
if err != Some(e) {
info!("waiting for card: {:?}", e);
err = Some(e);
}
}
}
}
let card = unwrap!(s.card());
info!("Card: {:#?}", Debug2Format(card));
info!("Clock: {}", s.clock());
info!("reading pattern2 written in 4bit mode...");
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern2);
info!("writing pattern1...");
s.write_block(block_idx, &pattern1).await.unwrap();
info!("reading...");
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern1);
info!("writing pattern2...");
s.write_block(block_idx, &pattern2).await.unwrap();
info!("reading...");
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern2);
drop(s);
info!("Test OK");
cortex_m::asm::bkpt();
}