aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsander <[email protected]>2023-04-20 08:45:58 +0200
committersander <[email protected]>2023-04-20 08:45:58 +0200
commit510ae7e3dc5bb6a43b1e83e1daed44a2f3546e46 (patch)
tree925c3b427316cbb6a6694fed12cbccb790cd91f0
parent3002ee0dcf0ae186867b2fd869daed609d7a4a23 (diff)
parent54fe50c685a37c7edaf7bd0fcd2d473109d1374d (diff)
Merge commit 'eecc41c2e4911c5f1cd232339999424760de9f06'
-rw-r--r--.vscode/settings.json6
-rwxr-xr-xci.sh1
-rwxr-xr-xci_stable.sh1
-rw-r--r--embassy-embedded-hal/src/adapter.rs42
-rw-r--r--embassy-executor/src/raw/run_queue.rs25
-rw-r--r--embassy-hal-common/src/macros.rs25
-rw-r--r--embassy-lora/Cargo.toml4
-rw-r--r--embassy-lora/src/lib.rs13
-rw-r--r--embassy-lora/src/stm32wl/mod.rs12
-rw-r--r--embassy-lora/src/sx126x/mod.rs132
-rw-r--r--embassy-lora/src/sx127x/mod.rs157
-rw-r--r--embassy-net/src/lib.rs9
-rw-r--r--embassy-nrf/src/uarte.rs77
-rw-r--r--embassy-rp/Cargo.toml3
-rw-r--r--embassy-rp/src/float/add_sub.rs92
-rw-r--r--embassy-rp/src/float/cmp.rs201
-rw-r--r--embassy-rp/src/float/conv.rs157
-rw-r--r--embassy-rp/src/float/div.rs141
-rw-r--r--embassy-rp/src/float/functions.rs239
-rw-r--r--embassy-rp/src/float/mod.rs149
-rw-r--r--embassy-rp/src/float/mul.rs70
-rw-r--r--embassy-rp/src/intrinsics.rs198
-rw-r--r--embassy-rp/src/lib.rs5
-rw-r--r--embassy-rp/src/rom_data.rs115
-rw-r--r--embassy-rp/src/uart/buffered.rs55
-rw-r--r--embassy-rp/src/uart/mod.rs108
-rw-r--r--embassy-stm32/Cargo.toml3
-rw-r--r--embassy-stm32/build.rs130
-rw-r--r--embassy-stm32/src/dcmi.rs42
-rw-r--r--embassy-stm32/src/dma/bdma.rs404
-rw-r--r--embassy-stm32/src/dma/dma.rs676
-rw-r--r--embassy-stm32/src/dma/dmamux.rs22
-rw-r--r--embassy-stm32/src/dma/gpdma.rs403
-rw-r--r--embassy-stm32/src/dma/mod.rs346
-rw-r--r--embassy-stm32/src/dma/word.rs79
-rw-r--r--embassy-stm32/src/flash/common.rs31
-rw-r--r--embassy-stm32/src/flash/mod.rs1
-rw-r--r--embassy-stm32/src/i2c/v2.rs116
-rw-r--r--embassy-stm32/src/lib.rs9
-rw-r--r--embassy-stm32/src/qspi/mod.rs32
-rw-r--r--embassy-stm32/src/rcc/f4.rs79
-rw-r--r--embassy-stm32/src/rcc/mod.rs6
-rw-r--r--embassy-stm32/src/rtc/datetime.rs203
-rw-r--r--embassy-stm32/src/rtc/datetime_chrono.rs85
-rw-r--r--embassy-stm32/src/rtc/mod.rs235
-rw-r--r--embassy-stm32/src/rtc/v2/mod.rs171
-rw-r--r--embassy-stm32/src/rtc/v2/v2f0.rs41
-rw-r--r--embassy-stm32/src/rtc/v2/v2f2.rs31
-rw-r--r--embassy-stm32/src/rtc/v2/v2f3.rs31
-rw-r--r--embassy-stm32/src/rtc/v2/v2f4.rs31
-rw-r--r--embassy-stm32/src/rtc/v2/v2f7.rs41
-rw-r--r--embassy-stm32/src/rtc/v2/v2h7.rs33
-rw-r--r--embassy-stm32/src/rtc/v2/v2l0.rs26
-rw-r--r--embassy-stm32/src/rtc/v2/v2l1.rs24
-rw-r--r--embassy-stm32/src/rtc/v2/v2l4.rs41
-rw-r--r--embassy-stm32/src/rtc/v2/v2wb.rs39
-rw-r--r--embassy-stm32/src/rtc/v3.rs226
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs1344
-rw-r--r--embassy-stm32/src/spi/mod.rs206
-rw-r--r--embassy-stm32/src/traits.rs2
-rw-r--r--embassy-stm32/src/usart/mod.rs94
-rw-r--r--examples/boot/application/nrf/Cargo.toml4
-rw-r--r--examples/boot/application/rp/Cargo.toml2
-rw-r--r--examples/boot/application/stm32f3/Cargo.toml2
-rw-r--r--examples/boot/application/stm32f7/Cargo.toml2
-rw-r--r--examples/boot/application/stm32h7/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l0/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l1/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l4/Cargo.toml2
-rw-r--r--examples/boot/application/stm32wl/Cargo.toml2
-rw-r--r--examples/boot/bootloader/nrf/Cargo.toml2
-rw-r--r--examples/boot/bootloader/rp/Cargo.toml2
-rw-r--r--examples/boot/bootloader/stm32/Cargo.toml2
-rw-r--r--examples/nrf-rtos-trace/Cargo.toml2
-rw-r--r--examples/nrf52840/Cargo.toml6
-rw-r--r--examples/nrf5340/Cargo.toml2
-rw-r--r--examples/rp/Cargo.toml2
-rw-r--r--examples/stm32f0/Cargo.toml2
-rw-r--r--examples/stm32f1/Cargo.toml2
-rw-r--r--examples/stm32f2/Cargo.toml2
-rw-r--r--examples/stm32f3/Cargo.toml2
-rw-r--r--examples/stm32f4/Cargo.toml4
-rw-r--r--examples/stm32f4/src/bin/sdmmc.rs13
-rw-r--r--examples/stm32f7/Cargo.toml2
-rw-r--r--examples/stm32g0/Cargo.toml2
-rw-r--r--examples/stm32g0/src/bin/spi_neopixel.rs101
-rw-r--r--examples/stm32g4/src/bin/pwm.rs4
-rw-r--r--examples/stm32h5/Cargo.toml2
-rw-r--r--examples/stm32h7/Cargo.toml2
-rw-r--r--examples/stm32l0/Cargo.toml6
-rw-r--r--examples/stm32l0/src/bin/lorawan.rs2
-rw-r--r--examples/stm32l1/Cargo.toml2
-rw-r--r--examples/stm32l5/Cargo.toml2
-rw-r--r--examples/stm32u5/Cargo.toml2
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--examples/stm32wl/Cargo.toml6
-rw-r--r--examples/stm32wl/src/bin/lorawan.rs2
-rw-r--r--rust-toolchain.toml2
-rw-r--r--tests/rp/.cargo/config.toml6
-rw-r--r--tests/rp/Cargo.toml2
-rw-r--r--tests/rp/src/bin/float.rs53
-rw-r--r--tests/stm32/Cargo.toml43
-rw-r--r--tests/stm32/gen_test.py44
-rw-r--r--tests/stm32/src/bin/sdmmc.rs148
104 files changed, 5290 insertions, 2528 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 700804dca..9ef7fe1ce 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -16,10 +16,11 @@
16 // "embassy-executor/Cargo.toml", 16 // "embassy-executor/Cargo.toml",
17 // "embassy-sync/Cargo.toml", 17 // "embassy-sync/Cargo.toml",
18 "examples/nrf52840/Cargo.toml", 18 "examples/nrf52840/Cargo.toml",
19 //"examples/nrf5340/Cargo.toml", 19 // "examples/nrf5340/Cargo.toml",
20 // "examples/nrf-rtos-trace/Cargo.toml", 20 // "examples/nrf-rtos-trace/Cargo.toml",
21 // "examples/rp/Cargo.toml", 21 // "examples/rp/Cargo.toml",
22 // "examples/std/Cargo.toml", 22 // "examples/std/Cargo.toml",
23 // "examples/stm32c0/Cargo.toml",
23 // "examples/stm32f0/Cargo.toml", 24 // "examples/stm32f0/Cargo.toml",
24 // "examples/stm32f1/Cargo.toml", 25 // "examples/stm32f1/Cargo.toml",
25 // "examples/stm32f2/Cargo.toml", 26 // "examples/stm32f2/Cargo.toml",
@@ -28,6 +29,7 @@
28 // "examples/stm32f7/Cargo.toml", 29 // "examples/stm32f7/Cargo.toml",
29 // "examples/stm32g0/Cargo.toml", 30 // "examples/stm32g0/Cargo.toml",
30 // "examples/stm32g4/Cargo.toml", 31 // "examples/stm32g4/Cargo.toml",
32 // "examples/stm32h5/Cargo.toml",
31 // "examples/stm32h7/Cargo.toml", 33 // "examples/stm32h7/Cargo.toml",
32 // "examples/stm32l0/Cargo.toml", 34 // "examples/stm32l0/Cargo.toml",
33 // "examples/stm32l1/Cargo.toml", 35 // "examples/stm32l1/Cargo.toml",
@@ -35,9 +37,7 @@
35 // "examples/stm32l5/Cargo.toml", 37 // "examples/stm32l5/Cargo.toml",
36 // "examples/stm32u5/Cargo.toml", 38 // "examples/stm32u5/Cargo.toml",
37 // "examples/stm32wb/Cargo.toml", 39 // "examples/stm32wb/Cargo.toml",
38 // "examples/stm32wb55/Cargo.toml",
39 // "examples/stm32wl/Cargo.toml", 40 // "examples/stm32wl/Cargo.toml",
40 // "examples/stm32wl55/Cargo.toml",
41 // "examples/wasm/Cargo.toml", 41 // "examples/wasm/Cargo.toml",
42 ], 42 ],
43} \ No newline at end of file 43} \ No newline at end of file
diff --git a/ci.sh b/ci.sh
index 1b3fac8bc..657975041 100755
--- a/ci.sh
+++ b/ci.sh
@@ -22,6 +22,7 @@ cargo batch \
22 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ 22 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
23 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ 23 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
24 --- 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 \ 24 --- 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 \
25 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \
25 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ 26 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
26 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ 27 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
27 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ 28 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
diff --git a/ci_stable.sh b/ci_stable.sh
index b4b0b83e7..18271ee73 100755
--- a/ci_stable.sh
+++ b/ci_stable.sh
@@ -13,6 +13,7 @@ cargo batch \
13 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ 13 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
14 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ 14 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
15 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ 15 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
16 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \
16 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ 17 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
17 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ 18 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
18 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ 19 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs
index ee919bd84..171ff6c9f 100644
--- a/embassy-embedded-hal/src/adapter.rs
+++ b/embassy-embedded-hal/src/adapter.rs
@@ -131,48 +131,6 @@ where
131 type Error = E; 131 type Error = E;
132} 132}
133 133
134#[cfg(feature = "_todo_embedded_hal_serial")]
135impl<T, E> embedded_hal_async::serial::Read for BlockingAsync<T>
136where
137 T: serial::Read<u8, Error = E>,
138 E: embedded_hal_1::serial::Error + 'static,
139{
140 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
141 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
142 async move {
143 let mut pos = 0;
144 while pos < buf.len() {
145 match self.wrapped.read() {
146 Err(nb::Error::WouldBlock) => {}
147 Err(nb::Error::Other(e)) => return Err(e),
148 Ok(b) => {
149 buf[pos] = b;
150 pos += 1;
151 }
152 }
153 }
154 Ok(())
155 }
156 }
157}
158
159#[cfg(feature = "_todo_embedded_hal_serial")]
160impl<T, E> embedded_hal_async::serial::Write for BlockingAsync<T>
161where
162 T: blocking::serial::Write<u8, Error = E> + serial::Read<u8, Error = E>,
163 E: embedded_hal_1::serial::Error + 'static,
164{
165 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
166 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
167 async move { self.wrapped.bwrite_all(buf) }
168 }
169
170 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
171 fn flush(&mut self) -> Result<(), Self::Error> {
172 async move { self.wrapped.bflush() }
173 }
174}
175
176/// NOR flash wrapper 134/// NOR flash wrapper
177use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 135use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
178use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; 136use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs
index a88174a0c..f1ec19ac1 100644
--- a/embassy-executor/src/raw/run_queue.rs
+++ b/embassy-executor/src/raw/run_queue.rs
@@ -4,15 +4,16 @@ use core::ptr::NonNull;
4use atomic_polyfill::{AtomicPtr, Ordering}; 4use atomic_polyfill::{AtomicPtr, Ordering};
5 5
6use super::{TaskHeader, TaskRef}; 6use super::{TaskHeader, TaskRef};
7use crate::raw::util::SyncUnsafeCell;
7 8
8pub(crate) struct RunQueueItem { 9pub(crate) struct RunQueueItem {
9 next: AtomicPtr<TaskHeader>, 10 next: SyncUnsafeCell<Option<TaskRef>>,
10} 11}
11 12
12impl RunQueueItem { 13impl RunQueueItem {
13 pub const fn new() -> Self { 14 pub const fn new() -> Self {
14 Self { 15 Self {
15 next: AtomicPtr::new(ptr::null_mut()), 16 next: SyncUnsafeCell::new(None),
16 } 17 }
17 } 18 }
18} 19}
@@ -51,7 +52,12 @@ impl RunQueue {
51 self.head 52 self.head
52 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { 53 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
53 was_empty = prev.is_null(); 54 was_empty = prev.is_null();
54 task.header().run_queue_item.next.store(prev, Ordering::Relaxed); 55 unsafe {
56 // safety: the pointer is either null or valid
57 let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr()));
58 // safety: there are no concurrent accesses to `next`
59 task.header().run_queue_item.next.set(prev);
60 }
55 Some(task.as_ptr() as *mut _) 61 Some(task.as_ptr() as *mut _)
56 }) 62 })
57 .ok(); 63 .ok();
@@ -64,18 +70,19 @@ impl RunQueue {
64 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. 70 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
65 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { 71 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
66 // Atomically empty the queue. 72 // Atomically empty the queue.
67 let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); 73 let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
74
75 // safety: the pointer is either null or valid
76 let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) };
68 77
69 // Iterate the linked list of tasks that were previously in the queue. 78 // Iterate the linked list of tasks that were previously in the queue.
70 while let Some(task) = NonNull::new(ptr) { 79 while let Some(task) = next {
71 let task = unsafe { TaskRef::from_ptr(task.as_ptr()) };
72 // If the task re-enqueues itself, the `next` pointer will get overwritten. 80 // If the task re-enqueues itself, the `next` pointer will get overwritten.
73 // Therefore, first read the next pointer, and only then process the task. 81 // Therefore, first read the next pointer, and only then process the task.
74 let next = task.header().run_queue_item.next.load(Ordering::Relaxed); 82 // safety: there are no concurrent accesses to `next`
83 next = unsafe { task.header().run_queue_item.next.get() };
75 84
76 on_task(task); 85 on_task(task);
77
78 ptr = next
79 } 86 }
80 } 87 }
81} 88}
diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs
index 5e62e048a..f06b46002 100644
--- a/embassy-hal-common/src/macros.rs
+++ b/embassy-hal-common/src/macros.rs
@@ -1,5 +1,5 @@
1#[macro_export] 1#[macro_export]
2macro_rules! peripherals { 2macro_rules! peripherals_definition {
3 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { 3 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
4 /// Types for the peripheral singletons. 4 /// Types for the peripheral singletons.
5 pub mod peripherals { 5 pub mod peripherals {
@@ -26,7 +26,12 @@ macro_rules! peripherals {
26 $crate::impl_peripheral!($name); 26 $crate::impl_peripheral!($name);
27 )* 27 )*
28 } 28 }
29 };
30}
29 31
32#[macro_export]
33macro_rules! peripherals_struct {
34 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
30 /// Struct containing all the peripheral singletons. 35 /// Struct containing all the peripheral singletons.
31 /// 36 ///
32 /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]. 37 /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`].
@@ -77,6 +82,24 @@ macro_rules! peripherals {
77} 82}
78 83
79#[macro_export] 84#[macro_export]
85macro_rules! peripherals {
86 ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
87 $crate::peripherals_definition!(
88 $(
89 $(#[$cfg])?
90 $name,
91 )*
92 );
93 $crate::peripherals_struct!(
94 $(
95 $(#[$cfg])?
96 $name,
97 )*
98 );
99 };
100}
101
102#[macro_export]
80macro_rules! into_ref { 103macro_rules! into_ref {
81 ($($name:ident),*) => { 104 ($($name:ident),*) => {
82 $( 105 $(
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index 79d5660f4..50449dd46 100644
--- a/embassy-lora/Cargo.toml
+++ b/embassy-lora/Cargo.toml
@@ -38,5 +38,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw
38embedded-hal = { version = "0.2", features = ["unproven"] } 38embedded-hal = { version = "0.2", features = ["unproven"] }
39bit_field = { version = "0.10" } 39bit_field = { version = "0.10" }
40 40
41lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } 41lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] }
42lorawan = { version = "0.7.1", default-features = false } 42lorawan = { version = "0.7.2", default-features = false }
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs
index 3e4748430..5c919cbb6 100644
--- a/embassy-lora/src/lib.rs
+++ b/embassy-lora/src/lib.rs
@@ -1,5 +1,6 @@
1#![no_std] 1#![no_std]
2#![feature(type_alias_impl_trait)] 2#![feature(async_fn_in_trait, impl_trait_projections)]
3#![allow(incomplete_features)]
3//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device 4//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
4//! crate's async LoRaWAN MAC implementation. 5//! crate's async LoRaWAN MAC implementation.
5 6
@@ -34,13 +35,11 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer {
34 self.start = Instant::now(); 35 self.start = Instant::now();
35 } 36 }
36 37
37 type AtFuture<'m> = impl core::future::Future<Output = ()> + 'm; 38 async fn at(&mut self, millis: u64) {
38 fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> { 39 Timer::at(self.start + Duration::from_millis(millis)).await
39 Timer::at(self.start + Duration::from_millis(millis))
40 } 40 }
41 41
42 type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm; 42 async fn delay_ms(&mut self, millis: u64) {
43 fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { 43 Timer::after(Duration::from_millis(millis)).await
44 Timer::after(Duration::from_millis(millis))
45 } 44 }
46} 45}
diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs
index 3d52c1cc7..d76e8c43b 100644
--- a/embassy-lora/src/stm32wl/mod.rs
+++ b/embassy-lora/src/stm32wl/mod.rs
@@ -1,5 +1,5 @@
1//! A radio driver integration for the radio found on STM32WL family devices. 1//! A radio driver integration for the radio found on STM32WL family devices.
2use core::future::{poll_fn, Future}; 2use core::future::poll_fn;
3use core::task::Poll; 3use core::task::Poll;
4 4
5use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 5use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
@@ -241,14 +241,12 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf
241impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { 241impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
242 type PhyError = RadioError; 242 type PhyError = RadioError;
243 243
244 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where Self: 'm; 244 async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
245 fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { 245 self.do_tx(config, buf).await
246 async move { self.do_tx(config, buf).await }
247 } 246 }
248 247
249 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where Self: 'm; 248 async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
250 fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { 249 self.do_rx(config, buf).await
251 async move { self.do_rx(config, buf).await }
252 } 250 }
253} 251}
254 252
diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs
index 8559574cb..2f0b8c8e3 100644
--- a/embassy-lora/src/sx126x/mod.rs
+++ b/embassy-lora/src/sx126x/mod.rs
@@ -1,5 +1,3 @@
1use core::future::Future;
2
3use defmt::Format; 1use defmt::Format;
4use embedded_hal::digital::v2::OutputPin; 2use embedded_hal::digital::v2::OutputPin;
5use embedded_hal_async::digital::Wait; 3use embedded_hal_async::digital::Wait;
@@ -71,83 +69,69 @@ where
71{ 69{
72 type PhyError = RadioError<BUS>; 70 type PhyError = RadioError<BUS>;
73 71
74 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm 72 async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result<u32, Self::PhyError> {
75 where
76 SPI: 'm,
77 CTRL: 'm,
78 WAIT: 'm,
79 BUS: 'm;
80
81 fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
82 trace!("TX START"); 73 trace!("TX START");
83 async move { 74 self.lora
84 self.lora 75 .set_tx_config(
85 .set_tx_config( 76 config.pw,
86 config.pw, 77 config.rf.spreading_factor.into(),
87 config.rf.spreading_factor.into(), 78 config.rf.bandwidth.into(),
88 config.rf.bandwidth.into(), 79 config.rf.coding_rate.into(),
89 config.rf.coding_rate.into(), 80 8,
90 8, 81 false,
91 false, 82 true,
92 true, 83 false,
93 false, 84 0,
94 0, 85 false,
95 false, 86 )
96 ) 87 .await?;
97 .await?; 88 self.lora.set_max_payload_length(buffer.len() as u8).await?;
98 self.lora.set_max_payload_length(buffer.len() as u8).await?; 89 self.lora.set_channel(config.rf.frequency).await?;
99 self.lora.set_channel(config.rf.frequency).await?; 90 self.lora.send(buffer, 0xffffff).await?;
100 self.lora.send(buffer, 0xffffff).await?; 91 self.lora.process_irq(None, None, None).await?;
101 self.lora.process_irq(None, None, None).await?; 92 trace!("TX DONE");
102 trace!("TX DONE"); 93 return Ok(0);
103 return Ok(0);
104 }
105 } 94 }
106 95
107 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm 96 async fn rx(
108 where 97 &mut self,
109 SPI: 'm, 98 config: RfConfig,
110 CTRL: 'm, 99 receiving_buffer: &mut [u8],
111 WAIT: 'm, 100 ) -> Result<(usize, RxQuality), Self::PhyError> {
112 BUS: 'm;
113
114 fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
115 trace!("RX START"); 101 trace!("RX START");
116 async move { 102 self.lora
117 self.lora 103 .set_rx_config(
118 .set_rx_config( 104 config.spreading_factor.into(),
119 config.spreading_factor.into(), 105 config.bandwidth.into(),
120 config.bandwidth.into(), 106 config.coding_rate.into(),
121 config.coding_rate.into(), 107 8,
122 8, 108 4,
123 4, 109 false,
124 false, 110 0u8,
125 0u8, 111 true,
126 true, 112 false,
127 false, 113 0,
128 0, 114 true,
129 true, 115 true,
130 true, 116 )
131 ) 117 .await?;
132 .await?; 118 self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
133 self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; 119 self.lora.set_channel(config.frequency).await?;
134 self.lora.set_channel(config.frequency).await?; 120 self.lora.rx(90 * 1000).await?;
135 self.lora.rx(90 * 1000).await?; 121 let mut received_len = 0u8;
136 let mut received_len = 0u8; 122 self.lora
137 self.lora 123 .process_irq(Some(receiving_buffer), Some(&mut received_len), None)
138 .process_irq(Some(receiving_buffer), Some(&mut received_len), None) 124 .await?;
139 .await?; 125 trace!("RX DONE");
140 trace!("RX DONE");
141
142 let packet_status = self.lora.get_latest_packet_status();
143 let mut rssi = 0i16;
144 let mut snr = 0i8;
145 if packet_status.is_some() {
146 rssi = packet_status.unwrap().rssi as i16;
147 snr = packet_status.unwrap().snr;
148 }
149 126
150 Ok((received_len as usize, RxQuality::new(rssi, snr))) 127 let packet_status = self.lora.get_latest_packet_status();
128 let mut rssi = 0i16;
129 let mut snr = 0i8;
130 if packet_status.is_some() {
131 rssi = packet_status.unwrap().rssi as i16;
132 snr = packet_status.unwrap().snr;
151 } 133 }
134
135 Ok((received_len as usize, RxQuality::new(rssi, snr)))
152 } 136 }
153} 137}
diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs
index 8904c9a13..4e8dc2232 100644
--- a/embassy-lora/src/sx127x/mod.rs
+++ b/embassy-lora/src/sx127x/mod.rs
@@ -1,5 +1,3 @@
1use core::future::Future;
2
3use embedded_hal::digital::v2::OutputPin; 1use embedded_hal::digital::v2::OutputPin;
4use embedded_hal_async::digital::Wait; 2use embedded_hal_async::digital::Wait;
5use embedded_hal_async::spi::*; 3use embedded_hal_async::spi::*;
@@ -88,101 +86,78 @@ where
88{ 86{
89 type PhyError = Sx127xError; 87 type PhyError = Sx127xError;
90 88
91 type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm 89 async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
92 where
93 SPI: 'm,
94 CS: 'm,
95 RESET: 'm,
96 E: 'm,
97 I: 'm,
98 RFS: 'm;
99
100 fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
101 trace!("TX START"); 90 trace!("TX START");
102 async move { 91 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
92 self.rfs.set_tx();
93 self.radio.set_tx_power(14, 0).await?;
94 self.radio.set_frequency(config.rf.frequency).await?;
95 // TODO: Modify radio to support other coding rates
96 self.radio.set_coding_rate_4(5).await?;
97 self.radio
98 .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))
99 .await?;
100 self.radio
101 .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))
102 .await?;
103
104 self.radio.set_preamble_length(8).await?;
105 self.radio.set_lora_pa_ramp().await?;
106 self.radio.set_lora_sync_word().await?;
107 self.radio.set_invert_iq(false).await?;
108 self.radio.set_crc(true).await?;
109
110 self.radio.set_dio0_tx_done().await?;
111
112 self.radio.transmit_start(buf).await?;
113
114 loop {
115 self.irq.wait_for_rising_edge().await.unwrap();
103 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); 116 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
104 self.rfs.set_tx(); 117 let irq = self.radio.clear_irq().await.ok().unwrap();
105 self.radio.set_tx_power(14, 0).await?; 118 if (irq & IRQ::IrqTxDoneMask.addr()) != 0 {
106 self.radio.set_frequency(config.rf.frequency).await?; 119 trace!("TX DONE");
107 // TODO: Modify radio to support other coding rates 120 return Ok(0);
108 self.radio.set_coding_rate_4(5).await?;
109 self.radio
110 .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))
111 .await?;
112 self.radio
113 .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))
114 .await?;
115
116 self.radio.set_preamble_length(8).await?;
117 self.radio.set_lora_pa_ramp().await?;
118 self.radio.set_lora_sync_word().await?;
119 self.radio.set_invert_iq(false).await?;
120 self.radio.set_crc(true).await?;
121
122 self.radio.set_dio0_tx_done().await?;
123
124 self.radio.transmit_start(buf).await?;
125
126 loop {
127 self.irq.wait_for_rising_edge().await.unwrap();
128 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
129 let irq = self.radio.clear_irq().await.ok().unwrap();
130 if (irq & IRQ::IrqTxDoneMask.addr()) != 0 {
131 trace!("TX DONE");
132 return Ok(0);
133 }
134 } 121 }
135 } 122 }
136 } 123 }
137 124
138 type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm 125 async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
139 where 126 self.rfs.set_rx();
140 SPI: 'm, 127 self.radio.reset_payload_length().await?;
141 CS: 'm, 128 self.radio.set_frequency(config.frequency).await?;
142 RESET: 'm, 129 // TODO: Modify radio to support other coding rates
143 E: 'm, 130 self.radio.set_coding_rate_4(5).await?;
144 I: 'm, 131 self.radio
145 RFS: 'm; 132 .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth))
146 133 .await?;
147 fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { 134 self.radio
148 trace!("RX START"); 135 .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor))
149 async move { 136 .await?;
150 self.rfs.set_rx(); 137
151 self.radio.reset_payload_length().await?; 138 self.radio.set_preamble_length(8).await?;
152 self.radio.set_frequency(config.frequency).await?; 139 self.radio.set_lora_sync_word().await?;
153 // TODO: Modify radio to support other coding rates 140 self.radio.set_invert_iq(true).await?;
154 self.radio.set_coding_rate_4(5).await?; 141 self.radio.set_crc(true).await?;
155 self.radio 142
156 .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) 143 self.radio.set_dio0_rx_done().await?;
157 .await?; 144 self.radio.set_mode(RadioMode::RxContinuous).await?;
158 self.radio 145
159 .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) 146 loop {
160 .await?; 147 self.irq.wait_for_rising_edge().await.unwrap();
161 148 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
162 self.radio.set_preamble_length(8).await?; 149 let irq = self.radio.clear_irq().await.ok().unwrap();
163 self.radio.set_lora_sync_word().await?; 150 if (irq & IRQ::IrqRxDoneMask.addr()) != 0 {
164 self.radio.set_invert_iq(true).await?; 151 let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16;
165 self.radio.set_crc(true).await?; 152 let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8;
166 153 let response = if let Ok(size) = self.radio.read_packet_size().await {
167 self.radio.set_dio0_rx_done().await?; 154 self.radio.read_packet(buf).await?;
168 self.radio.set_mode(RadioMode::RxContinuous).await?; 155 Ok((size, RxQuality::new(rssi, snr)))
169 156 } else {
170 loop { 157 Ok((0, RxQuality::new(rssi, snr)))
171 self.irq.wait_for_rising_edge().await.unwrap(); 158 };
172 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); 159 trace!("RX DONE");
173 let irq = self.radio.clear_irq().await.ok().unwrap(); 160 return response;
174 if (irq & IRQ::IrqRxDoneMask.addr()) != 0 {
175 let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16;
176 let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8;
177 let response = if let Ok(size) = self.radio.read_packet_size().await {
178 self.radio.read_packet(buf).await?;
179 Ok((size, RxQuality::new(rssi, snr)))
180 } else {
181 Ok((0, RxQuality::new(rssi, snr)))
182 };
183 trace!("RX DONE");
184 return response;
185 }
186 } 161 }
187 } 162 }
188 } 163 }
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index 7b9d0e773..5dfb5843e 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -27,12 +27,10 @@ use embassy_sync::waitqueue::WakerRegistration;
27use embassy_time::{Instant, Timer}; 27use embassy_time::{Instant, Timer};
28use futures::pin_mut; 28use futures::pin_mut;
29use heapless::Vec; 29use heapless::Vec;
30use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
30#[cfg(feature = "dhcpv4")] 31#[cfg(feature = "dhcpv4")]
31use smoltcp::iface::SocketHandle; 32use smoltcp::socket::dhcpv4::{self, RetryConfig};
32use smoltcp::iface::{Interface, SocketSet, SocketStorage};
33#[cfg(feature = "dhcpv4")] 33#[cfg(feature = "dhcpv4")]
34use smoltcp::socket::dhcpv4;
35use smoltcp::socket::dhcpv4::RetryConfig;
36use smoltcp::time::Duration; 34use smoltcp::time::Duration;
37// smoltcp reexports 35// smoltcp reexports
38pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; 36pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant};
@@ -76,6 +74,7 @@ pub struct StaticConfig {
76 pub dns_servers: Vec<Ipv4Address, 3>, 74 pub dns_servers: Vec<Ipv4Address, 3>,
77} 75}
78 76
77#[cfg(feature = "dhcpv4")]
79#[derive(Debug, Clone, PartialEq, Eq)] 78#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct DhcpConfig { 79pub struct DhcpConfig {
81 pub max_lease_duration: Option<Duration>, 80 pub max_lease_duration: Option<Duration>,
@@ -88,6 +87,7 @@ pub struct DhcpConfig {
88 pub client_port: u16, 87 pub client_port: u16,
89} 88}
90 89
90#[cfg(feature = "dhcpv4")]
91impl Default for DhcpConfig { 91impl Default for DhcpConfig {
92 fn default() -> Self { 92 fn default() -> Self {
93 Self { 93 Self {
@@ -384,6 +384,7 @@ impl<D: Driver + 'static> Inner<D> {
384 self.config = Some(config) 384 self.config = Some(config)
385 } 385 }
386 386
387 #[cfg(feature = "dhcpv4")]
387 fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { 388 fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) {
388 socket.set_ignore_naks(config.ignore_naks); 389 socket.set_ignore_naks(config.ignore_naks);
389 socket.set_max_lease_duration(config.max_lease_duration); 390 socket.set_max_lease_duration(config.max_lease_duration);
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index e59b2332a..586c88b2d 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -992,80 +992,3 @@ mod eh1 {
992 type Error = Error; 992 type Error = Error;
993 } 993 }
994} 994}
995
996#[cfg(all(
997 feature = "unstable-traits",
998 feature = "nightly",
999 feature = "_todo_embedded_hal_serial"
1000))]
1001mod eha {
1002 use core::future::Future;
1003
1004 use super::*;
1005
1006 impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
1007 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1008
1009 fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
1010 self.read(buffer)
1011 }
1012 }
1013
1014 impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
1015 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1016
1017 fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
1018 self.write(buffer)
1019 }
1020
1021 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1022
1023 fn flush(&mut self) -> Result<(), Self::Error> {
1024 async move { Ok(()) }
1025 }
1026 }
1027
1028 impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
1029 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1030
1031 fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
1032 self.write(buffer)
1033 }
1034
1035 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1036
1037 fn flush(&mut self) -> Result<(), Self::Error> {
1038 async move { Ok(()) }
1039 }
1040 }
1041
1042 impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
1043 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1044
1045 fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
1046 self.read(buffer)
1047 }
1048 }
1049
1050 impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> {
1051 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1052
1053 fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
1054 self.read(buffer)
1055 }
1056 }
1057
1058 impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> {
1059 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1060
1061 fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
1062 self.write(buffer)
1063 }
1064
1065 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1066
1067 fn flush(&mut self) -> Result<(), Self::Error> {
1068 async move { Ok(()) }
1069 }
1070 }
1071}
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 2ef2c8f07..b7ed6ccbb 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -61,8 +61,7 @@ embedded-io = { version = "0.4.0", features = ["async"], optional = true }
61embedded-storage = { version = "0.3" } 61embedded-storage = { version = "0.3" }
62rand_core = "0.6.4" 62rand_core = "0.6.4"
63 63
64rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } 64rp-pac = { version = "1", features = ["rt"] }
65#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
66 65
67embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 66embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
68embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} 67embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
diff --git a/embassy-rp/src/float/add_sub.rs b/embassy-rp/src/float/add_sub.rs
new file mode 100644
index 000000000..673544cfe
--- /dev/null
+++ b/embassy-rp/src/float/add_sub.rs
@@ -0,0 +1,92 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs
3
4use super::{Float, Int};
5use crate::rom_data;
6
7trait ROMAdd {
8 fn rom_add(self, b: Self) -> Self;
9}
10
11impl ROMAdd for f32 {
12 fn rom_add(self, b: Self) -> Self {
13 rom_data::float_funcs::fadd(self, b)
14 }
15}
16
17impl ROMAdd for f64 {
18 fn rom_add(self, b: Self) -> Self {
19 rom_data::double_funcs::dadd(self, b)
20 }
21}
22
23fn add<F: Float + ROMAdd>(a: F, b: F) -> F {
24 if a.is_not_finite() {
25 if b.is_not_finite() {
26 let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
27 let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
28
29 if class_a == F::Int::ZERO && class_b == F::Int::ZERO {
30 // inf + inf = inf
31 return a;
32 }
33 if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK {
34 // -inf + (-inf) = -inf
35 return a;
36 }
37
38 // Sign mismatch, or either is NaN already
39 return F::NAN;
40 }
41
42 // [-]inf/NaN + X = [-]inf/NaN
43 return a;
44 }
45
46 if b.is_not_finite() {
47 // X + [-]inf/NaN = [-]inf/NaN
48 return b;
49 }
50
51 a.rom_add(b)
52}
53
54intrinsics! {
55 #[alias = __addsf3vfp]
56 #[aeabi = __aeabi_fadd]
57 extern "C" fn __addsf3(a: f32, b: f32) -> f32 {
58 add(a, b)
59 }
60
61 #[bootrom_v2]
62 #[alias = __adddf3vfp]
63 #[aeabi = __aeabi_dadd]
64 extern "C" fn __adddf3(a: f64, b: f64) -> f64 {
65 add(a, b)
66 }
67
68 // The ROM just implements subtraction the same way, so just do it here
69 // and save the work of implementing more complicated NaN/inf handling.
70
71 #[alias = __subsf3vfp]
72 #[aeabi = __aeabi_fsub]
73 extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
74 add(a, -b)
75 }
76
77 #[bootrom_v2]
78 #[alias = __subdf3vfp]
79 #[aeabi = __aeabi_dsub]
80 extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
81 add(a, -b)
82 }
83
84 extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 {
85 add(b, -a)
86 }
87
88 #[bootrom_v2]
89 extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 {
90 add(b, -a)
91 }
92}
diff --git a/embassy-rp/src/float/cmp.rs b/embassy-rp/src/float/cmp.rs
new file mode 100644
index 000000000..e540e3918
--- /dev/null
+++ b/embassy-rp/src/float/cmp.rs
@@ -0,0 +1,201 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs
3
4use super::Float;
5use crate::rom_data;
6
7trait ROMCmp {
8 fn rom_cmp(self, b: Self) -> i32;
9}
10
11impl ROMCmp for f32 {
12 fn rom_cmp(self, b: Self) -> i32 {
13 rom_data::float_funcs::fcmp(self, b)
14 }
15}
16
17impl ROMCmp for f64 {
18 fn rom_cmp(self, b: Self) -> i32 {
19 rom_data::double_funcs::dcmp(self, b)
20 }
21}
22
23fn le_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
24 if a.is_nan() || b.is_nan() {
25 1
26 } else {
27 a.rom_cmp(b)
28 }
29}
30
31fn ge_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
32 if a.is_nan() || b.is_nan() {
33 -1
34 } else {
35 a.rom_cmp(b)
36 }
37}
38
39intrinsics! {
40 #[slower_than_default]
41 #[bootrom_v2]
42 #[alias = __eqsf2, __ltsf2, __nesf2]
43 extern "C" fn __lesf2(a: f32, b: f32) -> i32 {
44 le_abi(a, b)
45 }
46
47 #[slower_than_default]
48 #[bootrom_v2]
49 #[alias = __eqdf2, __ltdf2, __nedf2]
50 extern "C" fn __ledf2(a: f64, b: f64) -> i32 {
51 le_abi(a, b)
52 }
53
54 #[slower_than_default]
55 #[bootrom_v2]
56 #[alias = __gtsf2]
57 extern "C" fn __gesf2(a: f32, b: f32) -> i32 {
58 ge_abi(a, b)
59 }
60
61 #[slower_than_default]
62 #[bootrom_v2]
63 #[alias = __gtdf2]
64 extern "C" fn __gedf2(a: f64, b: f64) -> i32 {
65 ge_abi(a, b)
66 }
67
68
69 #[slower_than_default]
70 #[bootrom_v2]
71 extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {
72 (le_abi(a, b) <= 0) as i32
73 }
74
75 #[slower_than_default]
76 #[bootrom_v2]
77 extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {
78 (ge_abi(a, b) >= 0) as i32
79 }
80
81 #[slower_than_default]
82 #[bootrom_v2]
83 extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {
84 (le_abi(a, b) == 0) as i32
85 }
86
87 #[slower_than_default]
88 #[bootrom_v2]
89 extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {
90 (le_abi(a, b) < 0) as i32
91 }
92
93 #[slower_than_default]
94 #[bootrom_v2]
95 extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {
96 (ge_abi(a, b) > 0) as i32
97 }
98
99 #[slower_than_default]
100 #[bootrom_v2]
101 extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {
102 (le_abi(a, b) <= 0) as i32
103 }
104
105 #[slower_than_default]
106 #[bootrom_v2]
107 extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {
108 (ge_abi(a, b) >= 0) as i32
109 }
110
111 #[slower_than_default]
112 #[bootrom_v2]
113 extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {
114 (le_abi(a, b) == 0) as i32
115 }
116
117 #[slower_than_default]
118 #[bootrom_v2]
119 extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {
120 (le_abi(a, b) < 0) as i32
121 }
122
123 #[slower_than_default]
124 #[bootrom_v2]
125 extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {
126 (ge_abi(a, b) > 0) as i32
127 }
128
129
130 #[slower_than_default]
131 #[bootrom_v2]
132 extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 {
133 (ge_abi(a, b) >= 0) as i32
134 }
135
136 #[slower_than_default]
137 #[bootrom_v2]
138 extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 {
139 (ge_abi(a, b) >= 0) as i32
140 }
141
142 #[slower_than_default]
143 #[bootrom_v2]
144 extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 {
145 (ge_abi(a, b) > 0) as i32
146 }
147
148 #[slower_than_default]
149 #[bootrom_v2]
150 extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 {
151 (ge_abi(a, b) > 0) as i32
152 }
153
154 #[slower_than_default]
155 #[bootrom_v2]
156 extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 {
157 (le_abi(a, b) < 0) as i32
158 }
159
160 #[slower_than_default]
161 #[bootrom_v2]
162 extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 {
163 (le_abi(a, b) < 0) as i32
164 }
165
166 #[slower_than_default]
167 #[bootrom_v2]
168 extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 {
169 (le_abi(a, b) <= 0) as i32
170 }
171
172 #[slower_than_default]
173 #[bootrom_v2]
174 extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 {
175 (le_abi(a, b) <= 0) as i32
176 }
177
178 #[slower_than_default]
179 #[bootrom_v2]
180 extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 {
181 (le_abi(a, b) != 0) as i32
182 }
183
184 #[slower_than_default]
185 #[bootrom_v2]
186 extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 {
187 (le_abi(a, b) != 0) as i32
188 }
189
190 #[slower_than_default]
191 #[bootrom_v2]
192 extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 {
193 (le_abi(a, b) == 0) as i32
194 }
195
196 #[slower_than_default]
197 #[bootrom_v2]
198 extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 {
199 (le_abi(a, b) == 0) as i32
200 }
201}
diff --git a/embassy-rp/src/float/conv.rs b/embassy-rp/src/float/conv.rs
new file mode 100644
index 000000000..021826e28
--- /dev/null
+++ b/embassy-rp/src/float/conv.rs
@@ -0,0 +1,157 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs
3
4use super::Float;
5use crate::rom_data;
6
7// Some of these are also not connected in the Pico SDK. This is probably
8// because the ROM version actually does a fixed point conversion, just with
9// the fractional width set to zero.
10
11intrinsics! {
12 // Not connected in the Pico SDK
13 #[slower_than_default]
14 #[aeabi = __aeabi_i2f]
15 extern "C" fn __floatsisf(i: i32) -> f32 {
16 rom_data::float_funcs::int_to_float(i)
17 }
18
19 // Not connected in the Pico SDK
20 #[slower_than_default]
21 #[aeabi = __aeabi_i2d]
22 extern "C" fn __floatsidf(i: i32) -> f64 {
23 rom_data::double_funcs::int_to_double(i)
24 }
25
26 // Questionable gain
27 #[aeabi = __aeabi_l2f]
28 extern "C" fn __floatdisf(i: i64) -> f32 {
29 rom_data::float_funcs::int64_to_float(i)
30 }
31
32 #[bootrom_v2]
33 #[aeabi = __aeabi_l2d]
34 extern "C" fn __floatdidf(i: i64) -> f64 {
35 rom_data::double_funcs::int64_to_double(i)
36 }
37
38 // Not connected in the Pico SDK
39 #[slower_than_default]
40 #[aeabi = __aeabi_ui2f]
41 extern "C" fn __floatunsisf(i: u32) -> f32 {
42 rom_data::float_funcs::uint_to_float(i)
43 }
44
45 // Questionable gain
46 #[bootrom_v2]
47 #[aeabi = __aeabi_ui2d]
48 extern "C" fn __floatunsidf(i: u32) -> f64 {
49 rom_data::double_funcs::uint_to_double(i)
50 }
51
52 // Questionable gain
53 #[bootrom_v2]
54 #[aeabi = __aeabi_ul2f]
55 extern "C" fn __floatundisf(i: u64) -> f32 {
56 rom_data::float_funcs::uint64_to_float(i)
57 }
58
59 #[bootrom_v2]
60 #[aeabi = __aeabi_ul2d]
61 extern "C" fn __floatundidf(i: u64) -> f64 {
62 rom_data::double_funcs::uint64_to_double(i)
63 }
64
65
66 // The Pico SDK does some optimization here (e.x. fast paths for zero and
67 // one), but we can just directly connect it.
68 #[aeabi = __aeabi_f2iz]
69 extern "C" fn __fixsfsi(f: f32) -> i32 {
70 rom_data::float_funcs::float_to_int(f)
71 }
72
73 #[bootrom_v2]
74 #[aeabi = __aeabi_f2lz]
75 extern "C" fn __fixsfdi(f: f32) -> i64 {
76 rom_data::float_funcs::float_to_int64(f)
77 }
78
79 // Not connected in the Pico SDK
80 #[slower_than_default]
81 #[bootrom_v2]
82 #[aeabi = __aeabi_d2iz]
83 extern "C" fn __fixdfsi(f: f64) -> i32 {
84 rom_data::double_funcs::double_to_int(f)
85 }
86
87 // Like with the 32 bit version, there's optimization that we just
88 // skip.
89 #[bootrom_v2]
90 #[aeabi = __aeabi_d2lz]
91 extern "C" fn __fixdfdi(f: f64) -> i64 {
92 rom_data::double_funcs::double_to_int64(f)
93 }
94
95 #[slower_than_default]
96 #[aeabi = __aeabi_f2uiz]
97 extern "C" fn __fixunssfsi(f: f32) -> u32 {
98 rom_data::float_funcs::float_to_uint(f)
99 }
100
101 #[slower_than_default]
102 #[bootrom_v2]
103 #[aeabi = __aeabi_f2ulz]
104 extern "C" fn __fixunssfdi(f: f32) -> u64 {
105 rom_data::float_funcs::float_to_uint64(f)
106 }
107
108 #[slower_than_default]
109 #[bootrom_v2]
110 #[aeabi = __aeabi_d2uiz]
111 extern "C" fn __fixunsdfsi(f: f64) -> u32 {
112 rom_data::double_funcs::double_to_uint(f)
113 }
114
115 #[slower_than_default]
116 #[bootrom_v2]
117 #[aeabi = __aeabi_d2ulz]
118 extern "C" fn __fixunsdfdi(f: f64) -> u64 {
119 rom_data::double_funcs::double_to_uint64(f)
120 }
121
122 #[bootrom_v2]
123 #[alias = __extendsfdf2vfp]
124 #[aeabi = __aeabi_f2d]
125 extern "C" fn __extendsfdf2(f: f32) -> f64 {
126 if f.is_not_finite() {
127 return f64::from_repr(
128 // Not finite
129 f64::EXPONENT_MASK |
130 // Preserve NaN or inf
131 ((f.repr() & f32::SIGNIFICAND_MASK) as u64) |
132 // Preserve sign
133 ((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS)
134 );
135 }
136 rom_data::float_funcs::float_to_double(f)
137 }
138
139 #[bootrom_v2]
140 #[alias = __truncdfsf2vfp]
141 #[aeabi = __aeabi_d2f]
142 extern "C" fn __truncdfsf2(f: f64) -> f32 {
143 if f.is_not_finite() {
144 let mut repr: u32 =
145 // Not finite
146 f32::EXPONENT_MASK |
147 // Preserve sign
148 ((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32;
149 // Set NaN
150 if (f.repr() & f64::SIGNIFICAND_MASK) != 0 {
151 repr |= 1;
152 }
153 return f32::from_repr(repr);
154 }
155 rom_data::double_funcs::double_to_float(f)
156 }
157}
diff --git a/embassy-rp/src/float/div.rs b/embassy-rp/src/float/div.rs
new file mode 100644
index 000000000..094dec446
--- /dev/null
+++ b/embassy-rp/src/float/div.rs
@@ -0,0 +1,141 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs
3
4use super::Float;
5use crate::rom_data;
6
7// Make sure this stays as a separate call, because when it's inlined the
8// compiler will move the save of the registers used to contain the divider
9// state into the function prologue. That save and restore (push/pop) takes
10// longer than the actual division, so doing it in the common case where
11// they are not required wastes a lot of time.
12#[inline(never)]
13#[cold]
14fn save_divider_and_call<F, R>(f: F) -> R
15where
16 F: FnOnce() -> R,
17{
18 let sio = rp_pac::SIO;
19
20 unsafe {
21 // Since we can't save the signed-ness of the calculation, we have to make
22 // sure that there's at least an 8 cycle delay before we read the result.
23 // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads.
24 // Since we can't be sure the Rust implementation will optimize to the same,
25 // just use an explicit wait.
26 while !sio.div().csr().read().ready() {}
27
28 // Read the quotient last, since that's what clears the dirty flag
29 let dividend = sio.div().udividend().read();
30 let divisor = sio.div().udivisor().read();
31 let remainder = sio.div().remainder().read();
32 let quotient = sio.div().quotient().read();
33
34 // If we get interrupted here (before a write sets the DIRTY flag) its fine, since
35 // we have the full state, so the interruptor doesn't have to restore it. Once the
36 // write happens and the DIRTY flag is set, the interruptor becomes responsible for
37 // restoring our state.
38 let result = f();
39
40 // If we are interrupted here, then the interruptor will start an incorrect calculation
41 // using a wrong divisor, but we'll restore the divisor and result ourselves correctly.
42 // This sets DIRTY, so any interruptor will save the state.
43 sio.div().udividend().write_value(dividend);
44 // If we are interrupted here, the the interruptor may start the calculation using
45 // incorrectly signed inputs, but we'll restore the result ourselves.
46 // This sets DIRTY, so any interruptor will save the state.
47 sio.div().udivisor().write_value(divisor);
48 // If we are interrupted here, the interruptor will have restored everything but the
49 // quotient may be wrongly signed. If the calculation started by the above writes is
50 // still ongoing it is stopped, so it won't replace the result we're restoring.
51 // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state.
52 sio.div().remainder().write_value(remainder);
53 // State fully restored after the quotient write. This sets both DIRTY and READY, so
54 // whatever we may have interrupted can read the result.
55 sio.div().quotient().write_value(quotient);
56
57 result
58 }
59}
60
61fn save_divider<F, R>(f: F) -> R
62where
63 F: FnOnce() -> R,
64{
65 let sio = rp_pac::SIO;
66 if unsafe { !sio.div().csr().read().dirty() } {
67 // Not dirty, so nothing is waiting for the calculation. So we can just
68 // issue it directly without a save/restore.
69 f()
70 } else {
71 save_divider_and_call(f)
72 }
73}
74
75trait ROMDiv {
76 fn rom_div(self, b: Self) -> Self;
77}
78
79impl ROMDiv for f32 {
80 fn rom_div(self, b: Self) -> Self {
81 // ROM implementation uses the hardware divider, so we have to save it
82 save_divider(|| rom_data::float_funcs::fdiv(self, b))
83 }
84}
85
86impl ROMDiv for f64 {
87 fn rom_div(self, b: Self) -> Self {
88 // ROM implementation uses the hardware divider, so we have to save it
89 save_divider(|| rom_data::double_funcs::ddiv(self, b))
90 }
91}
92
93fn div<F: Float + ROMDiv>(a: F, b: F) -> F {
94 if a.is_not_finite() {
95 if b.is_not_finite() {
96 // inf/NaN / inf/NaN = NaN
97 return F::NAN;
98 }
99
100 if b.is_zero() {
101 // inf/NaN / 0 = NaN
102 return F::NAN;
103 }
104
105 return if b.is_sign_negative() {
106 // [+/-]inf/NaN / (-X) = [-/+]inf/NaN
107 a.negate()
108 } else {
109 // [-]inf/NaN / X = [-]inf/NaN
110 a
111 };
112 }
113
114 if b.is_nan() {
115 // X / NaN = NaN
116 return b;
117 }
118
119 // ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only
120 // need to catch 0 / 0
121 if b.is_zero() && a.is_zero() {
122 return F::NAN;
123 }
124
125 a.rom_div(b)
126}
127
128intrinsics! {
129 #[alias = __divsf3vfp]
130 #[aeabi = __aeabi_fdiv]
131 extern "C" fn __divsf3(a: f32, b: f32) -> f32 {
132 div(a, b)
133 }
134
135 #[bootrom_v2]
136 #[alias = __divdf3vfp]
137 #[aeabi = __aeabi_ddiv]
138 extern "C" fn __divdf3(a: f64, b: f64) -> f64 {
139 div(a, b)
140 }
141}
diff --git a/embassy-rp/src/float/functions.rs b/embassy-rp/src/float/functions.rs
new file mode 100644
index 000000000..de29ce336
--- /dev/null
+++ b/embassy-rp/src/float/functions.rs
@@ -0,0 +1,239 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs
3
4use crate::float::{Float, Int};
5use crate::rom_data;
6
7trait ROMFunctions {
8 fn sqrt(self) -> Self;
9 fn ln(self) -> Self;
10 fn exp(self) -> Self;
11 fn sin(self) -> Self;
12 fn cos(self) -> Self;
13 fn tan(self) -> Self;
14 fn atan2(self, y: Self) -> Self;
15
16 fn to_trig_range(self) -> Self;
17}
18
19impl ROMFunctions for f32 {
20 fn sqrt(self) -> Self {
21 rom_data::float_funcs::fsqrt(self)
22 }
23
24 fn ln(self) -> Self {
25 rom_data::float_funcs::fln(self)
26 }
27
28 fn exp(self) -> Self {
29 rom_data::float_funcs::fexp(self)
30 }
31
32 fn sin(self) -> Self {
33 rom_data::float_funcs::fsin(self)
34 }
35
36 fn cos(self) -> Self {
37 rom_data::float_funcs::fcos(self)
38 }
39
40 fn tan(self) -> Self {
41 rom_data::float_funcs::ftan(self)
42 }
43
44 fn atan2(self, y: Self) -> Self {
45 rom_data::float_funcs::fatan2(self, y)
46 }
47
48 fn to_trig_range(self) -> Self {
49 // -128 < X < 128, logic from the Pico SDK
50 let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
51 if exponent < 134 {
52 self
53 } else {
54 self % (core::f32::consts::PI * 2.0)
55 }
56 }
57}
58
59impl ROMFunctions for f64 {
60 fn sqrt(self) -> Self {
61 rom_data::double_funcs::dsqrt(self)
62 }
63
64 fn ln(self) -> Self {
65 rom_data::double_funcs::dln(self)
66 }
67
68 fn exp(self) -> Self {
69 rom_data::double_funcs::dexp(self)
70 }
71
72 fn sin(self) -> Self {
73 rom_data::double_funcs::dsin(self)
74 }
75
76 fn cos(self) -> Self {
77 rom_data::double_funcs::dcos(self)
78 }
79 fn tan(self) -> Self {
80 rom_data::double_funcs::dtan(self)
81 }
82
83 fn atan2(self, y: Self) -> Self {
84 rom_data::double_funcs::datan2(self, y)
85 }
86
87 fn to_trig_range(self) -> Self {
88 // -1024 < X < 1024, logic from the Pico SDK
89 let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
90 if exponent < 1033 {
91 self
92 } else {
93 self % (core::f64::consts::PI * 2.0)
94 }
95 }
96}
97
98fn is_negative_nonzero_or_nan<F: Float>(f: F) -> bool {
99 let repr = f.repr();
100 if (repr & F::SIGN_MASK) != F::Int::ZERO {
101 // Negative, so anything other than exactly zero
102 return (repr & (!F::SIGN_MASK)) != F::Int::ZERO;
103 }
104 // NaN
105 (repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK
106}
107
108fn sqrt<F: Float + ROMFunctions>(f: F) -> F {
109 if is_negative_nonzero_or_nan(f) {
110 F::NAN
111 } else {
112 f.sqrt()
113 }
114}
115
116fn ln<F: Float + ROMFunctions>(f: F) -> F {
117 if is_negative_nonzero_or_nan(f) {
118 F::NAN
119 } else {
120 f.ln()
121 }
122}
123
124fn exp<F: Float + ROMFunctions>(f: F) -> F {
125 if f.is_nan() {
126 F::NAN
127 } else {
128 f.exp()
129 }
130}
131
132fn sin<F: Float + ROMFunctions>(f: F) -> F {
133 if f.is_not_finite() {
134 F::NAN
135 } else {
136 f.to_trig_range().sin()
137 }
138}
139
140fn cos<F: Float + ROMFunctions>(f: F) -> F {
141 if f.is_not_finite() {
142 F::NAN
143 } else {
144 f.to_trig_range().cos()
145 }
146}
147
148fn tan<F: Float + ROMFunctions>(f: F) -> F {
149 if f.is_not_finite() {
150 F::NAN
151 } else {
152 f.to_trig_range().tan()
153 }
154}
155
156fn atan2<F: Float + ROMFunctions>(x: F, y: F) -> F {
157 if x.is_nan() || y.is_nan() {
158 F::NAN
159 } else {
160 x.to_trig_range().atan2(y)
161 }
162}
163
164// Name collisions
165mod intrinsics {
166 intrinsics! {
167 extern "C" fn sqrtf(f: f32) -> f32 {
168 super::sqrt(f)
169 }
170
171 #[bootrom_v2]
172 extern "C" fn sqrt(f: f64) -> f64 {
173 super::sqrt(f)
174 }
175
176 extern "C" fn logf(f: f32) -> f32 {
177 super::ln(f)
178 }
179
180 #[bootrom_v2]
181 extern "C" fn log(f: f64) -> f64 {
182 super::ln(f)
183 }
184
185 extern "C" fn expf(f: f32) -> f32 {
186 super::exp(f)
187 }
188
189 #[bootrom_v2]
190 extern "C" fn exp(f: f64) -> f64 {
191 super::exp(f)
192 }
193
194 #[slower_than_default]
195 extern "C" fn sinf(f: f32) -> f32 {
196 super::sin(f)
197 }
198
199 #[slower_than_default]
200 #[bootrom_v2]
201 extern "C" fn sin(f: f64) -> f64 {
202 super::sin(f)
203 }
204
205 #[slower_than_default]
206 extern "C" fn cosf(f: f32) -> f32 {
207 super::cos(f)
208 }
209
210 #[slower_than_default]
211 #[bootrom_v2]
212 extern "C" fn cos(f: f64) -> f64 {
213 super::cos(f)
214 }
215
216 #[slower_than_default]
217 extern "C" fn tanf(f: f32) -> f32 {
218 super::tan(f)
219 }
220
221 #[slower_than_default]
222 #[bootrom_v2]
223 extern "C" fn tan(f: f64) -> f64 {
224 super::tan(f)
225 }
226
227 // Questionable gain
228 #[bootrom_v2]
229 extern "C" fn atan2f(a: f32, b: f32) -> f32 {
230 super::atan2(a, b)
231 }
232
233 // Questionable gain
234 #[bootrom_v2]
235 extern "C" fn atan2(a: f64, b: f64) -> f64 {
236 super::atan2(a, b)
237 }
238 }
239}
diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs
new file mode 100644
index 000000000..945afff90
--- /dev/null
+++ b/embassy-rp/src/float/mod.rs
@@ -0,0 +1,149 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs
3
4use core::ops;
5
6// Borrowed and simplified from compiler-builtins so we can use bit ops
7// on floating point without macro soup.
8pub(crate) trait Int:
9 Copy
10 + core::fmt::Debug
11 + PartialEq
12 + PartialOrd
13 + ops::AddAssign
14 + ops::SubAssign
15 + ops::BitAndAssign
16 + ops::BitOrAssign
17 + ops::BitXorAssign
18 + ops::ShlAssign<i32>
19 + ops::ShrAssign<u32>
20 + ops::Add<Output = Self>
21 + ops::Sub<Output = Self>
22 + ops::Div<Output = Self>
23 + ops::Shl<u32, Output = Self>
24 + ops::Shr<u32, Output = Self>
25 + ops::BitOr<Output = Self>
26 + ops::BitXor<Output = Self>
27 + ops::BitAnd<Output = Self>
28 + ops::Not<Output = Self>
29{
30 const ZERO: Self;
31}
32
33macro_rules! int_impl {
34 ($ty:ty) => {
35 impl Int for $ty {
36 const ZERO: Self = 0;
37 }
38 };
39}
40
41int_impl!(u32);
42int_impl!(u64);
43
44pub(crate) trait Float:
45 Copy
46 + core::fmt::Debug
47 + PartialEq
48 + PartialOrd
49 + ops::AddAssign
50 + ops::MulAssign
51 + ops::Add<Output = Self>
52 + ops::Sub<Output = Self>
53 + ops::Div<Output = Self>
54 + ops::Rem<Output = Self>
55{
56 /// A uint of the same with as the float
57 type Int: Int;
58
59 /// NaN representation for the float
60 const NAN: Self;
61
62 /// The bitwidth of the float type
63 const BITS: u32;
64
65 /// The bitwidth of the significand
66 const SIGNIFICAND_BITS: u32;
67
68 /// A mask for the sign bit
69 const SIGN_MASK: Self::Int;
70
71 /// A mask for the significand
72 const SIGNIFICAND_MASK: Self::Int;
73
74 /// A mask for the exponent
75 const EXPONENT_MASK: Self::Int;
76
77 /// Returns `self` transmuted to `Self::Int`
78 fn repr(self) -> Self::Int;
79
80 /// Returns a `Self::Int` transmuted back to `Self`
81 fn from_repr(a: Self::Int) -> Self;
82
83 /// Return a sign swapped `self`
84 fn negate(self) -> Self;
85
86 /// Returns true if `self` is either NaN or infinity
87 fn is_not_finite(self) -> bool {
88 (self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK
89 }
90
91 /// Returns true if `self` is infinity
92 fn is_infinity(self) -> bool {
93 (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK
94 }
95
96 /// Returns true if `self is NaN
97 fn is_nan(self) -> bool {
98 (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK
99 }
100
101 /// Returns true if `self` is negative
102 fn is_sign_negative(self) -> bool {
103 (self.repr() & Self::SIGN_MASK) != Self::Int::ZERO
104 }
105
106 /// Returns true if `self` is zero (either sign)
107 fn is_zero(self) -> bool {
108 (self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO
109 }
110}
111
112macro_rules! float_impl {
113 ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {
114 impl Float for $ty {
115 type Int = $ity;
116
117 const NAN: Self = <$ty>::NAN;
118
119 const BITS: u32 = $bits;
120 const SIGNIFICAND_BITS: u32 = $significand_bits;
121
122 const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1);
123 const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1;
124 const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK);
125
126 fn repr(self) -> Self::Int {
127 self.to_bits()
128 }
129
130 fn from_repr(a: Self::Int) -> Self {
131 Self::from_bits(a)
132 }
133
134 fn negate(self) -> Self {
135 -self
136 }
137 }
138 };
139}
140
141float_impl!(f32, u32, 32, 23);
142float_impl!(f64, u64, 64, 52);
143
144mod add_sub;
145mod cmp;
146mod conv;
147mod div;
148mod functions;
149mod mul;
diff --git a/embassy-rp/src/float/mul.rs b/embassy-rp/src/float/mul.rs
new file mode 100644
index 000000000..ceb0210e3
--- /dev/null
+++ b/embassy-rp/src/float/mul.rs
@@ -0,0 +1,70 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs
3
4use super::Float;
5use crate::rom_data;
6
7trait ROMMul {
8 fn rom_mul(self, b: Self) -> Self;
9}
10
11impl ROMMul for f32 {
12 fn rom_mul(self, b: Self) -> Self {
13 rom_data::float_funcs::fmul(self, b)
14 }
15}
16
17impl ROMMul for f64 {
18 fn rom_mul(self, b: Self) -> Self {
19 rom_data::double_funcs::dmul(self, b)
20 }
21}
22
23fn mul<F: Float + ROMMul>(a: F, b: F) -> F {
24 if a.is_not_finite() {
25 if b.is_zero() {
26 // [-]inf/NaN * 0 = NaN
27 return F::NAN;
28 }
29
30 return if b.is_sign_negative() {
31 // [+/-]inf/NaN * (-X) = [-/+]inf/NaN
32 a.negate()
33 } else {
34 // [-]inf/NaN * X = [-]inf/NaN
35 a
36 };
37 }
38
39 if b.is_not_finite() {
40 if a.is_zero() {
41 // 0 * [-]inf/NaN = NaN
42 return F::NAN;
43 }
44
45 return if b.is_sign_negative() {
46 // (-X) * [+/-]inf/NaN = [-/+]inf/NaN
47 b.negate()
48 } else {
49 // X * [-]inf/NaN = [-]inf/NaN
50 b
51 };
52 }
53
54 a.rom_mul(b)
55}
56
57intrinsics! {
58 #[alias = __mulsf3vfp]
59 #[aeabi = __aeabi_fmul]
60 extern "C" fn __mulsf3(a: f32, b: f32) -> f32 {
61 mul(a, b)
62 }
63
64 #[bootrom_v2]
65 #[alias = __muldf3vfp]
66 #[aeabi = __aeabi_dmul]
67 extern "C" fn __muldf3(a: f64, b: f64) -> f64 {
68 mul(a, b)
69 }
70}
diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs
index 3e75fb7fc..3b63846d4 100644
--- a/embassy-rp/src/intrinsics.rs
+++ b/embassy-rp/src/intrinsics.rs
@@ -274,3 +274,201 @@ macro_rules! intrinsics {
274 intrinsics!($($rest)*); 274 intrinsics!($($rest)*);
275 }; 275 };
276} 276}
277
278// Credit: taken from `rp-hal` (also licensed Apache+MIT)
279// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/sio.rs
280
281// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers
282// by packing it into r0[0:31] and r1[32:63]. So all we need to do is put
283// the remainder in the high order 32 bits of a 64 bit result. We can also
284// alias the division operators to these for a similar reason r0 is the
285// result either way and r1 a scratch register, so the caller can't assume it
286// retains the argument value.
287#[cfg(target_arch = "arm")]
288core::arch::global_asm!(
289 ".macro hwdivider_head",
290 "ldr r2, =(0xd0000000)", // SIO_BASE
291 // Check the DIRTY state of the divider by shifting it into the C
292 // status bit.
293 "ldr r3, [r2, #0x078]", // DIV_CSR
294 "lsrs r3, #2", // DIRTY = 1, so shift 2 down
295 // We only need to save the state when DIRTY, otherwise we can just do the
296 // division directly.
297 "bcs 2f",
298 "1:",
299 // Do the actual division now, we're either not DIRTY, or we've saved the
300 // state and branched back here so it's safe now.
301 ".endm",
302 ".macro hwdivider_tail",
303 // 8 cycle delay to wait for the result. Each branch takes two cycles
304 // and fits into a 2-byte Thumb instruction, so this is smaller than
305 // 8 NOPs.
306 "b 3f",
307 "3: b 3f",
308 "3: b 3f",
309 "3: b 3f",
310 "3:",
311 // Read the quotient last, since that's what clears the dirty flag.
312 "ldr r1, [r2, #0x074]", // DIV_REMAINDER
313 "ldr r0, [r2, #0x070]", // DIV_QUOTIENT
314 // Either return to the caller or back to the state restore.
315 "bx lr",
316 "2:",
317 // Since we can't save the signed-ness of the calculation, we have to make
318 // sure that there's at least an 8 cycle delay before we read the result.
319 // The push takes 5 cycles, and we've already spent at least 7 checking
320 // the DIRTY state to get here.
321 "push {{r4-r6, lr}}",
322 // Read the quotient last, since that's what clears the dirty flag.
323 "ldr r3, [r2, #0x060]", // DIV_UDIVIDEND
324 "ldr r4, [r2, #0x064]", // DIV_UDIVISOR
325 "ldr r5, [r2, #0x074]", // DIV_REMAINDER
326 "ldr r6, [r2, #0x070]", // DIV_QUOTIENT
327 // If we get interrupted here (before a write sets the DIRTY flag) it's
328 // fine, since we have the full state, so the interruptor doesn't have to
329 // restore it. Once the write happens and the DIRTY flag is set, the
330 // interruptor becomes responsible for restoring our state.
331 "bl 1b",
332 // If we are interrupted here, then the interruptor will start an incorrect
333 // calculation using a wrong divisor, but we'll restore the divisor and
334 // result ourselves correctly. This sets DIRTY, so any interruptor will
335 // save the state.
336 "str r3, [r2, #0x060]", // DIV_UDIVIDEND
337 // If we are interrupted here, the the interruptor may start the
338 // calculation using incorrectly signed inputs, but we'll restore the
339 // result ourselves. This sets DIRTY, so any interruptor will save the
340 // state.
341 "str r4, [r2, #0x064]", // DIV_UDIVISOR
342 // If we are interrupted here, the interruptor will have restored
343 // everything but the quotient may be wrongly signed. If the calculation
344 // started by the above writes is still ongoing it is stopped, so it won't
345 // replace the result we're restoring. DIRTY and READY set, but only
346 // DIRTY matters to make the interruptor save the state.
347 "str r5, [r2, #0x074]", // DIV_REMAINDER
348 // State fully restored after the quotient write. This sets both DIRTY
349 // and READY, so whatever we may have interrupted can read the result.
350 "str r6, [r2, #0x070]", // DIV_QUOTIENT
351 "pop {{r4-r6, pc}}",
352 ".endm",
353);
354
355macro_rules! division_function {
356 (
357 $name:ident $($intrinsic:ident)* ( $argty:ty ) {
358 $($begin:literal),+
359 }
360 ) => {
361 #[cfg(all(target_arch = "arm", feature = "intrinsics"))]
362 core::arch::global_asm!(
363 // Mangle the name slightly, since this is a global symbol.
364 concat!(".global _rphal_", stringify!($name)),
365 concat!(".type _rphal_", stringify!($name), ", %function"),
366 ".align 2",
367 concat!("_rphal_", stringify!($name), ":"),
368 $(
369 concat!(".global ", stringify!($intrinsic)),
370 concat!(".type ", stringify!($intrinsic), ", %function"),
371 concat!(stringify!($intrinsic), ":"),
372 )*
373
374 "hwdivider_head",
375 $($begin),+ ,
376 "hwdivider_tail",
377 );
378
379 #[cfg(all(target_arch = "arm", not(feature = "intrinsics")))]
380 core::arch::global_asm!(
381 // Mangle the name slightly, since this is a global symbol.
382 concat!(".global _rphal_", stringify!($name)),
383 concat!(".type _rphal_", stringify!($name), ", %function"),
384 ".align 2",
385 concat!("_rphal_", stringify!($name), ":"),
386
387 "hwdivider_head",
388 $($begin),+ ,
389 "hwdivider_tail",
390 );
391
392 #[cfg(target_arch = "arm")]
393 extern "aapcs" {
394 // Connect a local name to global symbol above through FFI.
395 #[link_name = concat!("_rphal_", stringify!($name)) ]
396 fn $name(n: $argty, d: $argty) -> u64;
397 }
398
399 #[cfg(not(target_arch = "arm"))]
400 #[allow(unused_variables)]
401 unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }
402 };
403}
404
405division_function! {
406 unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {
407 "str r0, [r2, #0x060]", // DIV_UDIVIDEND
408 "str r1, [r2, #0x064]" // DIV_UDIVISOR
409 }
410}
411
412division_function! {
413 signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {
414 "str r0, [r2, #0x068]", // DIV_SDIVIDEND
415 "str r1, [r2, #0x06c]" // DIV_SDIVISOR
416 }
417}
418
419fn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {
420 let packed = unsafe { unsigned_divmod(n, d) };
421 DivResult {
422 quotient: packed as u32,
423 remainder: (packed >> 32) as u32,
424 }
425}
426
427fn divider_signed(n: i32, d: i32) -> DivResult<i32> {
428 let packed = unsafe { signed_divmod(n, d) };
429 // Double casts to avoid sign extension
430 DivResult {
431 quotient: packed as u32 as i32,
432 remainder: (packed >> 32) as u32 as i32,
433 }
434}
435
436/// Result of divide/modulo operation
437struct DivResult<T> {
438 /// The quotient of divide/modulo operation
439 pub quotient: T,
440 /// The remainder of divide/modulo operation
441 pub remainder: T,
442}
443
444intrinsics! {
445 extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
446 divider_unsigned(n, d).quotient
447 }
448
449 extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
450 divider_unsigned(n, d).remainder
451 }
452
453 extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
454 let quo_rem = divider_unsigned(n, d);
455 if let Some(rem) = rem {
456 *rem = quo_rem.remainder;
457 }
458 quo_rem.quotient
459 }
460
461 extern "C" fn __divsi3(n: i32, d: i32) -> i32 {
462 divider_signed(n, d).quotient
463 }
464
465 extern "C" fn __modsi3(n: i32, d: i32) -> i32 {
466 divider_signed(n, d).remainder
467 }
468
469 extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {
470 let quo_rem = divider_signed(n, d);
471 *rem = quo_rem.remainder;
472 quo_rem.quotient
473 }
474}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 9e99b2fbb..3841bb83a 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -12,6 +12,7 @@ mod intrinsics;
12 12
13pub mod adc; 13pub mod adc;
14pub mod dma; 14pub mod dma;
15mod float;
15pub mod gpio; 16pub mod gpio;
16pub mod i2c; 17pub mod i2c;
17pub mod interrupt; 18pub mod interrupt;
@@ -44,9 +45,9 @@ pub use embassy_cortex_m::executor;
44pub use embassy_cortex_m::interrupt::_export::interrupt; 45pub use embassy_cortex_m::interrupt::_export::interrupt;
45pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 46pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
46#[cfg(feature = "unstable-pac")] 47#[cfg(feature = "unstable-pac")]
47pub use rp2040_pac2 as pac; 48pub use rp_pac as pac;
48#[cfg(not(feature = "unstable-pac"))] 49#[cfg(not(feature = "unstable-pac"))]
49pub(crate) use rp2040_pac2 as pac; 50pub(crate) use rp_pac as pac;
50 51
51embassy_hal_common::peripherals! { 52embassy_hal_common::peripherals! {
52 PIN_0, 53 PIN_0,
diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs
index 757a27114..805c1f09f 100644
--- a/embassy-rp/src/rom_data.rs
+++ b/embassy-rp/src/rom_data.rs
@@ -56,56 +56,32 @@ macro_rules! declare_rom_function {
56 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty 56 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
57 $lookup:block 57 $lookup:block
58 ) => { 58 ) => {
59 #[doc = r"Additional access for the `"] 59 declare_rom_function!{
60 #[doc = stringify!($name)] 60 __internal ,
61 #[doc = r"` ROM function."] 61 $(#[$outer])*
62 pub mod $name { 62 fn $name( $($argname: $ty),* ) -> $ret
63 /// Retrieve a function pointer. 63 $lookup
64 #[cfg(not(feature = "rom-func-cache"))]
65 pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
66 let p: *const u32 = $lookup;
67 unsafe {
68 let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
69 func
70 }
71 }
72
73 /// Retrieve a function pointer.
74 #[cfg(feature = "rom-func-cache")]
75 pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
76 use core::sync::atomic::{AtomicU16, Ordering};
77
78 // All pointers in the ROM fit in 16 bits, so we don't need a
79 // full width word to store the cached value.
80 static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
81 // This is safe because the lookup will always resolve
82 // to the same value. So even if an interrupt or another
83 // core starts at the same time, it just repeats some
84 // work and eventually writes back the correct value.
85 let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
86 0 => {
87 let raw: *const u32 = $lookup;
88 CACHED_PTR.store(raw as u16, Ordering::Relaxed);
89 raw
90 },
91 val => val as *const u32,
92 };
93 unsafe {
94 let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
95 func
96 }
97 }
98 } 64 }
65 };
99 66
100 $(#[$outer])* 67 (
101 pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { 68 $(#[$outer:meta])*
102 $name::ptr()($($argname),*) 69 unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
70 $lookup:block
71 ) => {
72 declare_rom_function!{
73 __internal unsafe ,
74 $(#[$outer])*
75 fn $name( $($argname: $ty),* ) -> $ret
76 $lookup
103 } 77 }
104 }; 78 };
105 79
106 ( 80 (
81 __internal
82 $( $maybe_unsafe:ident )? ,
107 $(#[$outer:meta])* 83 $(#[$outer:meta])*
108 unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty 84 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
109 $lookup:block 85 $lookup:block
110 ) => { 86 ) => {
111 #[doc = r"Additional access for the `"] 87 #[doc = r"Additional access for the `"]
@@ -114,43 +90,58 @@ macro_rules! declare_rom_function {
114 pub mod $name { 90 pub mod $name {
115 /// Retrieve a function pointer. 91 /// Retrieve a function pointer.
116 #[cfg(not(feature = "rom-func-cache"))] 92 #[cfg(not(feature = "rom-func-cache"))]
117 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { 93 pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret {
118 let p: *const u32 = $lookup; 94 let p: *const u32 = $lookup;
119 unsafe { 95 unsafe {
120 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); 96 let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret
97 = core::mem::transmute(p);
121 func 98 func
122 } 99 }
123 } 100 }
124 101
102 #[cfg(feature = "rom-func-cache")]
103 // unlike rp2040-hal we store a full word, containing the full function pointer.
104 // rp2040-hal saves two bytes by storing only the rom offset, at the cost of
105 // having to do an indirection and an atomic operation on every rom call.
106 static mut CACHE: $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret
107 = trampoline;
108
109 #[cfg(feature = "rom-func-cache")]
110 $( $maybe_unsafe )? extern "C" fn trampoline( $($argname: $ty),* ) -> $ret {
111 use core::sync::atomic::{compiler_fence, Ordering};
112
113 let p: *const u32 = $lookup;
114 #[allow(unused_unsafe)]
115 unsafe {
116 CACHE = core::mem::transmute(p);
117 compiler_fence(Ordering::Release);
118 CACHE($($argname),*)
119 }
120 }
121
125 /// Retrieve a function pointer. 122 /// Retrieve a function pointer.
126 #[cfg(feature = "rom-func-cache")] 123 #[cfg(feature = "rom-func-cache")]
127 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { 124 pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret {
128 use core::sync::atomic::{AtomicU16, Ordering}; 125 use core::sync::atomic::{compiler_fence, Ordering};
129 126
130 // All pointers in the ROM fit in 16 bits, so we don't need a
131 // full width word to store the cached value.
132 static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
133 // This is safe because the lookup will always resolve 127 // This is safe because the lookup will always resolve
134 // to the same value. So even if an interrupt or another 128 // to the same value. So even if an interrupt or another
135 // core starts at the same time, it just repeats some 129 // core starts at the same time, it just repeats some
136 // work and eventually writes back the correct value. 130 // work and eventually writes back the correct value.
137 let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { 131 //
138 0 => { 132 // We easily get away with using only compiler fences here
139 let raw: *const u32 = $lookup; 133 // because RP2040 SRAM is not cached. If it were we'd need
140 CACHED_PTR.store(raw as u16, Ordering::Relaxed); 134 // to make sure updates propagate quickly, or just take the
141 raw 135 // hit and let each core resolve every function once.
142 }, 136 compiler_fence(Ordering::Acquire);
143 val => val as *const u32,
144 };
145 unsafe { 137 unsafe {
146 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); 138 CACHE
147 func
148 } 139 }
149 } 140 }
150 } 141 }
151 142
152 $(#[$outer])* 143 $(#[$outer])*
153 pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { 144 pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret {
154 $name::ptr()($($argname),*) 145 $name::ptr()($($argname),*)
155 } 146 }
156 }; 147 };
@@ -369,6 +360,7 @@ pub fn fplib_start() -> *const u8 {
369} 360}
370 361
371/// See Table 180 in the RP2040 datasheet for the contents of this table. 362/// See Table 180 in the RP2040 datasheet for the contents of this table.
363#[cfg_attr(feature = "rom-func-cache", inline(never))]
372pub fn soft_float_table() -> *const usize { 364pub fn soft_float_table() -> *const usize {
373 rom_table_lookup(DATA_TABLE, *b"SF") 365 rom_table_lookup(DATA_TABLE, *b"SF")
374} 366}
@@ -379,6 +371,7 @@ pub fn fplib_end() -> *const u8 {
379} 371}
380 372
381/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. 373/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
374#[cfg_attr(feature = "rom-func-cache", inline(never))]
382pub fn soft_double_table() -> *const usize { 375pub fn soft_double_table() -> *const usize {
383 if rom_version_number() < 2 { 376 if rom_version_number() < 2 {
384 panic!( 377 panic!(
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs
index c620ed08c..cb0461930 100644
--- a/embassy-rp/src/uart/buffered.rs
+++ b/embassy-rp/src/uart/buffered.rs
@@ -726,58 +726,3 @@ mod eh1 {
726 } 726 }
727 } 727 }
728} 728}
729
730#[cfg(all(
731 feature = "unstable-traits",
732 feature = "nightly",
733 feature = "_todo_embedded_hal_serial"
734))]
735mod eha {
736 use core::future::Future;
737
738 use super::*;
739
740 impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> {
741 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
742
743 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
744 Self::write(buf)
745 }
746
747 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
748
749 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
750 Self::flush()
751 }
752 }
753
754 impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> {
755 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
756
757 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
758 Self::read(buf)
759 }
760 }
761
762 impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUart<'d, T> {
763 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
764
765 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
766 BufferedUartTx::<'d, T>::write(buf)
767 }
768
769 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
770
771 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
772 BufferedUartTx::<'d, T>::flush()
773 }
774 }
775
776 impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUart<'d, T> {
777 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
778
779 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
780 BufferedUartRx::<'d, T>::read(buf)
781 }
782 }
783}
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index a945f2295..dedc390f0 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -5,6 +5,7 @@ use embassy_hal_common::{into_ref, PeripheralRef};
5use crate::dma::{AnyChannel, Channel}; 5use crate::dma::{AnyChannel, Channel};
6use crate::gpio::sealed::Pin; 6use crate::gpio::sealed::Pin;
7use crate::gpio::AnyPin; 7use crate::gpio::AnyPin;
8use crate::pac::io::vals::{Inover, Outover};
8use crate::{pac, peripherals, Peripheral}; 9use crate::{pac, peripherals, Peripheral};
9 10
10#[cfg(feature = "nightly")] 11#[cfg(feature = "nightly")]
@@ -53,6 +54,14 @@ pub struct Config {
53 pub data_bits: DataBits, 54 pub data_bits: DataBits,
54 pub stop_bits: StopBits, 55 pub stop_bits: StopBits,
55 pub parity: Parity, 56 pub parity: Parity,
57 /// Invert the tx pin output
58 pub invert_tx: bool,
59 /// Invert the rx pin input
60 pub invert_rx: bool,
61 // Invert the rts pin
62 pub invert_rts: bool,
63 // Invert the cts pin
64 pub invert_cts: bool,
56} 65}
57 66
58impl Default for Config { 67impl Default for Config {
@@ -62,6 +71,10 @@ impl Default for Config {
62 data_bits: DataBits::DataBits8, 71 data_bits: DataBits::DataBits8,
63 stop_bits: StopBits::STOP1, 72 stop_bits: StopBits::STOP1,
64 parity: Parity::ParityNone, 73 parity: Parity::ParityNone,
74 invert_rx: false,
75 invert_tx: false,
76 invert_rts: false,
77 invert_cts: false,
65 } 78 }
66 } 79 }
67} 80}
@@ -167,7 +180,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> {
167} 180}
168 181
169impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { 182impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
170 /// Create a new DMA-enabled UART which can only send data 183 /// Create a new DMA-enabled UART which can only recieve data
171 pub fn new( 184 pub fn new(
172 _uart: impl Peripheral<P = T> + 'd, 185 _uart: impl Peripheral<P = T> + 'd,
173 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 186 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
@@ -175,7 +188,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
175 config: Config, 188 config: Config,
176 ) -> Self { 189 ) -> Self {
177 into_ref!(rx, rx_dma); 190 into_ref!(rx, rx_dma);
178 Uart::<T, M>::init(Some(rx.map_into()), None, None, None, config); 191 Uart::<T, M>::init(None, Some(rx.map_into()), None, None, config);
179 Self::new_inner(Some(rx_dma.map_into())) 192 Self::new_inner(Some(rx_dma.map_into()))
180 } 193 }
181 194
@@ -381,19 +394,47 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
381 let r = T::regs(); 394 let r = T::regs();
382 unsafe { 395 unsafe {
383 if let Some(pin) = &tx { 396 if let Some(pin) = &tx {
384 pin.io().ctrl().write(|w| w.set_funcsel(2)); 397 pin.io().ctrl().write(|w| {
398 w.set_funcsel(2);
399 w.set_outover(if config.invert_tx {
400 Outover::INVERT
401 } else {
402 Outover::NORMAL
403 });
404 });
385 pin.pad_ctrl().write(|w| w.set_ie(true)); 405 pin.pad_ctrl().write(|w| w.set_ie(true));
386 } 406 }
387 if let Some(pin) = &rx { 407 if let Some(pin) = &rx {
388 pin.io().ctrl().write(|w| w.set_funcsel(2)); 408 pin.io().ctrl().write(|w| {
409 w.set_funcsel(2);
410 w.set_inover(if config.invert_rx {
411 Inover::INVERT
412 } else {
413 Inover::NORMAL
414 });
415 });
389 pin.pad_ctrl().write(|w| w.set_ie(true)); 416 pin.pad_ctrl().write(|w| w.set_ie(true));
390 } 417 }
391 if let Some(pin) = &cts { 418 if let Some(pin) = &cts {
392 pin.io().ctrl().write(|w| w.set_funcsel(2)); 419 pin.io().ctrl().write(|w| {
420 w.set_funcsel(2);
421 w.set_inover(if config.invert_cts {
422 Inover::INVERT
423 } else {
424 Inover::NORMAL
425 });
426 });
393 pin.pad_ctrl().write(|w| w.set_ie(true)); 427 pin.pad_ctrl().write(|w| w.set_ie(true));
394 } 428 }
395 if let Some(pin) = &rts { 429 if let Some(pin) = &rts {
396 pin.io().ctrl().write(|w| w.set_funcsel(2)); 430 pin.io().ctrl().write(|w| {
431 w.set_funcsel(2);
432 w.set_outover(if config.invert_rts {
433 Outover::INVERT
434 } else {
435 Outover::NORMAL
436 });
437 });
397 pin.pad_ctrl().write(|w| w.set_ie(true)); 438 pin.pad_ctrl().write(|w| w.set_ie(true));
398 } 439 }
399 440
@@ -651,61 +692,6 @@ mod eh1 {
651 } 692 }
652} 693}
653 694
654#[cfg(all(
655 feature = "unstable-traits",
656 feature = "nightly",
657 feature = "_todo_embedded_hal_serial"
658))]
659mod eha {
660 use core::future::Future;
661
662 use super::*;
663
664 impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> {
665 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
666
667 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
668 self.write(buf)
669 }
670
671 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
672
673 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
674 async move { Ok(()) }
675 }
676 }
677
678 impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> {
679 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
680
681 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
682 self.read(buf)
683 }
684 }
685
686 impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> {
687 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
688
689 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
690 self.write(buf)
691 }
692
693 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
694
695 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
696 async move { Ok(()) }
697 }
698 }
699
700 impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> {
701 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
702
703 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
704 self.read(buf)
705 }
706 }
707}
708
709mod sealed { 695mod sealed {
710 use super::*; 696 use super::*;
711 697
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 969dc3b70..f27cf6a51 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -55,7 +55,7 @@ cortex-m = "0.7.6"
55futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 55futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
56rand_core = "0.6.3" 56rand_core = "0.6.3"
57sdio-host = "0.5.0" 57sdio-host = "0.5.0"
58embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } 58embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
59critical-section = "1.1" 59critical-section = "1.1"
60atomic-polyfill = "1.0.1" 60atomic-polyfill = "1.0.1"
61stm32-metapac = "6" 61stm32-metapac = "6"
@@ -66,6 +66,7 @@ stm32-fmc = "0.2.4"
66seq-macro = "0.3.0" 66seq-macro = "0.3.0"
67cfg-if = "1.0.0" 67cfg-if = "1.0.0"
68embedded-io = { version = "0.4.0", features = ["async"], optional = true } 68embedded-io = { version = "0.4.0", features = ["async"], optional = true }
69chrono = { version = "^0.4", default-features = false, optional = true}
69 70
70[dev-dependencies] 71[dev-dependencies]
71critical-section = { version = "1.1", features = ["std"] } 72critical-section = { version = "1.1", features = ["std"] }
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 73bd29fcf..a00c6c416 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -81,11 +81,74 @@ fn main() {
81 singletons.push(c.name.to_string()); 81 singletons.push(c.name.to_string());
82 } 82 }
83 83
84 // ========
85 // Handle time-driver-XXXX features.
86
87 let time_driver = match env::vars()
88 .map(|(a, _)| a)
89 .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
90 .get_one()
91 {
92 Ok(x) => Some(
93 x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
94 .unwrap()
95 .to_ascii_lowercase(),
96 ),
97 Err(GetOneError::None) => None,
98 Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
99 };
100
101 let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
102 None => "",
103 Some("tim2") => "TIM2",
104 Some("tim3") => "TIM3",
105 Some("tim4") => "TIM4",
106 Some("tim5") => "TIM5",
107 Some("tim12") => "TIM12",
108 Some("tim15") => "TIM15",
109 Some("any") => {
110 if singletons.contains(&"TIM2".to_string()) {
111 "TIM2"
112 } else if singletons.contains(&"TIM3".to_string()) {
113 "TIM3"
114 } else if singletons.contains(&"TIM4".to_string()) {
115 "TIM4"
116 } else if singletons.contains(&"TIM5".to_string()) {
117 "TIM5"
118 } else if singletons.contains(&"TIM12".to_string()) {
119 "TIM12"
120 } else if singletons.contains(&"TIM15".to_string()) {
121 "TIM15"
122 } else {
123 panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
124 }
125 }
126 _ => panic!("unknown time_driver {:?}", time_driver),
127 };
128
129 if time_driver_singleton != "" {
130 println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase());
131 }
132
133 // ========
134 // Write singletons
135
84 let mut g = TokenStream::new(); 136 let mut g = TokenStream::new();
85 137
86 let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); 138 let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
139
140 g.extend(quote! {
141 embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*);
142 });
143
144 let singleton_tokens: Vec<_> = singletons
145 .iter()
146 .filter(|s| *s != &time_driver_singleton.to_string())
147 .map(|s| format_ident!("{}", s))
148 .collect();
149
87 g.extend(quote! { 150 g.extend(quote! {
88 embassy_hal_common::peripherals!(#(#singleton_tokens),*); 151 embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*);
89 }); 152 });
90 153
91 // ======== 154 // ========
@@ -192,12 +255,20 @@ fn main() {
192 ]; 255 ];
193 }); 256 });
194 257
258 let max_erase_size = flash_memory_regions
259 .iter()
260 .map(|region| region.settings.as_ref().unwrap().erase_size)
261 .max()
262 .unwrap();
263
264 g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; });
265
195 g.extend(quote! { pub mod flash_regions { #flash_regions } }); 266 g.extend(quote! { pub mod flash_regions { #flash_regions } });
196 267
197 // ======== 268 // ========
198 // Generate DMA IRQs. 269 // Generate DMA IRQs.
199 270
200 let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new(); 271 let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new();
201 272
202 for p in METADATA.peripherals { 273 for p in METADATA.peripherals {
203 if let Some(r) = &p.registers { 274 if let Some(r) = &p.registers {
@@ -207,7 +278,10 @@ fn main() {
207 continue; 278 continue;
208 } 279 }
209 for irq in p.interrupts { 280 for irq in p.interrupts {
210 dma_irqs.entry(irq.interrupt).or_default().push((p.name, irq.signal)); 281 dma_irqs
282 .entry(irq.interrupt)
283 .or_default()
284 .push((r.kind, p.name, irq.signal));
211 } 285 }
212 } 286 }
213 } 287 }
@@ -216,13 +290,14 @@ fn main() {
216 for (irq, channels) in dma_irqs { 290 for (irq, channels) in dma_irqs {
217 let irq = format_ident!("{}", irq); 291 let irq = format_ident!("{}", irq);
218 292
219 let channels = channels.iter().map(|(dma, ch)| format_ident!("{}_{}", dma, ch)); 293 let xdma = format_ident!("{}", channels[0].0);
294 let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
220 295
221 g.extend(quote! { 296 g.extend(quote! {
222 #[crate::interrupt] 297 #[crate::interrupt]
223 unsafe fn #irq () { 298 unsafe fn #irq () {
224 #( 299 #(
225 <crate::peripherals::#channels as crate::dma::sealed::Channel>::on_irq(); 300 <crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
226 )* 301 )*
227 } 302 }
228 }); 303 });
@@ -838,51 +913,6 @@ fn main() {
838 println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x 913 println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
839 println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9 914 println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
840 915
841 // ========
842 // Handle time-driver-XXXX features.
843
844 let time_driver = match env::vars()
845 .map(|(a, _)| a)
846 .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
847 .get_one()
848 {
849 Ok(x) => Some(
850 x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
851 .unwrap()
852 .to_ascii_lowercase(),
853 ),
854 Err(GetOneError::None) => None,
855 Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
856 };
857
858 match time_driver.as_ref().map(|x| x.as_ref()) {
859 None => {}
860 Some("tim2") => println!("cargo:rustc-cfg=time_driver_tim2"),
861 Some("tim3") => println!("cargo:rustc-cfg=time_driver_tim3"),
862 Some("tim4") => println!("cargo:rustc-cfg=time_driver_tim4"),
863 Some("tim5") => println!("cargo:rustc-cfg=time_driver_tim5"),
864 Some("tim12") => println!("cargo:rustc-cfg=time_driver_tim12"),
865 Some("tim15") => println!("cargo:rustc-cfg=time_driver_tim15"),
866 Some("any") => {
867 if singletons.contains(&"TIM2".to_string()) {
868 println!("cargo:rustc-cfg=time_driver_tim2");
869 } else if singletons.contains(&"TIM3".to_string()) {
870 println!("cargo:rustc-cfg=time_driver_tim3");
871 } else if singletons.contains(&"TIM4".to_string()) {
872 println!("cargo:rustc-cfg=time_driver_tim4");
873 } else if singletons.contains(&"TIM5".to_string()) {
874 println!("cargo:rustc-cfg=time_driver_tim5");
875 } else if singletons.contains(&"TIM12".to_string()) {
876 println!("cargo:rustc-cfg=time_driver_tim12");
877 } else if singletons.contains(&"TIM15".to_string()) {
878 println!("cargo:rustc-cfg=time_driver_tim15");
879 } else {
880 panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
881 }
882 }
883 _ => panic!("unknown time_driver {:?}", time_driver),
884 }
885
886 // Handle time-driver-XXXX features. 916 // Handle time-driver-XXXX features.
887 if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {} 917 if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
888 println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); 918 println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs
index 20e1a4070..c19be86c6 100644
--- a/embassy-stm32/src/dcmi.rs
+++ b/embassy-stm32/src/dcmi.rs
@@ -4,6 +4,7 @@ use core::task::Poll;
4use embassy_hal_common::{into_ref, PeripheralRef}; 4use embassy_hal_common::{into_ref, PeripheralRef};
5use embassy_sync::waitqueue::AtomicWaker; 5use embassy_sync::waitqueue::AtomicWaker;
6 6
7use crate::dma::Transfer;
7use crate::gpio::sealed::AFType; 8use crate::gpio::sealed::AFType;
8use crate::gpio::Speed; 9use crate::gpio::Speed;
9use crate::interrupt::{Interrupt, InterruptExt}; 10use crate::interrupt::{Interrupt, InterruptExt};
@@ -385,14 +386,11 @@ where
385 return self.capture_giant(buffer).await; 386 return self.capture_giant(buffer).await;
386 } 387 }
387 } 388 }
388
389 async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> { 389 async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
390 let channel = &mut self.dma;
391 let request = channel.request();
392
393 let r = self.inner.regs(); 390 let r = self.inner.regs();
394 let src = r.dr().ptr() as *mut u32; 391 let src = r.dr().ptr() as *mut u32;
395 let dma_read = crate::dma::read(channel, request, src, buffer); 392 let request = self.dma.request();
393 let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) };
396 394
397 Self::clear_interrupt_flags(); 395 Self::clear_interrupt_flags();
398 Self::enable_irqs(); 396 Self::enable_irqs();
@@ -436,6 +434,12 @@ where
436 result 434 result
437 } 435 }
438 436
437 #[cfg(not(dma))]
438 async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> {
439 panic!("capturing to buffers larger than 0xffff is only supported on DMA for now, not on BDMA or GPDMA.");
440 }
441
442 #[cfg(dma)]
439 async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> { 443 async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
440 use crate::dma::TransferOptions; 444 use crate::dma::TransferOptions;
441 445
@@ -460,16 +464,24 @@ where
460 let r = self.inner.regs(); 464 let r = self.inner.regs();
461 let src = r.dr().ptr() as *mut u32; 465 let src = r.dr().ptr() as *mut u32;
462 466
463 unsafe { 467 let mut transfer = unsafe {
464 channel.start_double_buffered_read(request, src, m0ar, m1ar, chunk_size, TransferOptions::default()); 468 crate::dma::DoubleBuffered::new_read(
465 } 469 &mut self.dma,
470 request,
471 src,
472 m0ar,
473 m1ar,
474 chunk_size,
475 TransferOptions::default(),
476 )
477 };
466 478
467 let mut last_chunk_set_for_transfer = false; 479 let mut last_chunk_set_for_transfer = false;
468 let mut buffer0_last_accessible = false; 480 let mut buffer0_last_accessible = false;
469 let dma_result = poll_fn(|cx| { 481 let dma_result = poll_fn(|cx| {
470 channel.set_waker(cx.waker()); 482 transfer.set_waker(cx.waker());
471 483
472 let buffer0_currently_accessible = unsafe { channel.is_buffer0_accessible() }; 484 let buffer0_currently_accessible = transfer.is_buffer0_accessible();
473 485
474 // check if the accessible buffer changed since last poll 486 // check if the accessible buffer changed since last poll
475 if buffer0_last_accessible == buffer0_currently_accessible { 487 if buffer0_last_accessible == buffer0_currently_accessible {
@@ -480,21 +492,21 @@ where
480 if remaining_chunks != 0 { 492 if remaining_chunks != 0 {
481 if remaining_chunks % 2 == 0 && buffer0_currently_accessible { 493 if remaining_chunks % 2 == 0 && buffer0_currently_accessible {
482 m0ar = unsafe { m0ar.add(2 * chunk_size) }; 494 m0ar = unsafe { m0ar.add(2 * chunk_size) };
483 unsafe { channel.set_buffer0(m0ar) } 495 unsafe { transfer.set_buffer0(m0ar) }
484 remaining_chunks -= 1; 496 remaining_chunks -= 1;
485 } else if !buffer0_currently_accessible { 497 } else if !buffer0_currently_accessible {
486 m1ar = unsafe { m1ar.add(2 * chunk_size) }; 498 m1ar = unsafe { m1ar.add(2 * chunk_size) };
487 unsafe { channel.set_buffer1(m1ar) }; 499 unsafe { transfer.set_buffer1(m1ar) };
488 remaining_chunks -= 1; 500 remaining_chunks -= 1;
489 } 501 }
490 } else { 502 } else {
491 if buffer0_currently_accessible { 503 if buffer0_currently_accessible {
492 unsafe { channel.set_buffer0(buffer.as_mut_ptr()) } 504 unsafe { transfer.set_buffer0(buffer.as_mut_ptr()) }
493 } else { 505 } else {
494 unsafe { channel.set_buffer1(buffer.as_mut_ptr()) } 506 unsafe { transfer.set_buffer1(buffer.as_mut_ptr()) }
495 } 507 }
496 if last_chunk_set_for_transfer { 508 if last_chunk_set_for_transfer {
497 channel.request_stop(); 509 transfer.request_stop();
498 return Poll::Ready(()); 510 return Poll::Ready(());
499 } 511 }
500 last_chunk_set_for_transfer = true; 512 last_chunk_set_for_transfer = true;
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs
index 5a7a408bb..a23bb8cd7 100644
--- a/embassy-stm32/src/dma/bdma.rs
+++ b/embassy-stm32/src/dma/bdma.rs
@@ -1,18 +1,32 @@
1#![macro_use] 1#![macro_use]
2 2
3use core::future::Future;
4use core::pin::Pin;
3use core::sync::atomic::{fence, Ordering}; 5use core::sync::atomic::{fence, Ordering};
4use core::task::Waker; 6use core::task::{Context, Poll};
5 7
6use embassy_cortex_m::interrupt::Priority; 8use embassy_cortex_m::interrupt::Priority;
9use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
7use embassy_sync::waitqueue::AtomicWaker; 10use embassy_sync::waitqueue::AtomicWaker;
8 11
9use super::{TransferOptions, Word, WordSize}; 12use super::word::{Word, WordSize};
13use super::Dir;
10use crate::_generated::BDMA_CHANNEL_COUNT; 14use crate::_generated::BDMA_CHANNEL_COUNT;
11use crate::dma::Request;
12use crate::interrupt::{Interrupt, InterruptExt}; 15use crate::interrupt::{Interrupt, InterruptExt};
13use crate::pac; 16use crate::pac;
14use crate::pac::bdma::vals; 17use crate::pac::bdma::vals;
15 18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub struct TransferOptions {}
23
24impl Default for TransferOptions {
25 fn default() -> Self {
26 Self {}
27 }
28}
29
16impl From<WordSize> for vals::Size { 30impl From<WordSize> for vals::Size {
17 fn from(raw: WordSize) -> Self { 31 fn from(raw: WordSize) -> Self {
18 match raw { 32 match raw {
@@ -23,6 +37,15 @@ impl From<WordSize> for vals::Size {
23 } 37 }
24} 38}
25 39
40impl From<Dir> for vals::Dir {
41 fn from(raw: Dir) -> Self {
42 match raw {
43 Dir::MemoryToPeripheral => Self::FROMMEMORY,
44 Dir::PeripheralToMemory => Self::FROMPERIPHERAL,
45 }
46 }
47}
48
26struct State { 49struct State {
27 ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT], 50 ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT],
28} 51}
@@ -55,228 +78,267 @@ foreach_dma_channel! {
55 // BDMA1 in H7 doesn't use DMAMUX, which breaks 78 // BDMA1 in H7 doesn't use DMAMUX, which breaks
56 }; 79 };
57 ($channel_peri:ident, $dma_peri:ident, bdma, $channel_num:expr, $index:expr, $dmamux:tt) => { 80 ($channel_peri:ident, $dma_peri:ident, bdma, $channel_num:expr, $index:expr, $dmamux:tt) => {
58 impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { 81 impl sealed::Channel for crate::peripherals::$channel_peri {
59 82 fn regs(&self) -> pac::bdma::Dma {
60 unsafe fn start_write<W: Word>(&mut self, _request: Request, buf: *const[W], reg_addr: *mut W, options: TransferOptions) { 83 pac::$dma_peri
61 let (ptr, len) = super::slice_ptr_parts(buf);
62 low_level_api::start_transfer(
63 pac::$dma_peri,
64 $channel_num,
65 #[cfg(any(bdma_v2, dmamux))]
66 _request,
67 vals::Dir::FROMMEMORY,
68 reg_addr as *const u32,
69 ptr as *mut u32,
70 len,
71 true,
72 vals::Size::from(W::bits()),
73 options,
74 #[cfg(dmamux)]
75 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
76 #[cfg(dmamux)]
77 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
78 );
79 } 84 }
80 85 fn num(&self) -> usize {
81 unsafe fn start_write_repeated<W: Word>(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { 86 $channel_num
82 low_level_api::start_transfer(
83 pac::$dma_peri,
84 $channel_num,
85 #[cfg(any(bdma_v2, dmamux))]
86 _request,
87 vals::Dir::FROMMEMORY,
88 reg_addr as *const u32,
89 repeated as *mut u32,
90 count,
91 false,
92 vals::Size::from(W::bits()),
93 options,
94 #[cfg(dmamux)]
95 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
96 #[cfg(dmamux)]
97 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
98 )
99 } 87 }
100 88 fn index(&self) -> usize {
101 unsafe fn start_read<W: Word>(&mut self, _request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { 89 $index
102 let (ptr, len) = super::slice_ptr_parts_mut(buf);
103 low_level_api::start_transfer(
104 pac::$dma_peri,
105 $channel_num,
106 #[cfg(any(bdma_v2, dmamux))]
107 _request,
108 vals::Dir::FROMPERIPHERAL,
109 reg_addr as *const u32,
110 ptr as *mut u32,
111 len,
112 true,
113 vals::Size::from(W::bits()),
114 options,
115 #[cfg(dmamux)]
116 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
117 #[cfg(dmamux)]
118 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
119 );
120 } 90 }
121 91 fn on_irq() {
122 unsafe fn start_double_buffered_read<W: super::Word>( 92 unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
123 &mut self,
124 _request: Request,
125 _reg_addr: *const W,
126 _buffer0: *mut W,
127 _buffer1: *mut W,
128 _buffer_len: usize,
129 _options: TransferOptions,
130 ) {
131 panic!("Unsafe double buffered mode is unavailable on BDMA");
132 } 93 }
94 }
133 95
134 unsafe fn set_buffer0<W: super::Word>(&mut self, _buffer: *mut W) { 96 impl Channel for crate::peripherals::$channel_peri {}
135 panic!("Unsafe double buffered mode is unavailable on BDMA"); 97 };
136 } 98}
137 99
138 unsafe fn set_buffer1<W: super::Word>(&mut self, _buffer: *mut W) { 100/// Safety: Must be called with a matching set of parameters for a valid dma channel
139 panic!("Unsafe double buffered mode is unavailable on BDMA"); 101pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index: usize) {
140 } 102 let isr = dma.isr().read();
103 let cr = dma.ch(channel_num).cr();
141 104
142 unsafe fn is_buffer0_accessible(&mut self) -> bool { 105 if isr.teif(channel_num) {
143 panic!("Unsafe double buffered mode is unavailable on BDMA"); 106 panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num);
144 } 107 }
108 if isr.tcif(channel_num) && cr.read().tcie() {
109 cr.write(|_| ()); // Disable channel interrupts with the default value.
110 STATE.ch_wakers[index].wake();
111 }
112}
145 113
146 fn request_stop(&mut self){ 114#[cfg(any(bdma_v2, dmamux))]
147 unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} 115pub type Request = u8;
148 } 116#[cfg(not(any(bdma_v2, dmamux)))]
117pub type Request = ();
149 118
150 fn is_running(&self) -> bool { 119#[cfg(dmamux)]
151 unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} 120pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
152 } 121#[cfg(not(dmamux))]
153 fn remaining_transfers(&mut self) -> u16 { 122pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
154 unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)}
155 }
156 123
157 fn set_waker(&mut self, waker: &Waker) { 124pub(crate) mod sealed {
158 unsafe { low_level_api::set_waker($index, waker) } 125 use super::*;
159 }
160 126
161 fn on_irq() { 127 pub trait Channel {
162 unsafe { 128 fn regs(&self) -> pac::bdma::Dma;
163 low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); 129 fn num(&self) -> usize;
164 } 130 fn index(&self) -> usize;
165 } 131 fn on_irq();
166 } 132 }
133}
167 134
168 impl crate::dma::Channel for crate::peripherals::$channel_peri {} 135#[must_use = "futures do nothing unless you `.await` or poll them"]
169 }; 136pub struct Transfer<'a, C: Channel> {
137 channel: PeripheralRef<'a, C>,
170} 138}
171 139
172mod low_level_api { 140impl<'a, C: Channel> Transfer<'a, C> {
173 use super::*; 141 pub unsafe fn new_read<W: Word>(
142 channel: impl Peripheral<P = C> + 'a,
143 request: Request,
144 peri_addr: *mut W,
145 buf: &'a mut [W],
146 options: TransferOptions,
147 ) -> Self {
148 Self::new_read_raw(channel, request, peri_addr, buf, options)
149 }
174 150
175 pub unsafe fn start_transfer( 151 pub unsafe fn new_read_raw<W: Word>(
176 dma: pac::bdma::Dma, 152 channel: impl Peripheral<P = C> + 'a,
177 channel_number: u8, 153 request: Request,
178 #[cfg(any(bdma_v2, dmamux))] request: Request, 154 peri_addr: *mut W,
179 dir: vals::Dir, 155 buf: *mut [W],
180 peri_addr: *const u32,
181 mem_addr: *mut u32,
182 mem_len: usize,
183 incr_mem: bool,
184 data_size: vals::Size,
185 options: TransferOptions, 156 options: TransferOptions,
186 #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, 157 ) -> Self {
187 #[cfg(dmamux)] dmamux_ch_num: u8, 158 into_ref!(channel);
188 ) { 159
189 assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); 160 let (ptr, len) = super::slice_ptr_parts_mut(buf);
190 assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); 161 assert!(len > 0 && len <= 0xFFFF);
191 assert!( 162
192 options.flow_ctrl == crate::dma::FlowControl::Dma, 163 Self::new_inner(
193 "Peripheral flow control not supported" 164 channel,
194 ); 165 request,
195 assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); 166 Dir::PeripheralToMemory,
167 peri_addr as *const u32,
168 ptr as *mut u32,
169 len,
170 true,
171 W::size(),
172 options,
173 )
174 }
196 175
197 let ch = dma.ch(channel_number as _); 176 pub unsafe fn new_write<W: Word>(
177 channel: impl Peripheral<P = C> + 'a,
178 request: Request,
179 buf: &'a [W],
180 peri_addr: *mut W,
181 options: TransferOptions,
182 ) -> Self {
183 Self::new_write_raw(channel, request, buf, peri_addr, options)
184 }
198 185
199 reset_status(dma, channel_number); 186 pub unsafe fn new_write_raw<W: Word>(
187 channel: impl Peripheral<P = C> + 'a,
188 request: Request,
189 buf: *const [W],
190 peri_addr: *mut W,
191 options: TransferOptions,
192 ) -> Self {
193 into_ref!(channel);
194
195 let (ptr, len) = super::slice_ptr_parts(buf);
196 assert!(len > 0 && len <= 0xFFFF);
197
198 Self::new_inner(
199 channel,
200 request,
201 Dir::MemoryToPeripheral,
202 peri_addr as *const u32,
203 ptr as *mut u32,
204 len,
205 true,
206 W::size(),
207 options,
208 )
209 }
200 210
201 #[cfg(dmamux)] 211 pub unsafe fn new_write_repeated<W: Word>(
202 super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); 212 channel: impl Peripheral<P = C> + 'a,
213 request: Request,
214 repeated: &'a W,
215 count: usize,
216 peri_addr: *mut W,
217 options: TransferOptions,
218 ) -> Self {
219 into_ref!(channel);
220
221 Self::new_inner(
222 channel,
223 request,
224 Dir::MemoryToPeripheral,
225 peri_addr as *const u32,
226 repeated as *const W as *mut u32,
227 count,
228 false,
229 W::size(),
230 options,
231 )
232 }
203 233
204 #[cfg(bdma_v2)] 234 unsafe fn new_inner(
205 critical_section::with(|_| dma.cselr().modify(|w| w.set_cs(channel_number as _, request))); 235 channel: PeripheralRef<'a, C>,
236 _request: Request,
237 dir: Dir,
238 peri_addr: *const u32,
239 mem_addr: *mut u32,
240 mem_len: usize,
241 incr_mem: bool,
242 data_size: WordSize,
243 _options: TransferOptions,
244 ) -> Self {
245 let ch = channel.regs().ch(channel.num());
206 246
207 // "Preceding reads and writes cannot be moved past subsequent writes." 247 // "Preceding reads and writes cannot be moved past subsequent writes."
208 fence(Ordering::SeqCst); 248 fence(Ordering::SeqCst);
209 249
250 #[cfg(bdma_v2)]
251 critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request)));
252
253 let mut this = Self { channel };
254 this.clear_irqs();
255
256 #[cfg(dmamux)]
257 super::dmamux::configure_dmamux(&mut *this.channel, _request);
258
210 ch.par().write_value(peri_addr as u32); 259 ch.par().write_value(peri_addr as u32);
211 ch.mar().write_value(mem_addr as u32); 260 ch.mar().write_value(mem_addr as u32);
212 ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); 261 ch.ndtr().write(|w| w.set_ndt(mem_len as u16));
213 ch.cr().write(|w| { 262 ch.cr().write(|w| {
214 w.set_psize(data_size); 263 w.set_psize(data_size.into());
215 w.set_msize(data_size); 264 w.set_msize(data_size.into());
216 if incr_mem { 265 if incr_mem {
217 w.set_minc(vals::Inc::ENABLED); 266 w.set_minc(vals::Inc::ENABLED);
218 } else { 267 } else {
219 w.set_minc(vals::Inc::DISABLED); 268 w.set_minc(vals::Inc::DISABLED);
220 } 269 }
221 w.set_dir(dir); 270 w.set_dir(dir.into());
222 w.set_teie(true); 271 w.set_teie(true);
223 w.set_tcie(true); 272 w.set_tcie(true);
224 w.set_en(true); 273 w.set_en(true);
225 }); 274 });
226 }
227 275
228 pub unsafe fn request_stop(dma: pac::bdma::Dma, channel_number: u8) { 276 this
229 reset_status(dma, channel_number); 277 }
230 278
231 let ch = dma.ch(channel_number as _); 279 fn clear_irqs(&mut self) {
280 unsafe {
281 self.channel.regs().ifcr().write(|w| {
282 w.set_tcif(self.channel.num(), true);
283 w.set_teif(self.channel.num(), true);
284 })
285 }
286 }
232 287
233 // Disable the channel and interrupts with the default value. 288 pub fn request_stop(&mut self) {
234 ch.cr().write(|_| ()); 289 let ch = self.channel.regs().ch(self.channel.num());
235 290
236 // "Subsequent reads and writes cannot be moved ahead of preceding reads." 291 // Disable the channel. Keep the IEs enabled so the irqs still fire.
237 fence(Ordering::SeqCst); 292 unsafe {
293 ch.cr().write(|w| {
294 w.set_teie(true);
295 w.set_tcie(true);
296 })
297 }
238 } 298 }
239 299
240 pub unsafe fn is_running(dma: pac::bdma::Dma, ch: u8) -> bool { 300 pub fn is_running(&mut self) -> bool {
241 let ch = dma.ch(ch as _); 301 let ch = self.channel.regs().ch(self.channel.num());
242 ch.cr().read().en() 302 unsafe { ch.cr().read() }.en()
243 } 303 }
244 304
245 /// Gets the total remaining transfers for the channel 305 /// Gets the total remaining transfers for the channel
246 /// Note: this will be zero for transfers that completed without cancellation. 306 /// Note: this will be zero for transfers that completed without cancellation.
247 pub unsafe fn get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 { 307 pub fn get_remaining_transfers(&self) -> u16 {
248 // get a handle on the channel itself 308 let ch = self.channel.regs().ch(self.channel.num());
249 let ch = dma.ch(ch as _); 309 unsafe { ch.ndtr().read() }.ndt()
250 // read the remaining transfer count. If this is zero, the transfer completed fully.
251 ch.ndtr().read().ndt() as u16
252 } 310 }
253 311
254 /// Sets the waker for the specified DMA channel 312 pub fn blocking_wait(mut self) {
255 pub unsafe fn set_waker(state_number: usize, waker: &Waker) { 313 while self.is_running() {}
256 STATE.ch_wakers[state_number].register(waker);
257 }
258 314
259 pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) { 315 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
260 dma.ifcr().write(|w| { 316 fence(Ordering::SeqCst);
261 w.set_tcif(channel_number as _, true); 317
262 w.set_teif(channel_number as _, true); 318 core::mem::forget(self);
263 });
264 } 319 }
320}
265 321
266 /// Safety: Must be called with a matching set of parameters for a valid dma channel 322impl<'a, C: Channel> Drop for Transfer<'a, C> {
267 pub unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: u8, index: u8) { 323 fn drop(&mut self) {
268 let channel_num = channel_num as usize; 324 self.request_stop();
269 let index = index as usize; 325 while self.is_running() {}
270 326
271 let isr = dma.isr().read(); 327 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
272 let cr = dma.ch(channel_num).cr(); 328 fence(Ordering::SeqCst);
329 }
330}
273 331
274 if isr.teif(channel_num) { 332impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
275 panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); 333impl<'a, C: Channel> Future for Transfer<'a, C> {
276 } 334 type Output = ();
277 if isr.tcif(channel_num) && cr.read().tcie() { 335 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
278 cr.write(|_| ()); // Disable channel interrupts with the default value. 336 STATE.ch_wakers[self.channel.index()].register(cx.waker());
279 STATE.ch_wakers[index].wake(); 337
338 if self.is_running() {
339 Poll::Pending
340 } else {
341 Poll::Ready(())
280 } 342 }
281 } 343 }
282} 344}
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs
index 59937f4b0..ef1d27573 100644
--- a/embassy-stm32/src/dma/dma.rs
+++ b/embassy-stm32/src/dma/dma.rs
@@ -1,15 +1,46 @@
1use core::future::Future;
2use core::marker::PhantomData;
3use core::pin::Pin;
1use core::sync::atomic::{fence, Ordering}; 4use core::sync::atomic::{fence, Ordering};
2use core::task::Waker; 5use core::task::{Context, Poll, Waker};
3 6
4use embassy_cortex_m::interrupt::Priority; 7use embassy_cortex_m::interrupt::Priority;
8use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
5use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10use pac::dma::regs;
6 11
7use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; 12use super::word::{Word, WordSize};
13use super::Dir;
8use crate::_generated::DMA_CHANNEL_COUNT; 14use crate::_generated::DMA_CHANNEL_COUNT;
9use crate::interrupt::{Interrupt, InterruptExt}; 15use crate::interrupt::{Interrupt, InterruptExt};
10use crate::pac::dma::{regs, vals}; 16use crate::pac::dma::vals;
11use crate::{interrupt, pac}; 17use crate::{interrupt, pac};
12 18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub struct TransferOptions {
23 /// Peripheral burst transfer configuration
24 pub pburst: Burst,
25 /// Memory burst transfer configuration
26 pub mburst: Burst,
27 /// Flow control configuration
28 pub flow_ctrl: FlowControl,
29 /// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
30 pub fifo_threshold: Option<FifoThreshold>,
31}
32
33impl Default for TransferOptions {
34 fn default() -> Self {
35 Self {
36 pburst: Burst::Single,
37 mburst: Burst::Single,
38 flow_ctrl: FlowControl::Dma,
39 fifo_threshold: None,
40 }
41 }
42}
43
13impl From<WordSize> for vals::Size { 44impl From<WordSize> for vals::Size {
14 fn from(raw: WordSize) -> Self { 45 fn from(raw: WordSize) -> Self {
15 match raw { 46 match raw {
@@ -20,6 +51,28 @@ impl From<WordSize> for vals::Size {
20 } 51 }
21} 52}
22 53
54impl From<Dir> for vals::Dir {
55 fn from(raw: Dir) -> Self {
56 match raw {
57 Dir::MemoryToPeripheral => Self::MEMORYTOPERIPHERAL,
58 Dir::PeripheralToMemory => Self::PERIPHERALTOMEMORY,
59 }
60 }
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Eq)]
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65pub enum Burst {
66 /// Single transfer
67 Single,
68 /// Incremental burst of 4 beats
69 Incr4,
70 /// Incremental burst of 8 beats
71 Incr8,
72 /// Incremental burst of 16 beats
73 Incr16,
74}
75
23impl From<Burst> for vals::Burst { 76impl From<Burst> for vals::Burst {
24 fn from(burst: Burst) -> Self { 77 fn from(burst: Burst) -> Self {
25 match burst { 78 match burst {
@@ -31,6 +84,15 @@ impl From<Burst> for vals::Burst {
31 } 84 }
32} 85}
33 86
87#[derive(Debug, Copy, Clone, PartialEq, Eq)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub enum FlowControl {
90 /// Flow control by DMA
91 Dma,
92 /// Flow control by peripheral
93 Peripheral,
94}
95
34impl From<FlowControl> for vals::Pfctrl { 96impl From<FlowControl> for vals::Pfctrl {
35 fn from(flow: FlowControl) -> Self { 97 fn from(flow: FlowControl) -> Self {
36 match flow { 98 match flow {
@@ -40,6 +102,19 @@ impl From<FlowControl> for vals::Pfctrl {
40 } 102 }
41} 103}
42 104
105#[derive(Debug, Copy, Clone, PartialEq, Eq)]
106#[cfg_attr(feature = "defmt", derive(defmt::Format))]
107pub enum FifoThreshold {
108 /// 1/4 full FIFO
109 Quarter,
110 /// 1/2 full FIFO
111 Half,
112 /// 3/4 full FIFO
113 ThreeQuarters,
114 /// Full FIFO
115 Full,
116}
117
43impl From<FifoThreshold> for vals::Fth { 118impl From<FifoThreshold> for vals::Fth {
44 fn from(value: FifoThreshold) -> Self { 119 fn from(value: FifoThreshold) -> Self {
45 match value { 120 match value {
@@ -51,27 +126,15 @@ impl From<FifoThreshold> for vals::Fth {
51 } 126 }
52} 127}
53 128
54struct ChannelState {
55 waker: AtomicWaker,
56}
57
58impl ChannelState {
59 const fn new() -> Self {
60 Self {
61 waker: AtomicWaker::new(),
62 }
63 }
64}
65
66struct State { 129struct State {
67 channels: [ChannelState; DMA_CHANNEL_COUNT], 130 ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT],
68} 131}
69 132
70impl State { 133impl State {
71 const fn new() -> Self { 134 const fn new() -> Self {
72 const CH: ChannelState = ChannelState::new(); 135 const AW: AtomicWaker = AtomicWaker::new();
73 Self { 136 Self {
74 channels: [CH; DMA_CHANNEL_COUNT], 137 ch_wakers: [AW; DMA_CHANNEL_COUNT],
75 } 138 }
76 } 139 }
77} 140}
@@ -92,158 +155,183 @@ pub(crate) unsafe fn init(irq_priority: Priority) {
92 155
93foreach_dma_channel! { 156foreach_dma_channel! {
94 ($channel_peri:ident, $dma_peri:ident, dma, $channel_num:expr, $index:expr, $dmamux:tt) => { 157 ($channel_peri:ident, $dma_peri:ident, dma, $channel_num:expr, $index:expr, $dmamux:tt) => {
95 impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { 158 impl sealed::Channel for crate::peripherals::$channel_peri {
96 unsafe fn start_write<W: Word>(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { 159 fn regs(&self) -> pac::dma::Dma {
97 let (ptr, len) = super::slice_ptr_parts(buf); 160 pac::$dma_peri
98 low_level_api::start_transfer(
99 pac::$dma_peri,
100 $channel_num,
101 request,
102 vals::Dir::MEMORYTOPERIPHERAL,
103 reg_addr as *const u32,
104 ptr as *mut u32,
105 len,
106 true,
107 vals::Size::from(W::bits()),
108 options,
109 #[cfg(dmamux)]
110 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
111 #[cfg(dmamux)]
112 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
113 )
114 } 161 }
115 162 fn num(&self) -> usize {
116 unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { 163 $channel_num
117 low_level_api::start_transfer(
118 pac::$dma_peri,
119 $channel_num,
120 request,
121 vals::Dir::MEMORYTOPERIPHERAL,
122 reg_addr as *const u32,
123 repeated as *mut u32,
124 count,
125 false,
126 vals::Size::from(W::bits()),
127 options,
128 #[cfg(dmamux)]
129 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
130 #[cfg(dmamux)]
131 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
132 )
133 } 164 }
134 165 fn index(&self) -> usize {
135 unsafe fn start_read<W: Word>(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { 166 $index
136 let (ptr, len) = super::slice_ptr_parts_mut(buf);
137 low_level_api::start_transfer(
138 pac::$dma_peri,
139 $channel_num,
140 request,
141 vals::Dir::PERIPHERALTOMEMORY,
142 reg_addr as *const u32,
143 ptr as *mut u32,
144 len,
145 true,
146 vals::Size::from(W::bits()),
147 options,
148 #[cfg(dmamux)]
149 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
150 #[cfg(dmamux)]
151 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
152 );
153 } 167 }
154 168 fn on_irq() {
155 unsafe fn start_double_buffered_read<W: Word>( 169 unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
156 &mut self,
157 request: Request,
158 reg_addr: *const W,
159 buffer0: *mut W,
160 buffer1: *mut W,
161 buffer_len: usize,
162 options: TransferOptions,
163 ) {
164 low_level_api::start_dbm_transfer(
165 pac::$dma_peri,
166 $channel_num,
167 request,
168 vals::Dir::PERIPHERALTOMEMORY,
169 reg_addr as *const u32,
170 buffer0 as *mut u32,
171 buffer1 as *mut u32,
172 buffer_len,
173 true,
174 vals::Size::from(W::bits()),
175 options,
176 #[cfg(dmamux)]
177 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
178 #[cfg(dmamux)]
179 <Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
180 );
181 } 170 }
171 }
182 172
183 unsafe fn set_buffer0<W: Word>(&mut self, buffer: *mut W) { 173 impl Channel for crate::peripherals::$channel_peri {}
184 low_level_api::set_dbm_buffer0(pac::$dma_peri, $channel_num, buffer as *mut u32); 174 };
185 } 175}
186 176
187 unsafe fn set_buffer1<W: Word>(&mut self, buffer: *mut W) { 177/// Safety: Must be called with a matching set of parameters for a valid dma channel
188 low_level_api::set_dbm_buffer1(pac::$dma_peri, $channel_num, buffer as *mut u32); 178pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: usize) {
189 } 179 let cr = dma.st(channel_num).cr();
180 let isr = dma.isr(channel_num / 4).read();
190 181
191 unsafe fn is_buffer0_accessible(&mut self) -> bool { 182 if isr.teif(channel_num % 4) {
192 low_level_api::is_buffer0_accessible(pac::$dma_peri, $channel_num) 183 panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
193 } 184 }
194 185
195 fn request_stop(&mut self) { 186 if isr.tcif(channel_num % 4) && cr.read().tcie() {
196 unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} 187 /* acknowledge transfer complete interrupt */
197 } 188 dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
189 STATE.ch_wakers[index].wake();
190 }
191}
198 192
199 fn is_running(&self) -> bool { 193#[cfg(any(dma_v2, dmamux))]
200 unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} 194pub type Request = u8;
201 } 195#[cfg(not(any(dma_v2, dmamux)))]
196pub type Request = ();
202 197
203 fn remaining_transfers(&mut self) -> u16 { 198#[cfg(dmamux)]
204 unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} 199pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
205 } 200#[cfg(not(dmamux))]
201pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
206 202
207 fn set_waker(&mut self, waker: &Waker) { 203pub(crate) mod sealed {
208 unsafe {low_level_api::set_waker($index, waker )} 204 use super::*;
209 }
210 205
211 fn on_irq() { 206 pub trait Channel {
212 unsafe { 207 fn regs(&self) -> pac::dma::Dma;
213 low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); 208 fn num(&self) -> usize;
214 } 209 fn index(&self) -> usize;
215 } 210 fn on_irq();
216 } 211 }
217 impl crate::dma::Channel for crate::peripherals::$channel_peri { }
218 };
219} 212}
220 213
221mod low_level_api { 214#[must_use = "futures do nothing unless you `.await` or poll them"]
222 use super::*; 215pub struct Transfer<'a, C: Channel> {
216 channel: PeripheralRef<'a, C>,
217}
218
219impl<'a, C: Channel> Transfer<'a, C> {
220 pub unsafe fn new_read<W: Word>(
221 channel: impl Peripheral<P = C> + 'a,
222 request: Request,
223 peri_addr: *mut W,
224 buf: &'a mut [W],
225 options: TransferOptions,
226 ) -> Self {
227 Self::new_read_raw(channel, request, peri_addr, buf, options)
228 }
229
230 pub unsafe fn new_read_raw<W: Word>(
231 channel: impl Peripheral<P = C> + 'a,
232 request: Request,
233 peri_addr: *mut W,
234 buf: *mut [W],
235 options: TransferOptions,
236 ) -> Self {
237 into_ref!(channel);
238
239 let (ptr, len) = super::slice_ptr_parts_mut(buf);
240 assert!(len > 0 && len <= 0xFFFF);
241
242 Self::new_inner(
243 channel,
244 request,
245 Dir::PeripheralToMemory,
246 peri_addr as *const u32,
247 ptr as *mut u32,
248 len,
249 true,
250 W::size(),
251 options,
252 )
253 }
254
255 pub unsafe fn new_write<W: Word>(
256 channel: impl Peripheral<P = C> + 'a,
257 request: Request,
258 buf: &'a [W],
259 peri_addr: *mut W,
260 options: TransferOptions,
261 ) -> Self {
262 Self::new_write_raw(channel, request, buf, peri_addr, options)
263 }
264
265 pub unsafe fn new_write_raw<W: Word>(
266 channel: impl Peripheral<P = C> + 'a,
267 request: Request,
268 buf: *const [W],
269 peri_addr: *mut W,
270 options: TransferOptions,
271 ) -> Self {
272 into_ref!(channel);
273
274 let (ptr, len) = super::slice_ptr_parts(buf);
275 assert!(len > 0 && len <= 0xFFFF);
276
277 Self::new_inner(
278 channel,
279 request,
280 Dir::MemoryToPeripheral,
281 peri_addr as *const u32,
282 ptr as *mut u32,
283 len,
284 true,
285 W::size(),
286 options,
287 )
288 }
223 289
224 pub unsafe fn start_transfer( 290 pub unsafe fn new_write_repeated<W: Word>(
225 dma: pac::dma::Dma, 291 channel: impl Peripheral<P = C> + 'a,
226 channel_number: u8,
227 request: Request, 292 request: Request,
228 dir: vals::Dir, 293 repeated: &'a W,
294 count: usize,
295 peri_addr: *mut W,
296 options: TransferOptions,
297 ) -> Self {
298 into_ref!(channel);
299
300 Self::new_inner(
301 channel,
302 request,
303 Dir::MemoryToPeripheral,
304 peri_addr as *const u32,
305 repeated as *const W as *mut u32,
306 count,
307 false,
308 W::size(),
309 options,
310 )
311 }
312
313 unsafe fn new_inner(
314 channel: PeripheralRef<'a, C>,
315 _request: Request,
316 dir: Dir,
229 peri_addr: *const u32, 317 peri_addr: *const u32,
230 mem_addr: *mut u32, 318 mem_addr: *mut u32,
231 mem_len: usize, 319 mem_len: usize,
232 incr_mem: bool, 320 incr_mem: bool,
233 data_size: vals::Size, 321 data_size: WordSize,
234 options: TransferOptions, 322 options: TransferOptions,
235 #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, 323 ) -> Self {
236 #[cfg(dmamux)] dmamux_ch_num: u8, 324 let ch = channel.regs().st(channel.num());
237 ) {
238 #[cfg(dmamux)]
239 super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
240 325
241 // "Preceding reads and writes cannot be moved past subsequent writes." 326 // "Preceding reads and writes cannot be moved past subsequent writes."
242 fence(Ordering::SeqCst); 327 fence(Ordering::SeqCst);
243 328
244 reset_status(dma, channel_number); 329 let mut this = Self { channel };
330 this.clear_irqs();
331
332 #[cfg(dmamux)]
333 super::dmamux::configure_dmamux(&mut *this.channel, _request);
245 334
246 let ch = dma.st(channel_number as _);
247 ch.par().write_value(peri_addr as u32); 335 ch.par().write_value(peri_addr as u32);
248 ch.m0ar().write_value(mem_addr as u32); 336 ch.m0ar().write_value(mem_addr as u32);
249 ch.ndtr().write_value(regs::Ndtr(mem_len as _)); 337 ch.ndtr().write_value(regs::Ndtr(mem_len as _));
@@ -258,15 +346,14 @@ mod low_level_api {
258 } 346 }
259 }); 347 });
260 ch.cr().write(|w| { 348 ch.cr().write(|w| {
261 w.set_dir(dir); 349 w.set_dir(dir.into());
262 w.set_msize(data_size); 350 w.set_msize(data_size.into());
263 w.set_psize(data_size); 351 w.set_psize(data_size.into());
264 w.set_pl(vals::Pl::VERYHIGH); 352 w.set_pl(vals::Pl::VERYHIGH);
265 if incr_mem { 353 w.set_minc(match incr_mem {
266 w.set_minc(vals::Inc::INCREMENTED); 354 true => vals::Inc::INCREMENTED,
267 } else { 355 false => vals::Inc::FIXED,
268 w.set_minc(vals::Inc::FIXED); 356 });
269 }
270 w.set_pinc(vals::Inc::FIXED); 357 w.set_pinc(vals::Inc::FIXED);
271 w.set_teie(true); 358 w.set_teie(true);
272 w.set_tcie(true); 359 w.set_tcie(true);
@@ -274,7 +361,7 @@ mod low_level_api {
274 w.set_trbuff(true); 361 w.set_trbuff(true);
275 362
276 #[cfg(dma_v2)] 363 #[cfg(dma_v2)]
277 w.set_chsel(request); 364 w.set_chsel(_request);
278 365
279 w.set_pburst(options.pburst.into()); 366 w.set_pburst(options.pburst.into());
280 w.set_mburst(options.mburst.into()); 367 w.set_mburst(options.mburst.into());
@@ -282,66 +369,148 @@ mod low_level_api {
282 369
283 w.set_en(true); 370 w.set_en(true);
284 }); 371 });
372
373 this
285 } 374 }
286 375
287 pub unsafe fn start_dbm_transfer( 376 fn clear_irqs(&mut self) {
288 dma: pac::dma::Dma, 377 let isrn = self.channel.num() / 4;
289 channel_number: u8, 378 let isrbit = self.channel.num() % 4;
290 request: Request, 379
291 dir: vals::Dir, 380 unsafe {
292 peri_addr: *const u32, 381 self.channel.regs().ifcr(isrn).write(|w| {
293 mem0_addr: *mut u32, 382 w.set_tcif(isrbit, true);
294 mem1_addr: *mut u32, 383 w.set_teif(isrbit, true);
295 mem_len: usize, 384 })
296 incr_mem: bool, 385 }
297 data_size: vals::Size, 386 }
387
388 pub fn request_stop(&mut self) {
389 let ch = self.channel.regs().st(self.channel.num());
390
391 // Disable the channel. Keep the IEs enabled so the irqs still fire.
392 unsafe {
393 ch.cr().write(|w| {
394 w.set_teie(true);
395 w.set_tcie(true);
396 })
397 }
398 }
399
400 pub fn is_running(&mut self) -> bool {
401 let ch = self.channel.regs().st(self.channel.num());
402 unsafe { ch.cr().read() }.en()
403 }
404
405 /// Gets the total remaining transfers for the channel
406 /// Note: this will be zero for transfers that completed without cancellation.
407 pub fn get_remaining_transfers(&self) -> u16 {
408 let ch = self.channel.regs().st(self.channel.num());
409 unsafe { ch.ndtr().read() }.ndt()
410 }
411
412 pub fn blocking_wait(mut self) {
413 while self.is_running() {}
414
415 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
416 fence(Ordering::SeqCst);
417
418 core::mem::forget(self);
419 }
420}
421
422impl<'a, C: Channel> Drop for Transfer<'a, C> {
423 fn drop(&mut self) {
424 self.request_stop();
425 while self.is_running() {}
426
427 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
428 fence(Ordering::SeqCst);
429 }
430}
431
432impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
433impl<'a, C: Channel> Future for Transfer<'a, C> {
434 type Output = ();
435 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
436 STATE.ch_wakers[self.channel.index()].register(cx.waker());
437
438 if self.is_running() {
439 Poll::Pending
440 } else {
441 Poll::Ready(())
442 }
443 }
444}
445
446// ==================================
447
448#[must_use = "futures do nothing unless you `.await` or poll them"]
449pub struct DoubleBuffered<'a, C: Channel, W: Word> {
450 channel: PeripheralRef<'a, C>,
451 _phantom: PhantomData<W>,
452}
453
454impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
455 pub unsafe fn new_read(
456 channel: impl Peripheral<P = C> + 'a,
457 _request: Request,
458 peri_addr: *mut W,
459 buf0: *mut W,
460 buf1: *mut W,
461 len: usize,
298 options: TransferOptions, 462 options: TransferOptions,
299 #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, 463 ) -> Self {
300 #[cfg(dmamux)] dmamux_ch_num: u8, 464 into_ref!(channel);
301 ) { 465 assert!(len > 0 && len <= 0xFFFF);
302 #[cfg(dmamux)] 466
303 super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); 467 let dir = Dir::PeripheralToMemory;
468 let data_size = W::size();
304 469
305 trace!( 470 let channel_number = channel.num();
306 "Starting DBM transfer with 0: 0x{:x}, 1: 0x{:x}, len: 0x{:x}", 471 let dma = channel.regs();
307 mem0_addr as u32,
308 mem1_addr as u32,
309 mem_len
310 );
311 472
312 // "Preceding reads and writes cannot be moved past subsequent writes." 473 // "Preceding reads and writes cannot be moved past subsequent writes."
313 fence(Ordering::SeqCst); 474 fence(Ordering::SeqCst);
314 475
315 reset_status(dma, channel_number); 476 let mut this = Self {
477 channel,
478 _phantom: PhantomData,
479 };
480 this.clear_irqs();
481
482 #[cfg(dmamux)]
483 super::dmamux::configure_dmamux(&mut *this.channel, _request);
316 484
317 let ch = dma.st(channel_number as _); 485 let ch = dma.st(channel_number);
318 ch.par().write_value(peri_addr as u32); 486 ch.par().write_value(peri_addr as u32);
319 ch.m0ar().write_value(mem0_addr as u32); 487 ch.m0ar().write_value(buf0 as u32);
320 // configures the second buffer for DBM 488 ch.m1ar().write_value(buf1 as u32);
321 ch.m1ar().write_value(mem1_addr as u32); 489 ch.ndtr().write_value(regs::Ndtr(len as _));
322 ch.ndtr().write_value(regs::Ndtr(mem_len as _)); 490 ch.fcr().write(|w| {
323 ch.cr().write(|w| { 491 if let Some(fth) = options.fifo_threshold {
324 w.set_dir(dir); 492 // FIFO mode
325 w.set_msize(data_size); 493 w.set_dmdis(vals::Dmdis::DISABLED);
326 w.set_psize(data_size); 494 w.set_fth(fth.into());
327 w.set_pl(vals::Pl::VERYHIGH);
328 if incr_mem {
329 w.set_minc(vals::Inc::INCREMENTED);
330 } else { 495 } else {
331 w.set_minc(vals::Inc::FIXED); 496 // Direct mode
497 w.set_dmdis(vals::Dmdis::ENABLED);
332 } 498 }
499 });
500 ch.cr().write(|w| {
501 w.set_dir(dir.into());
502 w.set_msize(data_size.into());
503 w.set_psize(data_size.into());
504 w.set_pl(vals::Pl::VERYHIGH);
505 w.set_minc(vals::Inc::INCREMENTED);
333 w.set_pinc(vals::Inc::FIXED); 506 w.set_pinc(vals::Inc::FIXED);
334 w.set_teie(true); 507 w.set_teie(true);
335 w.set_tcie(true); 508 w.set_tcie(true);
336
337 #[cfg(dma_v1)] 509 #[cfg(dma_v1)]
338 w.set_trbuff(true); 510 w.set_trbuff(true);
339 511
340 #[cfg(dma_v2)] 512 #[cfg(dma_v2)]
341 w.set_chsel(request); 513 w.set_chsel(_request);
342
343 // enable double buffered mode
344 w.set_dbm(vals::Dbm::ENABLED);
345 514
346 w.set_pburst(options.pburst.into()); 515 w.set_pburst(options.pburst.into());
347 w.set_mburst(options.mburst.into()); 516 w.set_mburst(options.mburst.into());
@@ -349,92 +518,83 @@ mod low_level_api {
349 518
350 w.set_en(true); 519 w.set_en(true);
351 }); 520 });
521
522 this
352 } 523 }
353 524
354 pub unsafe fn set_dbm_buffer0(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) { 525 fn clear_irqs(&mut self) {
355 // get a handle on the channel itself 526 let channel_number = self.channel.num();
356 let ch = dma.st(channel_number as _); 527 let dma = self.channel.regs();
357 // change M0AR to the new address 528 let isrn = channel_number / 4;
358 ch.m0ar().write_value(mem_addr as _); 529 let isrbit = channel_number % 4;
530
531 unsafe {
532 dma.ifcr(isrn).write(|w| {
533 w.set_tcif(isrbit, true);
534 w.set_teif(isrbit, true);
535 })
536 }
359 } 537 }
360 538
361 pub unsafe fn set_dbm_buffer1(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) { 539 pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
362 // get a handle on the channel itself 540 let ch = self.channel.regs().st(self.channel.num());
363 let ch = dma.st(channel_number as _); 541 ch.m0ar().write_value(buffer as _);
364 // change M1AR to the new address
365 ch.m1ar().write_value(mem_addr as _);
366 } 542 }
367 543
368 pub unsafe fn is_buffer0_accessible(dma: pac::dma::Dma, channel_number: u8) -> bool { 544 pub unsafe fn set_buffer1(&mut self, buffer: *mut W) {
369 // get a handle on the channel itself 545 let ch = self.channel.regs().st(self.channel.num());
370 let ch = dma.st(channel_number as _); 546 ch.m1ar().write_value(buffer as _);
371 // check the current target register value
372 ch.cr().read().ct() == vals::Ct::MEMORY1
373 } 547 }
374 548
375 /// Stops the DMA channel. 549 pub fn is_buffer0_accessible(&mut self) -> bool {
376 pub unsafe fn request_stop(dma: pac::dma::Dma, channel_number: u8) { 550 let ch = self.channel.regs().st(self.channel.num());
377 // get a handle on the channel itself 551 unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1
378 let ch = dma.st(channel_number as _); 552 }
379 553
380 // Disable the channel. Keep the IEs enabled so the irqs still fire. 554 pub fn set_waker(&mut self, waker: &Waker) {
381 ch.cr().write(|w| { 555 STATE.ch_wakers[self.channel.index()].register(waker);
382 w.set_teie(true); 556 }
383 w.set_tcie(true);
384 });
385 557
386 // "Subsequent reads and writes cannot be moved ahead of preceding reads." 558 pub fn request_stop(&mut self) {
387 fence(Ordering::SeqCst); 559 let ch = self.channel.regs().st(self.channel.num());
560
561 // Disable the channel. Keep the IEs enabled so the irqs still fire.
562 unsafe {
563 ch.cr().write(|w| {
564 w.set_teie(true);
565 w.set_tcie(true);
566 })
567 }
388 } 568 }
389 569
390 /// Gets the running status of the channel 570 pub fn is_running(&mut self) -> bool {
391 pub unsafe fn is_running(dma: pac::dma::Dma, ch: u8) -> bool { 571 let ch = self.channel.regs().st(self.channel.num());
392 // get a handle on the channel itself 572 unsafe { ch.cr().read() }.en()
393 let ch = dma.st(ch as _);
394 // Get whether it's enabled (running)
395 ch.cr().read().en()
396 } 573 }
397 574
398 /// Gets the total remaining transfers for the channel 575 /// Gets the total remaining transfers for the channel
399 /// Note: this will be zero for transfers that completed without cancellation. 576 /// Note: this will be zero for transfers that completed without cancellation.
400 pub unsafe fn get_remaining_transfers(dma: pac::dma::Dma, ch: u8) -> u16 { 577 pub fn get_remaining_transfers(&self) -> u16 {
401 // get a handle on the channel itself 578 let ch = self.channel.regs().st(self.channel.num());
402 let ch = dma.st(ch as _); 579 unsafe { ch.ndtr().read() }.ndt()
403 // read the remaining transfer count. If this is zero, the transfer completed fully.
404 ch.ndtr().read().ndt()
405 } 580 }
406 581
407 /// Sets the waker for the specified DMA channel 582 pub fn blocking_wait(mut self) {
408 pub unsafe fn set_waker(state_number: usize, waker: &Waker) { 583 while self.is_running() {}
409 STATE.channels[state_number].waker.register(waker);
410 }
411 584
412 pub unsafe fn reset_status(dma: pac::dma::Dma, channel_number: u8) { 585 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
413 let isrn = channel_number as usize / 4; 586 fence(Ordering::SeqCst);
414 let isrbit = channel_number as usize % 4;
415 587
416 dma.ifcr(isrn).write(|w| { 588 core::mem::forget(self);
417 w.set_tcif(isrbit, true);
418 w.set_teif(isrbit, true);
419 });
420 } 589 }
590}
421 591
422 /// Safety: Must be called with a matching set of parameters for a valid dma channel 592impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> {
423 pub unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: u8, state_index: u8) { 593 fn drop(&mut self) {
424 let channel_num = channel_num as usize; 594 self.request_stop();
425 let state_index = state_index as usize; 595 while self.is_running() {}
426
427 let cr = dma.st(channel_num).cr();
428 let isr = dma.isr(channel_num / 4).read();
429
430 if isr.teif(channel_num % 4) {
431 panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
432 }
433 596
434 if isr.tcif(channel_num % 4) && cr.read().tcie() { 597 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
435 /* acknowledge transfer complete interrupt */ 598 fence(Ordering::SeqCst);
436 dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
437 STATE.channels[state_index].waker.wake();
438 }
439 } 599 }
440} 600}
diff --git a/embassy-stm32/src/dma/dmamux.rs b/embassy-stm32/src/dma/dmamux.rs
index e9967e349..a8c4c5827 100644
--- a/embassy-stm32/src/dma/dmamux.rs
+++ b/embassy-stm32/src/dma/dmamux.rs
@@ -2,8 +2,8 @@
2 2
3use crate::{pac, peripherals}; 3use crate::{pac, peripherals};
4 4
5pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_ch_num: u8, request: u8) { 5pub(crate) unsafe fn configure_dmamux<M: MuxChannel>(channel: &mut M, request: u8) {
6 let ch_mux_regs = dmamux_regs.ccr(dmamux_ch_num as _); 6 let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num());
7 ch_mux_regs.write(|reg| { 7 ch_mux_regs.write(|reg| {
8 reg.set_nbreq(0); 8 reg.set_nbreq(0);
9 reg.set_dmareq_id(request); 9 reg.set_dmareq_id(request);
@@ -14,11 +14,11 @@ pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_c
14 }); 14 });
15} 15}
16 16
17pub(crate) mod sealed { 17pub(crate) mod dmamux_sealed {
18 use super::*; 18 use super::*;
19 pub trait MuxChannel { 19 pub trait MuxChannel {
20 const DMAMUX_CH_NUM: u8; 20 fn mux_regs(&self) -> pac::dmamux::Dmamux;
21 const DMAMUX_REGS: pac::dmamux::Dmamux; 21 fn mux_num(&self) -> usize;
22 } 22 }
23} 23}
24 24
@@ -26,15 +26,19 @@ pub struct DMAMUX1;
26#[cfg(stm32h7)] 26#[cfg(stm32h7)]
27pub struct DMAMUX2; 27pub struct DMAMUX2;
28 28
29pub trait MuxChannel: sealed::MuxChannel + super::Channel { 29pub trait MuxChannel: dmamux_sealed::MuxChannel {
30 type Mux; 30 type Mux;
31} 31}
32 32
33foreach_dma_channel! { 33foreach_dma_channel! {
34 ($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $index:expr, {dmamux: $dmamux:ident, dmamux_channel: $dmamux_channel:expr}) => { 34 ($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $index:expr, {dmamux: $dmamux:ident, dmamux_channel: $dmamux_channel:expr}) => {
35 impl sealed::MuxChannel for peripherals::$channel_peri { 35 impl dmamux_sealed::MuxChannel for peripherals::$channel_peri {
36 const DMAMUX_CH_NUM: u8 = $dmamux_channel; 36 fn mux_regs(&self) -> pac::dmamux::Dmamux {
37 const DMAMUX_REGS: pac::dmamux::Dmamux = pac::$dmamux; 37 pac::$dmamux
38 }
39 fn mux_num(&self) -> usize {
40 $dmamux_channel
41 }
38 } 42 }
39 impl MuxChannel for peripherals::$channel_peri { 43 impl MuxChannel for peripherals::$channel_peri {
40 type Mux = $dmamux; 44 type Mux = $dmamux;
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs
index 6f26fd194..5a516ccda 100644
--- a/embassy-stm32/src/dma/gpdma.rs
+++ b/embassy-stm32/src/dma/gpdma.rs
@@ -1,13 +1,31 @@
1#![macro_use]
2
3use core::future::Future;
4use core::pin::Pin;
1use core::sync::atomic::{fence, Ordering}; 5use core::sync::atomic::{fence, Ordering};
2use core::task::Waker; 6use core::task::{Context, Poll};
3 7
8use embassy_cortex_m::interrupt::Priority;
9use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
4use embassy_sync::waitqueue::AtomicWaker; 10use embassy_sync::waitqueue::AtomicWaker;
5 11
6use super::{Request, TransferOptions, Word, WordSize}; 12use super::word::{Word, WordSize};
13use super::Dir;
7use crate::_generated::GPDMA_CHANNEL_COUNT; 14use crate::_generated::GPDMA_CHANNEL_COUNT;
8use crate::interrupt::{Interrupt, InterruptExt}; 15use crate::interrupt::{Interrupt, InterruptExt};
9use crate::pac::gpdma::{vals, Gpdma}; 16use crate::pac;
10use crate::{interrupt, pac}; 17use crate::pac::gpdma::vals;
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub struct TransferOptions {}
23
24impl Default for TransferOptions {
25 fn default() -> Self {
26 Self {}
27 }
28}
11 29
12impl From<WordSize> for vals::ChTr1Dw { 30impl From<WordSize> for vals::ChTr1Dw {
13 fn from(raw: WordSize) -> Self { 31 fn from(raw: WordSize) -> Self {
@@ -19,27 +37,15 @@ impl From<WordSize> for vals::ChTr1Dw {
19 } 37 }
20} 38}
21 39
22struct ChannelState {
23 waker: AtomicWaker,
24}
25
26impl ChannelState {
27 const fn new() -> Self {
28 Self {
29 waker: AtomicWaker::new(),
30 }
31 }
32}
33
34struct State { 40struct State {
35 channels: [ChannelState; GPDMA_CHANNEL_COUNT], 41 ch_wakers: [AtomicWaker; GPDMA_CHANNEL_COUNT],
36} 42}
37 43
38impl State { 44impl State {
39 const fn new() -> Self { 45 const fn new() -> Self {
40 const CH: ChannelState = ChannelState::new(); 46 const AW: AtomicWaker = AtomicWaker::new();
41 Self { 47 Self {
42 channels: [CH; GPDMA_CHANNEL_COUNT], 48 ch_wakers: [AW; GPDMA_CHANNEL_COUNT],
43 } 49 }
44 } 50 }
45} 51}
@@ -47,10 +53,12 @@ impl State {
47static STATE: State = State::new(); 53static STATE: State = State::new();
48 54
49/// safety: must be called only once 55/// safety: must be called only once
50pub(crate) unsafe fn init() { 56pub(crate) unsafe fn init(irq_priority: Priority) {
51 foreach_interrupt! { 57 foreach_interrupt! {
52 ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { 58 ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
53 interrupt::$irq::steal().enable(); 59 let irq = crate::interrupt::$irq::steal();
60 irq.set_priority(irq_priority);
61 irq.enable();
54 }; 62 };
55 } 63 }
56 crate::_generated::init_gpdma(); 64 crate::_generated::init_gpdma();
@@ -58,117 +66,171 @@ pub(crate) unsafe fn init() {
58 66
59foreach_dma_channel! { 67foreach_dma_channel! {
60 ($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => { 68 ($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => {
61 impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { 69 impl sealed::Channel for crate::peripherals::$channel_peri {
62 unsafe fn start_write<W: Word>(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { 70 fn regs(&self) -> pac::gpdma::Gpdma {
63 let (ptr, len) = super::slice_ptr_parts(buf); 71 pac::$dma_peri
64 low_level_api::start_transfer(
65 pac::$dma_peri,
66 $channel_num,
67 request,
68 low_level_api::Dir::MemoryToPeripheral,
69 reg_addr as *const u32,
70 ptr as *mut u32,
71 len,
72 true,
73 W::bits(),
74 options,
75 )
76 } 72 }
77 73 fn num(&self) -> usize {
78 unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { 74 $channel_num
79 low_level_api::start_transfer(
80 pac::$dma_peri,
81 $channel_num,
82 request,
83 low_level_api::Dir::MemoryToPeripheral,
84 reg_addr as *const u32,
85 repeated as *mut u32,
86 count,
87 false,
88 W::bits(),
89 options,
90 )
91 } 75 }
92 76 fn index(&self) -> usize {
93 unsafe fn start_read<W: Word>(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { 77 $index
94 let (ptr, len) = super::slice_ptr_parts_mut(buf);
95 low_level_api::start_transfer(
96 pac::$dma_peri,
97 $channel_num,
98 request,
99 low_level_api::Dir::PeripheralToMemory,
100 reg_addr as *const u32,
101 ptr as *mut u32,
102 len,
103 true,
104 W::bits(),
105 options,
106 );
107 } 78 }
108 79 fn on_irq() {
109 unsafe fn start_double_buffered_read<W: Word>( 80 unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
110 &mut self,
111 _request: Request,
112 _reg_addr: *const W,
113 _buffer0: *mut W,
114 _buffer1: *mut W,
115 _buffer_len: usize,
116 _options: TransferOptions,
117 ) {
118 panic!("Unsafe double buffered mode is unavailable on GPBDMA");
119 } 81 }
82 }
120 83
121 unsafe fn set_buffer0<W: Word>(&mut self, _buffer: *mut W) { 84 impl Channel for crate::peripherals::$channel_peri {}
122 panic!("Unsafe double buffered mode is unavailable on GPBDMA"); 85 };
123 } 86}
124 87
125 unsafe fn set_buffer1<W: Word>(&mut self, _buffer: *mut W) { 88/// Safety: Must be called with a matching set of parameters for a valid dma channel
126 panic!("Unsafe double buffered mode is unavailable on GPBDMA"); 89pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, index: usize) {
127 } 90 let ch = dma.ch(channel_num);
91 let sr = ch.sr().read();
128 92
129 unsafe fn is_buffer0_accessible(&mut self) -> bool { 93 if sr.dtef() {
130 panic!("Unsafe double buffered mode is unavailable on GPBDMA"); 94 panic!(
131 } 95 "DMA: data transfer error on DMA@{:08x} channel {}",
96 dma.0 as u32, channel_num
97 );
98 }
99 if sr.usef() {
100 panic!(
101 "DMA: user settings error on DMA@{:08x} channel {}",
102 dma.0 as u32, channel_num
103 );
104 }
132 105
133 fn request_stop(&mut self) { 106 if sr.suspf() || sr.tcf() {
134 unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} 107 // disable all xxIEs to prevent the irq from firing again.
135 } 108 ch.cr().write(|_| {});
136 109
137 fn is_running(&self) -> bool { 110 // Wake the future. It'll look at tcf and see it's set.
138 unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} 111 STATE.ch_wakers[index].wake();
139 } 112 }
113}
140 114
141 fn remaining_transfers(&mut self) -> u16 { 115pub type Request = u8;
142 unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)}
143 }
144 116
145 fn set_waker(&mut self, waker: &Waker) { 117#[cfg(dmamux)]
146 unsafe {low_level_api::set_waker($index, waker )} 118pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
147 } 119#[cfg(not(dmamux))]
120pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
148 121
149 fn on_irq() { 122pub(crate) mod sealed {
150 unsafe { 123 use super::*;
151 low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); 124
152 } 125 pub trait Channel {
153 } 126 fn regs(&self) -> pac::gpdma::Gpdma;
154 } 127 fn num(&self) -> usize;
155 impl crate::dma::Channel for crate::peripherals::$channel_peri { } 128 fn index(&self) -> usize;
156 }; 129 fn on_irq();
130 }
157} 131}
158 132
159mod low_level_api { 133#[must_use = "futures do nothing unless you `.await` or poll them"]
160 use super::*; 134pub struct Transfer<'a, C: Channel> {
135 channel: PeripheralRef<'a, C>,
136}
161 137
162 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 138impl<'a, C: Channel> Transfer<'a, C> {
163 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 139 pub unsafe fn new_read<W: Word>(
164 pub enum Dir { 140 channel: impl Peripheral<P = C> + 'a,
165 MemoryToPeripheral, 141 request: Request,
166 PeripheralToMemory, 142 peri_addr: *mut W,
143 buf: &'a mut [W],
144 options: TransferOptions,
145 ) -> Self {
146 Self::new_read_raw(channel, request, peri_addr, buf, options)
167 } 147 }
168 148
169 pub unsafe fn start_transfer( 149 pub unsafe fn new_read_raw<W: Word>(
170 dma: Gpdma, 150 channel: impl Peripheral<P = C> + 'a,
171 channel_number: u8, 151 request: Request,
152 peri_addr: *mut W,
153 buf: *mut [W],
154 options: TransferOptions,
155 ) -> Self {
156 into_ref!(channel);
157
158 let (ptr, len) = super::slice_ptr_parts_mut(buf);
159 assert!(len > 0 && len <= 0xFFFF);
160
161 Self::new_inner(
162 channel,
163 request,
164 Dir::PeripheralToMemory,
165 peri_addr as *const u32,
166 ptr as *mut u32,
167 len,
168 true,
169 W::size(),
170 options,
171 )
172 }
173
174 pub unsafe fn new_write<W: Word>(
175 channel: impl Peripheral<P = C> + 'a,
176 request: Request,
177 buf: &'a [W],
178 peri_addr: *mut W,
179 options: TransferOptions,
180 ) -> Self {
181 Self::new_write_raw(channel, request, buf, peri_addr, options)
182 }
183
184 pub unsafe fn new_write_raw<W: Word>(
185 channel: impl Peripheral<P = C> + 'a,
186 request: Request,
187 buf: *const [W],
188 peri_addr: *mut W,
189 options: TransferOptions,
190 ) -> Self {
191 into_ref!(channel);
192
193 let (ptr, len) = super::slice_ptr_parts(buf);
194 assert!(len > 0 && len <= 0xFFFF);
195
196 Self::new_inner(
197 channel,
198 request,
199 Dir::MemoryToPeripheral,
200 peri_addr as *const u32,
201 ptr as *mut u32,
202 len,
203 true,
204 W::size(),
205 options,
206 )
207 }
208
209 pub unsafe fn new_write_repeated<W: Word>(
210 channel: impl Peripheral<P = C> + 'a,
211 request: Request,
212 repeated: &'a W,
213 count: usize,
214 peri_addr: *mut W,
215 options: TransferOptions,
216 ) -> Self {
217 into_ref!(channel);
218
219 Self::new_inner(
220 channel,
221 request,
222 Dir::MemoryToPeripheral,
223 peri_addr as *const u32,
224 repeated as *const W as *mut u32,
225 count,
226 false,
227 W::size(),
228 options,
229 )
230 }
231
232 unsafe fn new_inner(
233 channel: PeripheralRef<'a, C>,
172 request: Request, 234 request: Request,
173 dir: Dir, 235 dir: Dir,
174 peri_addr: *const u32, 236 peri_addr: *const u32,
@@ -176,24 +238,19 @@ mod low_level_api {
176 mem_len: usize, 238 mem_len: usize,
177 incr_mem: bool, 239 incr_mem: bool,
178 data_size: WordSize, 240 data_size: WordSize,
179 options: TransferOptions, 241 _options: TransferOptions,
180 ) { 242 ) -> Self {
181 assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); 243 let ch = channel.regs().ch(channel.num());
182 assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported");
183 assert!(
184 options.flow_ctrl == crate::dma::FlowControl::Dma,
185 "Peripheral flow control not supported"
186 );
187 assert!(options.fifo_threshold.is_none(), "FIFO mode not supported");
188 244
189 // "Preceding reads and writes cannot be moved past subsequent writes." 245 // "Preceding reads and writes cannot be moved past subsequent writes."
190 fence(Ordering::SeqCst); 246 fence(Ordering::SeqCst);
191 247
192 let ch = dma.ch(channel_number as _); 248 let this = Self { channel };
193 249
194 // Reset ch 250 #[cfg(dmamux)]
195 ch.cr().write(|w| w.set_reset(true)); 251 super::dmamux::configure_dmamux(&mut *this.channel, request);
196 252
253 ch.cr().write(|w| w.set_reset(true));
197 ch.llr().write(|_| {}); // no linked list 254 ch.llr().write(|_| {}); // no linked list
198 ch.tr1().write(|w| { 255 ch.tr1().write(|w| {
199 w.set_sdw(data_size.into()); 256 w.set_sdw(data_size.into());
@@ -234,72 +291,66 @@ mod low_level_api {
234 // Start it 291 // Start it
235 w.set_en(true); 292 w.set_en(true);
236 }); 293 });
294
295 this
237 } 296 }
238 297
239 /// Stops the DMA channel. 298 pub fn request_stop(&mut self) {
240 pub unsafe fn request_stop(dma: Gpdma, channel_number: u8) { 299 let ch = self.channel.regs().ch(self.channel.num());
241 // get a handle on the channel itself
242 let ch = dma.ch(channel_number as _);
243 300
244 // Disable the channel. Keep the IEs enabled so the irqs still fire. 301 // Disable the channel. Keep the IEs enabled so the irqs still fire.
245 ch.cr().write(|w| { 302 unsafe {
246 w.set_tcie(true); 303 ch.cr().write(|w| {
247 w.set_useie(true); 304 w.set_tcie(true);
248 w.set_dteie(true); 305 w.set_useie(true);
249 w.set_suspie(true); 306 w.set_dteie(true);
250 }); 307 w.set_suspie(true);
251 308 })
252 // "Subsequent reads and writes cannot be moved ahead of preceding reads." 309 }
253 fence(Ordering::SeqCst);
254 } 310 }
255 311
256 /// Gets the running status of the channel 312 pub fn is_running(&mut self) -> bool {
257 pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { 313 let ch = self.channel.regs().ch(self.channel.num());
258 let ch = dma.ch(ch as _); 314 !unsafe { ch.sr().read() }.tcf()
259 !ch.sr().read().tcf()
260 } 315 }
261 316
262 /// Gets the total remaining transfers for the channel 317 /// Gets the total remaining transfers for the channel
263 /// Note: this will be zero for transfers that completed without cancellation. 318 /// Note: this will be zero for transfers that completed without cancellation.
264 pub unsafe fn get_remaining_transfers(dma: Gpdma, ch: u8) -> u16 { 319 pub fn get_remaining_transfers(&self) -> u16 {
265 // get a handle on the channel itself 320 let ch = self.channel.regs().ch(self.channel.num());
266 let ch = dma.ch(ch as _); 321 unsafe { ch.br1().read() }.bndt()
267 // read the remaining transfer count. If this is zero, the transfer completed fully.
268 ch.br1().read().bndt()
269 } 322 }
270 323
271 /// Sets the waker for the specified DMA channel 324 pub fn blocking_wait(mut self) {
272 pub unsafe fn set_waker(state_number: usize, waker: &Waker) { 325 while self.is_running() {}
273 STATE.channels[state_number].waker.register(waker);
274 }
275 326
276 /// Safety: Must be called with a matching set of parameters for a valid dma channel 327 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
277 pub unsafe fn on_irq_inner(dma: Gpdma, channel_num: u8, state_index: u8) { 328 fence(Ordering::SeqCst);
278 let channel_num = channel_num as usize;
279 let state_index = state_index as usize;
280 329
281 let ch = dma.ch(channel_num); 330 core::mem::forget(self);
282 let sr = ch.sr().read(); 331 }
332}
283 333
284 if sr.dtef() { 334impl<'a, C: Channel> Drop for Transfer<'a, C> {
285 panic!( 335 fn drop(&mut self) {
286 "DMA: data transfer error on DMA@{:08x} channel {}", 336 self.request_stop();
287 dma.0 as u32, channel_num 337 while self.is_running() {}
288 ); 338
289 } 339 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
290 if sr.usef() { 340 fence(Ordering::SeqCst);
291 panic!( 341 }
292 "DMA: user settings error on DMA@{:08x} channel {}", 342}
293 dma.0 as u32, channel_num
294 );
295 }
296 343
297 if sr.suspf() || sr.tcf() { 344impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
298 // disable all xxIEs to prevent the irq from firing again. 345impl<'a, C: Channel> Future for Transfer<'a, C> {
299 ch.cr().write(|_| {}); 346 type Output = ();
347 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
348 STATE.ch_wakers[self.channel.index()].register(cx.waker());
300 349
301 // Wake the future. It'll look at tcf and see it's set. 350 if self.is_running() {
302 STATE.channels[state_index].waker.wake(); 351 Poll::Pending
352 } else {
353 Poll::Ready(())
303 } 354 }
304 } 355 }
305} 356}
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index 0030bd575..3312ca752 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -1,329 +1,47 @@
1#[cfg(bdma)]
2pub(crate) mod bdma;
3#[cfg(dma)] 1#[cfg(dma)]
4pub(crate) mod dma; 2pub(crate) mod dma;
3#[cfg(dma)]
4pub use dma::*;
5
6// stm32h7 has both dma and bdma. In that case, we export dma as "main" dma,
7// and bdma as "secondary", under `embassy_stm32::dma::bdma`.
8#[cfg(all(bdma, dma))]
9pub mod bdma;
10
11#[cfg(all(bdma, not(dma)))]
12pub(crate) mod bdma;
13#[cfg(all(bdma, not(dma)))]
14pub use bdma::*;
15
16#[cfg(gpdma)]
17pub(crate) mod gpdma;
18#[cfg(gpdma)]
19pub use gpdma::*;
20
5#[cfg(dmamux)] 21#[cfg(dmamux)]
6mod dmamux; 22mod dmamux;
7#[cfg(gpdma)]
8mod gpdma;
9 23
10use core::future::Future; 24pub mod word;
25
11use core::mem; 26use core::mem;
12use core::pin::Pin;
13use core::task::{Context, Poll, Waker};
14 27
15#[cfg(any(dma, bdma))]
16use embassy_cortex_m::interrupt::Priority; 28use embassy_cortex_m::interrupt::Priority;
17use embassy_hal_common::{impl_peripheral, into_ref}; 29use embassy_hal_common::impl_peripheral;
18 30
19#[cfg(dmamux)] 31#[cfg(dmamux)]
20pub use self::dmamux::*; 32pub use self::dmamux::*;
21use crate::Peripheral;
22
23#[cfg(feature = "unstable-pac")]
24pub mod low_level {
25 pub use super::transfers::*;
26}
27
28pub(crate) use transfers::*;
29
30#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))]
31pub type Request = u8;
32#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))]
33pub type Request = ();
34
35pub(crate) mod sealed {
36 use super::*;
37
38 pub trait Word {}
39
40 pub trait Channel {
41 /// Starts this channel for writing a stream of words.
42 ///
43 /// Safety:
44 /// - `buf` must point to a valid buffer for DMA reading.
45 /// - `buf` must be alive for the entire duration of the DMA transfer.
46 /// - `reg_addr` must be a valid peripheral register address to write to.
47 unsafe fn start_write<W: super::Word>(
48 &mut self,
49 request: Request,
50 buf: *const [W],
51 reg_addr: *mut W,
52 options: TransferOptions,
53 );
54
55 /// Starts this channel for writing a word repeatedly.
56 ///
57 /// Safety:
58 /// - `reg_addr` must be a valid peripheral register address to write to.
59 unsafe fn start_write_repeated<W: super::Word>(
60 &mut self,
61 request: Request,
62 repeated: *const W,
63 count: usize,
64 reg_addr: *mut W,
65 options: TransferOptions,
66 );
67
68 /// Starts this channel for reading a stream of words.
69 ///
70 /// Safety:
71 /// - `buf` must point to a valid buffer for DMA writing.
72 /// - `buf` must be alive for the entire duration of the DMA transfer.
73 /// - `reg_addr` must be a valid peripheral register address to read from.
74 unsafe fn start_read<W: super::Word>(
75 &mut self,
76 request: Request,
77 reg_addr: *const W,
78 buf: *mut [W],
79 options: TransferOptions,
80 );
81
82 /// DMA double-buffered mode is unsafe as UB can happen when the hardware writes to a buffer currently owned by the software
83 /// more information can be found here: https://github.com/embassy-rs/embassy/issues/702
84 /// This feature is now used solely for the purposes of implementing giant DMA transfers required for DCMI
85 unsafe fn start_double_buffered_read<W: super::Word>(
86 &mut self,
87 request: Request,
88 reg_addr: *const W,
89 buffer0: *mut W,
90 buffer1: *mut W,
91 buffer_len: usize,
92 options: TransferOptions,
93 );
94
95 unsafe fn set_buffer0<W: super::Word>(&mut self, buffer: *mut W);
96
97 unsafe fn set_buffer1<W: super::Word>(&mut self, buffer: *mut W);
98
99 unsafe fn is_buffer0_accessible(&mut self) -> bool;
100
101 /// Requests the channel to stop.
102 /// NOTE: The channel does not immediately stop, you have to wait
103 /// for `is_running() = false`.
104 fn request_stop(&mut self);
105
106 /// Returns whether this channel is running or stopped.
107 ///
108 /// The channel stops running when it either completes or is manually stopped.
109 fn is_running(&self) -> bool;
110
111 /// Returns the total number of remaining transfers.
112 fn remaining_transfers(&mut self) -> u16;
113
114 /// Sets the waker that is called when this channel stops (either completed or manually stopped)
115 fn set_waker(&mut self, waker: &Waker);
116
117 /// This is called when this channel triggers an interrupt.
118 /// Note: Because some channels share an interrupt, this function might be
119 /// called for a channel that didn't trigger an interrupt.
120 fn on_irq();
121 }
122}
123
124#[derive(Debug, Copy, Clone, PartialEq, Eq)]
125#[cfg_attr(feature = "defmt", derive(defmt::Format))]
126pub enum WordSize {
127 OneByte,
128 TwoBytes,
129 FourBytes,
130}
131
132impl WordSize {
133 pub fn bytes(&self) -> usize {
134 match self {
135 Self::OneByte => 1,
136 Self::TwoBytes => 2,
137 Self::FourBytes => 4,
138 }
139 }
140}
141
142pub trait Word: sealed::Word {
143 fn bits() -> WordSize;
144}
145
146impl sealed::Word for u8 {}
147impl Word for u8 {
148 fn bits() -> WordSize {
149 WordSize::OneByte
150 }
151}
152
153impl sealed::Word for u16 {}
154impl Word for u16 {
155 fn bits() -> WordSize {
156 WordSize::TwoBytes
157 }
158}
159
160impl sealed::Word for u32 {}
161impl Word for u32 {
162 fn bits() -> WordSize {
163 WordSize::FourBytes
164 }
165}
166
167#[derive(Debug, Copy, Clone, PartialEq, Eq)]
168#[cfg_attr(feature = "defmt", derive(defmt::Format))]
169pub enum Burst {
170 /// Single transfer
171 Single,
172 /// Incremental burst of 4 beats
173 Incr4,
174 /// Incremental burst of 8 beats
175 Incr8,
176 /// Incremental burst of 16 beats
177 Incr16,
178}
179 33
180#[derive(Debug, Copy, Clone, PartialEq, Eq)] 34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))] 35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182pub enum FlowControl { 36enum Dir {
183 /// Flow control by DMA 37 MemoryToPeripheral,
184 Dma, 38 PeripheralToMemory,
185 /// Flow control by peripheral
186 Peripheral,
187} 39}
188 40
189#[derive(Debug, Copy, Clone, PartialEq, Eq)]
190#[cfg_attr(feature = "defmt", derive(defmt::Format))]
191pub enum FifoThreshold {
192 /// 1/4 full FIFO
193 Quarter,
194 /// 1/2 full FIFO
195 Half,
196 /// 3/4 full FIFO
197 ThreeQuarters,
198 /// Full FIFO
199 Full,
200}
201
202#[derive(Debug, Copy, Clone, PartialEq, Eq)]
203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
204pub struct TransferOptions {
205 /// Peripheral burst transfer configuration
206 pub pburst: Burst,
207 /// Memory burst transfer configuration
208 pub mburst: Burst,
209 /// Flow control configuration
210 pub flow_ctrl: FlowControl,
211 /// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
212 pub fifo_threshold: Option<FifoThreshold>,
213}
214
215impl Default for TransferOptions {
216 fn default() -> Self {
217 Self {
218 pburst: Burst::Single,
219 mburst: Burst::Single,
220 flow_ctrl: FlowControl::Dma,
221 fifo_threshold: None,
222 }
223 }
224}
225
226mod transfers {
227 use embassy_hal_common::PeripheralRef;
228
229 use super::*;
230
231 #[allow(unused)]
232 pub fn read<'a, W: Word>(
233 channel: impl Peripheral<P = impl Channel> + 'a,
234 request: Request,
235 reg_addr: *mut W,
236 buf: &'a mut [W],
237 ) -> impl Future<Output = ()> + 'a {
238 assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
239 into_ref!(channel);
240
241 unsafe { channel.start_read::<W>(request, reg_addr, buf, Default::default()) };
242
243 Transfer::new(channel)
244 }
245
246 #[allow(unused)]
247 pub fn write<'a, W: Word>(
248 channel: impl Peripheral<P = impl Channel> + 'a,
249 request: Request,
250 buf: &'a [W],
251 reg_addr: *mut W,
252 ) -> impl Future<Output = ()> + 'a {
253 assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
254 into_ref!(channel);
255
256 unsafe { channel.start_write::<W>(request, buf, reg_addr, Default::default()) };
257
258 Transfer::new(channel)
259 }
260
261 #[allow(unused)]
262 pub fn write_repeated<'a, W: Word>(
263 channel: impl Peripheral<P = impl Channel> + 'a,
264 request: Request,
265 repeated: *const W,
266 count: usize,
267 reg_addr: *mut W,
268 ) -> impl Future<Output = ()> + 'a {
269 into_ref!(channel);
270
271 unsafe { channel.start_write_repeated::<W>(request, repeated, count, reg_addr, Default::default()) };
272
273 Transfer::new(channel)
274 }
275
276 #[must_use = "futures do nothing unless you `.await` or poll them"]
277 pub(crate) struct Transfer<'a, C: Channel> {
278 channel: PeripheralRef<'a, C>,
279 }
280
281 impl<'a, C: Channel> Transfer<'a, C> {
282 pub(crate) fn new(channel: impl Peripheral<P = C> + 'a) -> Self {
283 into_ref!(channel);
284 Self { channel }
285 }
286 }
287
288 impl<'a, C: Channel> Drop for Transfer<'a, C> {
289 fn drop(&mut self) {
290 self.channel.request_stop();
291 while self.channel.is_running() {}
292 }
293 }
294
295 impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
296 impl<'a, C: Channel> Future for Transfer<'a, C> {
297 type Output = ();
298 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
299 self.channel.set_waker(cx.waker());
300 if self.channel.is_running() {
301 Poll::Pending
302 } else {
303 Poll::Ready(())
304 }
305 }
306 }
307}
308
309pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
310
311pub struct NoDma; 41pub struct NoDma;
312 42
313impl_peripheral!(NoDma); 43impl_peripheral!(NoDma);
314 44
315// safety: must be called only once at startup
316pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) {
317 #[cfg(bdma)]
318 bdma::init(bdma_priority);
319 #[cfg(dma)]
320 dma::init(dma_priority);
321 #[cfg(dmamux)]
322 dmamux::init();
323 #[cfg(gpdma)]
324 gpdma::init();
325}
326
327// TODO: replace transmutes with core::ptr::metadata once it's stable 45// TODO: replace transmutes with core::ptr::metadata once it's stable
328#[allow(unused)] 46#[allow(unused)]
329pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) { 47pub(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) {
334pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) { 52pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) {
335 unsafe { mem::transmute(slice) } 53 unsafe { mem::transmute(slice) }
336} 54}
55
56// safety: must be called only once at startup
57pub(crate) unsafe fn init(
58 #[cfg(bdma)] bdma_priority: Priority,
59 #[cfg(dma)] dma_priority: Priority,
60 #[cfg(gpdma)] gpdma_priority: Priority,
61) {
62 #[cfg(bdma)]
63 bdma::init(bdma_priority);
64 #[cfg(dma)]
65 dma::init(dma_priority);
66 #[cfg(gpdma)]
67 gpdma::init(gpdma_priority);
68 #[cfg(dmamux)]
69 dmamux::init();
70}
diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs
new file mode 100644
index 000000000..aef6e9700
--- /dev/null
+++ b/embassy-stm32/src/dma/word.rs
@@ -0,0 +1,79 @@
1#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2#[cfg_attr(feature = "defmt", derive(defmt::Format))]
3pub enum WordSize {
4 OneByte,
5 TwoBytes,
6 FourBytes,
7}
8
9impl WordSize {
10 pub fn bytes(&self) -> usize {
11 match self {
12 Self::OneByte => 1,
13 Self::TwoBytes => 2,
14 Self::FourBytes => 4,
15 }
16 }
17}
18
19mod sealed {
20 pub trait Word {}
21}
22
23pub trait Word: sealed::Word + Default + Copy + 'static {
24 fn size() -> WordSize;
25 fn bits() -> usize;
26}
27
28macro_rules! impl_word {
29 (_, $T:ident, $bits:literal, $size:ident) => {
30 impl sealed::Word for $T {}
31 impl Word for $T {
32 fn bits() -> usize {
33 $bits
34 }
35 fn size() -> WordSize {
36 WordSize::$size
37 }
38 }
39 };
40 ($T:ident, $uX:ident, $bits:literal, $size:ident) => {
41 #[repr(transparent)]
42 #[derive(Copy, Clone, Default)]
43 pub struct $T(pub $uX);
44 impl_word!(_, $T, $bits, $size);
45 };
46}
47
48impl_word!(U1, u8, 1, OneByte);
49impl_word!(U2, u8, 2, OneByte);
50impl_word!(U3, u8, 3, OneByte);
51impl_word!(U4, u8, 4, OneByte);
52impl_word!(U5, u8, 5, OneByte);
53impl_word!(U6, u8, 6, OneByte);
54impl_word!(U7, u8, 7, OneByte);
55impl_word!(_, u8, 8, OneByte);
56impl_word!(U9, u16, 9, TwoBytes);
57impl_word!(U10, u16, 10, TwoBytes);
58impl_word!(U11, u16, 11, TwoBytes);
59impl_word!(U12, u16, 12, TwoBytes);
60impl_word!(U13, u16, 13, TwoBytes);
61impl_word!(U14, u16, 14, TwoBytes);
62impl_word!(U15, u16, 15, TwoBytes);
63impl_word!(_, u16, 16, TwoBytes);
64impl_word!(U17, u32, 17, FourBytes);
65impl_word!(U18, u32, 18, FourBytes);
66impl_word!(U19, u32, 19, FourBytes);
67impl_word!(U20, u32, 20, FourBytes);
68impl_word!(U21, u32, 21, FourBytes);
69impl_word!(U22, u32, 22, FourBytes);
70impl_word!(U23, u32, 23, FourBytes);
71impl_word!(U24, u32, 24, FourBytes);
72impl_word!(U25, u32, 25, FourBytes);
73impl_word!(U26, u32, 26, FourBytes);
74impl_word!(U27, u32, 27, FourBytes);
75impl_word!(U28, u32, 28, FourBytes);
76impl_word!(U29, u32, 29, FourBytes);
77impl_word!(U30, u32, 30, FourBytes);
78impl_word!(U31, u32, 31, FourBytes);
79impl_word!(_, u32, 32, FourBytes);
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs
index 1189e447e..6d7f55974 100644
--- a/embassy-stm32/src/flash/common.rs
+++ b/embassy-stm32/src/flash/common.rs
@@ -2,7 +2,7 @@ use atomic_polyfill::{fence, Ordering};
2use embassy_hal_common::drop::OnDrop; 2use embassy_hal_common::drop::OnDrop;
3use embassy_hal_common::{into_ref, PeripheralRef}; 3use embassy_hal_common::{into_ref, PeripheralRef};
4 4
5use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; 5use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE};
6use crate::flash::FlashBank; 6use crate::flash::FlashBank;
7use crate::Peripheral; 7use crate::Peripheral;
8 8
@@ -162,6 +162,35 @@ impl FlashRegion {
162 } 162 }
163} 163}
164 164
165impl embedded_storage::nor_flash::ErrorType for Flash<'_> {
166 type Error = Error;
167}
168
169impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> {
170 const READ_SIZE: usize = 1;
171
172 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
173 self.blocking_read(offset, bytes)
174 }
175
176 fn capacity(&self) -> usize {
177 FLASH_SIZE
178 }
179}
180
181impl embedded_storage::nor_flash::NorFlash for Flash<'_> {
182 const WRITE_SIZE: usize = WRITE_SIZE;
183 const ERASE_SIZE: usize = MAX_ERASE_SIZE;
184
185 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
186 self.blocking_write(offset, bytes)
187 }
188
189 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
190 self.blocking_erase(from, to)
191 }
192}
193
165foreach_flash_region! { 194foreach_flash_region! {
166 ($type_name:ident, $write_size:literal, $erase_size:literal) => { 195 ($type_name:ident, $write_size:literal, $erase_size:literal) => {
167 impl crate::_generated::flash_regions::$type_name<'_> { 196 impl crate::_generated::flash_regions::$type_name<'_> {
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index 231ff1f9e..7d5596b1f 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -7,6 +7,7 @@ mod common;
7pub use common::*; 7pub use common::*;
8 8
9pub use crate::_generated::flash_regions::*; 9pub use crate::_generated::flash_regions::*;
10pub use crate::_generated::MAX_ERASE_SIZE;
10pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; 11pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
11 12
12#[derive(Debug)] 13#[derive(Debug)]
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 7218f7706..853bc128f 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -1,6 +1,5 @@
1use core::cmp; 1use core::cmp;
2use core::future::poll_fn; 2use core::future::poll_fn;
3use core::sync::atomic::{AtomicUsize, Ordering};
4use core::task::Poll; 3use core::task::Poll;
5 4
6use embassy_embedded_hal::SetConfig; 5use embassy_embedded_hal::SetConfig;
@@ -8,7 +7,7 @@ use embassy_hal_common::drop::OnDrop;
8use embassy_hal_common::{into_ref, PeripheralRef}; 7use embassy_hal_common::{into_ref, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker; 8use embassy_sync::waitqueue::AtomicWaker;
10 9
11use crate::dma::NoDma; 10use crate::dma::{NoDma, Transfer};
12use crate::gpio::sealed::AFType; 11use crate::gpio::sealed::AFType;
13use crate::gpio::Pull; 12use crate::gpio::Pull;
14use crate::i2c::{Error, Instance, SclPin, SdaPin}; 13use crate::i2c::{Error, Instance, SclPin, SdaPin};
@@ -35,14 +34,12 @@ impl Default for Config {
35 34
36pub struct State { 35pub struct State {
37 waker: AtomicWaker, 36 waker: AtomicWaker,
38 chunks_transferred: AtomicUsize,
39} 37}
40 38
41impl State { 39impl State {
42 pub(crate) const fn new() -> Self { 40 pub(crate) const fn new() -> Self {
43 Self { 41 Self {
44 waker: AtomicWaker::new(), 42 waker: AtomicWaker::new(),
45 chunks_transferred: AtomicUsize::new(0),
46 } 43 }
47 } 44 }
48} 45}
@@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
130 let isr = regs.isr().read(); 127 let isr = regs.isr().read();
131 128
132 if isr.tcr() || isr.tc() { 129 if isr.tcr() || isr.tc() {
133 let state = T::state(); 130 T::state().waker.wake();
134 let transferred = state.chunks_transferred.load(Ordering::Relaxed);
135 state.chunks_transferred.store(transferred + 1, Ordering::Relaxed);
136 state.waker.wake();
137 } 131 }
138 // The flag can only be cleared by writting to nbytes, we won't do that here, so disable 132 // The flag can only be cleared by writting to nbytes, we won't do that here, so disable
139 // the interrupt 133 // the interrupt
@@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
457 TXDMA: crate::i2c::TxDma<T>, 451 TXDMA: crate::i2c::TxDma<T>,
458 { 452 {
459 let total_len = write.len(); 453 let total_len = write.len();
460 let completed_chunks = total_len / 255;
461 let total_chunks = if completed_chunks * 255 == total_len {
462 completed_chunks
463 } else {
464 completed_chunks + 1
465 };
466 454
467 let dma_transfer = unsafe { 455 let dma_transfer = unsafe {
468 let regs = T::regs(); 456 let regs = T::regs();
@@ -476,11 +464,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
476 464
477 let ch = &mut self.tx_dma; 465 let ch = &mut self.tx_dma;
478 let request = ch.request(); 466 let request = ch.request();
479 crate::dma::write(ch, request, write, dst) 467 Transfer::new_write(ch, request, write, dst, Default::default())
480 }; 468 };
481 469
482 let state = T::state(); 470 let state = T::state();
483 state.chunks_transferred.store(0, Ordering::Relaxed);
484 let mut remaining_len = total_len; 471 let mut remaining_len = total_len;
485 472
486 let on_drop = OnDrop::new(|| { 473 let on_drop = OnDrop::new(|| {
@@ -495,33 +482,35 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
495 } 482 }
496 }); 483 });
497 484
498 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
499 if first_slice {
500 unsafe {
501 Self::master_write(
502 address,
503 total_len.min(255),
504 Stop::Software,
505 (total_chunks != 1) || !last_slice,
506 &check_timeout,
507 )?;
508 }
509 } else {
510 unsafe {
511 Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?;
512 T::regs().cr1().modify(|w| w.set_tcie(true));
513 }
514 }
515
516 poll_fn(|cx| { 485 poll_fn(|cx| {
517 state.waker.register(cx.waker()); 486 state.waker.register(cx.waker());
518 let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
519 487
520 if chunks_transferred == total_chunks { 488 let isr = unsafe { T::regs().isr().read() };
489 if remaining_len == total_len {
490 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
491 if first_slice {
492 unsafe {
493 Self::master_write(
494 address,
495 total_len.min(255),
496 Stop::Software,
497 (total_len > 255) || !last_slice,
498 &check_timeout,
499 )?;
500 }
501 } else {
502 unsafe {
503 Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?;
504 T::regs().cr1().modify(|w| w.set_tcie(true));
505 }
506 }
507 } else if !(isr.tcr() || isr.tc()) {
508 // poll_fn was woken without an interrupt present
509 return Poll::Pending;
510 } else if remaining_len == 0 {
521 return Poll::Ready(Ok(())); 511 return Poll::Ready(Ok(()));
522 } else if chunks_transferred != 0 { 512 } else {
523 remaining_len = remaining_len.saturating_sub(255); 513 let last_piece = (remaining_len <= 255) && last_slice;
524 let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
525 514
526 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers 515 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
527 unsafe { 516 unsafe {
@@ -531,6 +520,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
531 T::regs().cr1().modify(|w| w.set_tcie(true)); 520 T::regs().cr1().modify(|w| w.set_tcie(true));
532 } 521 }
533 } 522 }
523
524 remaining_len = remaining_len.saturating_sub(255);
534 Poll::Pending 525 Poll::Pending
535 }) 526 })
536 .await?; 527 .await?;
@@ -559,12 +550,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
559 RXDMA: crate::i2c::RxDma<T>, 550 RXDMA: crate::i2c::RxDma<T>,
560 { 551 {
561 let total_len = buffer.len(); 552 let total_len = buffer.len();
562 let completed_chunks = total_len / 255;
563 let total_chunks = if completed_chunks * 255 == total_len {
564 completed_chunks
565 } else {
566 completed_chunks + 1
567 };
568 553
569 let dma_transfer = unsafe { 554 let dma_transfer = unsafe {
570 let regs = T::regs(); 555 let regs = T::regs();
@@ -576,11 +561,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
576 561
577 let ch = &mut self.rx_dma; 562 let ch = &mut self.rx_dma;
578 let request = ch.request(); 563 let request = ch.request();
579 crate::dma::read(ch, request, src, buffer) 564 Transfer::new_read(ch, request, src, buffer, Default::default())
580 }; 565 };
581 566
582 let state = T::state(); 567 let state = T::state();
583 state.chunks_transferred.store(0, Ordering::Relaxed);
584 let mut remaining_len = total_len; 568 let mut remaining_len = total_len;
585 569
586 let on_drop = OnDrop::new(|| { 570 let on_drop = OnDrop::new(|| {
@@ -593,27 +577,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
593 } 577 }
594 }); 578 });
595 579
596 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
597 unsafe {
598 Self::master_read(
599 address,
600 total_len.min(255),
601 Stop::Software,
602 total_chunks != 1,
603 restart,
604 &check_timeout,
605 )?;
606 }
607
608 poll_fn(|cx| { 580 poll_fn(|cx| {
609 state.waker.register(cx.waker()); 581 state.waker.register(cx.waker());
610 let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
611 582
612 if chunks_transferred == total_chunks { 583 let isr = unsafe { T::regs().isr().read() };
584 if remaining_len == total_len {
585 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
586 unsafe {
587 Self::master_read(
588 address,
589 total_len.min(255),
590 Stop::Software,
591 total_len > 255,
592 restart,
593 &check_timeout,
594 )?;
595 }
596 } else if !(isr.tcr() || isr.tc()) {
597 // poll_fn was woken without an interrupt present
598 return Poll::Pending;
599 } else if remaining_len == 0 {
613 return Poll::Ready(Ok(())); 600 return Poll::Ready(Ok(()));
614 } else if chunks_transferred != 0 { 601 } else {
615 remaining_len = remaining_len.saturating_sub(255); 602 let last_piece = remaining_len <= 255;
616 let last_piece = chunks_transferred + 1 == total_chunks;
617 603
618 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers 604 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
619 unsafe { 605 unsafe {
@@ -623,6 +609,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
623 T::regs().cr1().modify(|w| w.set_tcie(true)); 609 T::regs().cr1().modify(|w| w.set_tcie(true));
624 } 610 }
625 } 611 }
612
613 remaining_len = remaining_len.saturating_sub(255);
626 Poll::Pending 614 Poll::Pending
627 }) 615 })
628 .await?; 616 .await?;
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index d4d7155bd..bbde2da57 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -49,6 +49,8 @@ pub mod pwm;
49pub mod qspi; 49pub mod qspi;
50#[cfg(rng)] 50#[cfg(rng)]
51pub mod rng; 51pub mod rng;
52#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))]
53pub mod rtc;
52#[cfg(sdmmc)] 54#[cfg(sdmmc)]
53pub mod sdmmc; 55pub mod sdmmc;
54#[cfg(spi)] 56#[cfg(spi)]
@@ -76,7 +78,6 @@ pub(crate) mod _generated {
76// Reexports 78// Reexports
77pub use _generated::{peripherals, Peripherals}; 79pub use _generated::{peripherals, Peripherals};
78pub use embassy_cortex_m::executor; 80pub use embassy_cortex_m::executor;
79#[cfg(any(dma, bdma))]
80use embassy_cortex_m::interrupt::Priority; 81use embassy_cortex_m::interrupt::Priority;
81pub use embassy_cortex_m::interrupt::_export::interrupt; 82pub use embassy_cortex_m::interrupt::_export::interrupt;
82pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 83pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
@@ -94,6 +95,8 @@ pub struct Config {
94 pub bdma_interrupt_priority: Priority, 95 pub bdma_interrupt_priority: Priority,
95 #[cfg(dma)] 96 #[cfg(dma)]
96 pub dma_interrupt_priority: Priority, 97 pub dma_interrupt_priority: Priority,
98 #[cfg(gpdma)]
99 pub gpdma_interrupt_priority: Priority,
97} 100}
98 101
99impl Default for Config { 102impl Default for Config {
@@ -106,6 +109,8 @@ impl Default for Config {
106 bdma_interrupt_priority: Priority::P0, 109 bdma_interrupt_priority: Priority::P0,
107 #[cfg(dma)] 110 #[cfg(dma)]
108 dma_interrupt_priority: Priority::P0, 111 dma_interrupt_priority: Priority::P0,
112 #[cfg(gpdma)]
113 gpdma_interrupt_priority: Priority::P0,
109 } 114 }
110 } 115 }
111} 116}
@@ -149,6 +154,8 @@ pub fn init(config: Config) -> Peripherals {
149 config.bdma_interrupt_priority, 154 config.bdma_interrupt_priority,
150 #[cfg(dma)] 155 #[cfg(dma)]
151 config.dma_interrupt_priority, 156 config.dma_interrupt_priority,
157 #[cfg(gpdma)]
158 config.gpdma_interrupt_priority,
152 ); 159 );
153 #[cfg(feature = "exti")] 160 #[cfg(feature = "exti")]
154 exti::init(); 161 exti::init();
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
index f33319620..c3126b37f 100644
--- a/embassy-stm32/src/qspi/mod.rs
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -5,7 +5,7 @@ pub mod enums;
5use embassy_hal_common::{into_ref, PeripheralRef}; 5use embassy_hal_common::{into_ref, PeripheralRef};
6use enums::*; 6use enums::*;
7 7
8use crate::dma::TransferOptions; 8use crate::dma::Transfer;
9use crate::gpio::sealed::AFType; 9use crate::gpio::sealed::AFType;
10use crate::gpio::AnyPin; 10use crate::gpio::AnyPin;
11use crate::pac::quadspi::Quadspi as Regs; 11use crate::pac::quadspi::Quadspi as Regs;
@@ -230,9 +230,6 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
230 unsafe { 230 unsafe {
231 self.setup_transaction(QspiMode::IndirectWrite, &transaction); 231 self.setup_transaction(QspiMode::IndirectWrite, &transaction);
232 232
233 let request = self.dma.request();
234 let options = TransferOptions::default();
235
236 T::REGS.ccr().modify(|v| { 233 T::REGS.ccr().modify(|v| {
237 v.set_fmode(QspiMode::IndirectRead.into()); 234 v.set_fmode(QspiMode::IndirectRead.into());
238 }); 235 });
@@ -241,12 +238,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
241 v.set_address(current_ar); 238 v.set_address(current_ar);
242 }); 239 });
243 240
244 self.dma 241 let request = self.dma.request();
245 .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options); 242 let transfer = Transfer::new_read(
243 &mut self.dma,
244 request,
245 T::REGS.dr().ptr() as *mut u8,
246 buf,
247 Default::default(),
248 );
246 249
247 T::REGS.cr().modify(|v| v.set_dmaen(true)); 250 T::REGS.cr().modify(|v| v.set_dmaen(true));
248 251
249 while self.dma.is_running() {} 252 transfer.blocking_wait();
250 } 253 }
251 } 254 }
252 255
@@ -257,19 +260,22 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
257 unsafe { 260 unsafe {
258 self.setup_transaction(QspiMode::IndirectWrite, &transaction); 261 self.setup_transaction(QspiMode::IndirectWrite, &transaction);
259 262
260 let request = self.dma.request();
261 let options = TransferOptions::default();
262
263 T::REGS.ccr().modify(|v| { 263 T::REGS.ccr().modify(|v| {
264 v.set_fmode(QspiMode::IndirectWrite.into()); 264 v.set_fmode(QspiMode::IndirectWrite.into());
265 }); 265 });
266 266
267 self.dma 267 let request = self.dma.request();
268 .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options); 268 let transfer = Transfer::new_write(
269 &mut self.dma,
270 request,
271 buf,
272 T::REGS.dr().ptr() as *mut u8,
273 Default::default(),
274 );
269 275
270 T::REGS.cr().modify(|v| v.set_dmaen(true)); 276 T::REGS.cr().modify(|v| v.set_dmaen(true));
271 277
272 while self.dma.is_running() {} 278 transfer.blocking_wait();
273 } 279 }
274 } 280 }
275 281
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index 2a17eb9b0..e0929ca49 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -29,10 +29,66 @@ pub struct Config {
29 pub pclk1: Option<Hertz>, 29 pub pclk1: Option<Hertz>,
30 pub pclk2: Option<Hertz>, 30 pub pclk2: Option<Hertz>,
31 31
32 #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
33 pub plli2s: Option<Hertz>,
34
32 pub pll48: bool, 35 pub pll48: bool,
33} 36}
34 37
35unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults { 38#[cfg(stm32f410)]
39unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
40 None
41}
42
43// Not currently implemented, but will be in the future
44#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
45unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
46 None
47}
48
49#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
50unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
51 let min_div = 2;
52 let max_div = 7;
53 let target = match plli2s {
54 Some(target) => target,
55 None => return None,
56 };
57
58 // We loop through the possible divider values to find the best configuration. Looping
59 // through all possible "N" values would result in more iterations.
60 let (n, outdiv, output, _error) = (min_div..=max_div)
61 .filter_map(|outdiv| {
62 let target_vco_out = match target.checked_mul(outdiv) {
63 Some(x) => x,
64 None => return None,
65 };
66 let n = (target_vco_out + (vco_in >> 1)) / vco_in;
67 let vco_out = vco_in * n;
68 if !(100_000_000..=432_000_000).contains(&vco_out) {
69 return None;
70 }
71 let output = vco_out / outdiv;
72 let error = (output as i32 - target as i32).unsigned_abs();
73 Some((n, outdiv, output, error))
74 })
75 .min_by_key(|(_, _, _, error)| *error)?;
76
77 RCC.plli2scfgr().modify(|w| {
78 w.set_plli2sn(n as u16);
79 w.set_plli2sr(outdiv as u8);
80 });
81
82 Some(output)
83}
84
85unsafe fn setup_pll(
86 pllsrcclk: u32,
87 use_hse: bool,
88 pllsysclk: Option<u32>,
89 plli2s: Option<u32>,
90 pll48clk: bool,
91) -> PllResults {
36 use crate::pac::rcc::vals::{Pllp, Pllsrc}; 92 use crate::pac::rcc::vals::{Pllp, Pllsrc};
37 93
38 let sysclk = pllsysclk.unwrap_or(pllsrcclk); 94 let sysclk = pllsysclk.unwrap_or(pllsrcclk);
@@ -43,6 +99,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
43 use_pll: false, 99 use_pll: false,
44 pllsysclk: None, 100 pllsysclk: None,
45 pll48clk: None, 101 pll48clk: None,
102 plli2sclk: None,
46 }; 103 };
47 } 104 }
48 // Input divisor from PLL source clock, must result to frequency in 105 // 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
101 use_pll: true, 158 use_pll: true,
102 pllsysclk: Some(real_pllsysclk), 159 pllsysclk: Some(real_pllsysclk),
103 pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, 160 pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
161 plli2sclk: setup_i2s_pll(vco_in, plli2s),
104 } 162 }
105} 163}
106 164
@@ -286,6 +344,10 @@ pub(crate) unsafe fn init(config: Config) {
286 pllsrcclk, 344 pllsrcclk,
287 config.hse.is_some(), 345 config.hse.is_some(),
288 if sysclk_on_pll { Some(sysclk) } else { None }, 346 if sysclk_on_pll { Some(sysclk) } else { None },
347 #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
348 config.plli2s.map(|i2s| i2s.0),
349 #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
350 None,
289 config.pll48, 351 config.pll48,
290 ); 352 );
291 353
@@ -376,6 +438,13 @@ pub(crate) unsafe fn init(config: Config) {
376 while !RCC.cr().read().pllrdy() {} 438 while !RCC.cr().read().pllrdy() {}
377 } 439 }
378 440
441 #[cfg(not(stm32f410))]
442 if plls.plli2sclk.is_some() {
443 RCC.cr().modify(|w| w.set_plli2son(true));
444
445 while !RCC.cr().read().plli2srdy() {}
446 }
447
379 RCC.cfgr().modify(|w| { 448 RCC.cfgr().modify(|w| {
380 w.set_ppre2(Ppre(ppre2_bits)); 449 w.set_ppre2(Ppre(ppre2_bits));
381 w.set_ppre1(Ppre(ppre1_bits)); 450 w.set_ppre1(Ppre(ppre1_bits));
@@ -409,6 +478,12 @@ pub(crate) unsafe fn init(config: Config) {
409 ahb3: Hertz(hclk), 478 ahb3: Hertz(hclk),
410 479
411 pll48: plls.pll48clk.map(Hertz), 480 pll48: plls.pll48clk.map(Hertz),
481
482 #[cfg(not(stm32f410))]
483 plli2s: plls.plli2sclk.map(Hertz),
484
485 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
486 pllsai: None,
412 }); 487 });
413} 488}
414 489
@@ -416,6 +491,8 @@ struct PllResults {
416 use_pll: bool, 491 use_pll: bool,
417 pllsysclk: Option<u32>, 492 pllsysclk: Option<u32>,
418 pll48clk: Option<u32>, 493 pll48clk: Option<u32>,
494 #[allow(dead_code)]
495 plli2sclk: Option<u32>,
419} 496}
420 497
421mod max { 498mod max {
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index d6a31f17b..d6816d6a8 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -60,6 +60,12 @@ pub struct Clocks {
60 #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] 60 #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
61 pub pll48: Option<Hertz>, 61 pub pll48: Option<Hertz>,
62 62
63 #[cfg(all(rcc_f4, not(stm32f410)))]
64 pub plli2s: Option<Hertz>,
65
66 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
67 pub pllsai: Option<Hertz>,
68
63 #[cfg(stm32f1)] 69 #[cfg(stm32f1)]
64 pub adc: Hertz, 70 pub adc: Hertz,
65 71
diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs
new file mode 100644
index 000000000..6274c1e05
--- /dev/null
+++ b/embassy-stm32/src/rtc/datetime.rs
@@ -0,0 +1,203 @@
1#[cfg(feature = "chrono")]
2use core::convert::From;
3
4#[cfg(feature = "chrono")]
5use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
6
7use super::byte_to_bcd2;
8use crate::pac::rtc::Rtc;
9
10/// Errors regarding the [`DateTime`] struct.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum Error {
13 /// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
14 InvalidYear,
15 /// The [DateTime] contains an invalid month value. Must be between `1..=12`.
16 InvalidMonth,
17 /// The [DateTime] contains an invalid day value. Must be between `1..=31`.
18 InvalidDay,
19 /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
20 InvalidDayOfWeek(
21 /// The value of the DayOfWeek that was given.
22 u8,
23 ),
24 /// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
25 InvalidHour,
26 /// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
27 InvalidMinute,
28 /// The [DateTime] contains an invalid second value. Must be between `0..=59`.
29 InvalidSecond,
30}
31
32/// Structure containing date and time information
33pub struct DateTime {
34 /// 0..4095
35 pub year: u16,
36 /// 1..12, 1 is January
37 pub month: u8,
38 /// 1..28,29,30,31 depending on month
39 pub day: u8,
40 ///
41 pub day_of_week: DayOfWeek,
42 /// 0..23
43 pub hour: u8,
44 /// 0..59
45 pub minute: u8,
46 /// 0..59
47 pub second: u8,
48}
49
50#[cfg(feature = "chrono")]
51impl From<chrono::NaiveDateTime> for DateTime {
52 fn from(date_time: chrono::NaiveDateTime) -> Self {
53 Self {
54 year: (date_time.year() - 1970) as u16,
55 month: date_time.month() as u8,
56 day: date_time.day() as u8,
57 day_of_week: date_time.weekday().into(),
58 hour: date_time.hour() as u8,
59 minute: date_time.minute() as u8,
60 second: date_time.second() as u8,
61 }
62 }
63}
64
65#[cfg(feature = "chrono")]
66impl From<DateTime> for chrono::NaiveDateTime {
67 fn from(date_time: DateTime) -> Self {
68 NaiveDate::from_ymd_opt(
69 (date_time.year + 1970) as i32,
70 date_time.month as u32,
71 date_time.day as u32,
72 )
73 .unwrap()
74 .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32)
75 .unwrap()
76 }
77}
78
79/// A day of the week
80#[repr(u8)]
81#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
82#[allow(missing_docs)]
83pub enum DayOfWeek {
84 Monday = 0,
85 Tuesday = 1,
86 Wednesday = 2,
87 Thursday = 3,
88 Friday = 4,
89 Saturday = 5,
90 Sunday = 6,
91}
92
93#[cfg(feature = "chrono")]
94impl From<chrono::Weekday> for DayOfWeek {
95 fn from(weekday: Weekday) -> Self {
96 day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()
97 }
98}
99
100#[cfg(feature = "chrono")]
101impl From<DayOfWeek> for chrono::Weekday {
102 fn from(weekday: DayOfWeek) -> Self {
103 match weekday {
104 DayOfWeek::Monday => Weekday::Mon,
105 DayOfWeek::Tuesday => Weekday::Tue,
106 DayOfWeek::Wednesday => Weekday::Wed,
107 DayOfWeek::Thursday => Weekday::Thu,
108 DayOfWeek::Friday => Weekday::Fri,
109 DayOfWeek::Saturday => Weekday::Sat,
110 DayOfWeek::Sunday => Weekday::Sun,
111 }
112 }
113}
114
115fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
116 Ok(match v {
117 0 => DayOfWeek::Monday,
118 1 => DayOfWeek::Tuesday,
119 2 => DayOfWeek::Wednesday,
120 3 => DayOfWeek::Thursday,
121 4 => DayOfWeek::Friday,
122 5 => DayOfWeek::Saturday,
123 6 => DayOfWeek::Sunday,
124 x => return Err(Error::InvalidDayOfWeek(x)),
125 })
126}
127
128pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
129 dotw as u8
130}
131
132pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
133 if dt.year > 4095 {
134 Err(Error::InvalidYear)
135 } else if dt.month < 1 || dt.month > 12 {
136 Err(Error::InvalidMonth)
137 } else if dt.day < 1 || dt.day > 31 {
138 Err(Error::InvalidDay)
139 } else if dt.hour > 23 {
140 Err(Error::InvalidHour)
141 } else if dt.minute > 59 {
142 Err(Error::InvalidMinute)
143 } else if dt.second > 59 {
144 Err(Error::InvalidSecond)
145 } else {
146 Ok(())
147 }
148}
149
150pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
151 let (ht, hu) = byte_to_bcd2(t.hour as u8);
152 let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
153 let (st, su) = byte_to_bcd2(t.second as u8);
154
155 let (dt, du) = byte_to_bcd2(t.day as u8);
156 let (mt, mu) = byte_to_bcd2(t.month as u8);
157 let yr = t.year as u16;
158 let yr_offset = (yr - 1970_u16) as u8;
159 let (yt, yu) = byte_to_bcd2(yr_offset);
160
161 unsafe {
162 rtc.tr().write(|w| {
163 w.set_ht(ht);
164 w.set_hu(hu);
165 w.set_mnt(mnt);
166 w.set_mnu(mnu);
167 w.set_st(st);
168 w.set_su(su);
169 w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
170 });
171
172 rtc.dr().write(|w| {
173 w.set_dt(dt);
174 w.set_du(du);
175 w.set_mt(mt > 0);
176 w.set_mu(mu);
177 w.set_yt(yt);
178 w.set_yu(yu);
179 w.set_wdu(day_of_week_to_u8(t.day_of_week));
180 });
181 }
182}
183
184pub(super) fn datetime(
185 year: u16,
186 month: u8,
187 day: u8,
188 day_of_week: u8,
189 hour: u8,
190 minute: u8,
191 second: u8,
192) -> Result<DateTime, Error> {
193 let day_of_week = day_of_week_from_u8(day_of_week)?;
194 Ok(DateTime {
195 year,
196 month,
197 day,
198 day_of_week,
199 hour,
200 minute,
201 second,
202 })
203}
diff --git a/embassy-stm32/src/rtc/datetime_chrono.rs b/embassy-stm32/src/rtc/datetime_chrono.rs
new file mode 100644
index 000000000..b46316cc9
--- /dev/null
+++ b/embassy-stm32/src/rtc/datetime_chrono.rs
@@ -0,0 +1,85 @@
1use chrono::{Datelike, Timelike};
2
3use super::byte_to_bcd2;
4use crate::pac::rtc::Rtc;
5
6/// Alias for [`chrono::NaiveDateTime`]
7pub type DateTime = chrono::NaiveDateTime;
8/// Alias for [`chrono::Weekday`]
9pub type DayOfWeek = chrono::Weekday;
10
11/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
12///
13/// [`DateTimeFilter`]: struct.DateTimeFilter.html
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum Error {
16 /// The [DateTime] has an invalid year. The year must be between 0 and 4095.
17 InvalidYear,
18 /// The [DateTime] contains an invalid date.
19 InvalidDate,
20 /// The [DateTime] contains an invalid time.
21 InvalidTime,
22}
23
24pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
25 dotw.num_days_from_monday() as u8
26}
27
28pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
29 if dt.year() < 0 || dt.year() > 4095 {
30 // rp2040 can't hold these years
31 Err(Error::InvalidYear)
32 } else {
33 // The rest of the chrono date is assumed to be valid
34 Ok(())
35 }
36}
37
38pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
39 let (ht, hu) = byte_to_bcd2(t.hour() as u8);
40 let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
41 let (st, su) = byte_to_bcd2(t.second() as u8);
42
43 let (dt, du) = byte_to_bcd2(t.day() as u8);
44 let (mt, mu) = byte_to_bcd2(t.month() as u8);
45 let yr = t.year() as u16;
46 let yr_offset = (yr - 1970_u16) as u8;
47 let (yt, yu) = byte_to_bcd2(yr_offset);
48
49 unsafe {
50 rtc.tr().write(|w| {
51 w.set_ht(ht);
52 w.set_hu(hu);
53 w.set_mnt(mnt);
54 w.set_mnu(mnu);
55 w.set_st(st);
56 w.set_su(su);
57 w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
58 });
59
60 rtc.dr().write(|w| {
61 w.set_dt(dt);
62 w.set_du(du);
63 w.set_mt(mt > 0);
64 w.set_mu(mu);
65 w.set_yt(yt);
66 w.set_yu(yu);
67 w.set_wdu(day_of_week_to_u8(t.weekday()));
68 });
69 }
70}
71
72pub(super) fn datetime(
73 year: u16,
74 month: u8,
75 day: u8,
76 _day_of_week: u8,
77 hour: u8,
78 minute: u8,
79 second: u8,
80) -> Result<DateTime, Error> {
81 let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into())
82 .ok_or(Error::InvalidDate)?;
83 let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?;
84 Ok(DateTime::new(date, time))
85}
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
new file mode 100644
index 000000000..170783b2d
--- /dev/null
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -0,0 +1,235 @@
1//! RTC peripheral abstraction
2use core::marker::PhantomData;
3mod datetime;
4
5pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
6
7/// refer to AN4759 to compare features of RTC2 and RTC3
8#[cfg_attr(any(rtc_v1), path = "v1.rs")]
9#[cfg_attr(
10 any(
11 rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
12 ),
13 path = "v2/mod.rs"
14)]
15#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
16mod versions;
17use embassy_hal_common::Peripheral;
18pub use versions::*;
19
20/// Errors that can occur on methods on [RtcClock]
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum RtcError {
23 /// An invalid DateTime was given or stored on the hardware.
24 InvalidDateTime(DateTimeError),
25
26 /// The RTC clock is not running
27 NotRunning,
28}
29
30/// RTC Abstraction
31pub struct Rtc<'d, T: Instance> {
32 phantom: PhantomData<&'d mut T>,
33 rtc_config: RtcConfig,
34}
35
36#[derive(Copy, Clone, Debug, PartialEq)]
37#[repr(u8)]
38pub enum RtcClockSource {
39 /// 00: No clock
40 NoClock = 0b00,
41 /// 01: LSE oscillator clock used as RTC clock
42 LSE = 0b01,
43 /// 10: LSI oscillator clock used as RTC clock
44 LSI = 0b10,
45 /// 11: HSE oscillator clock divided by 32 used as RTC clock
46 HSE = 0b11,
47}
48
49#[derive(Copy, Clone, PartialEq)]
50pub struct RtcConfig {
51 /// RTC clock source
52 clock_config: RtcClockSource,
53 /// Asynchronous prescaler factor
54 /// This is the asynchronous division factor:
55 /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
56 /// ck_apre drives the subsecond register
57 async_prescaler: u8,
58 /// Synchronous prescaler factor
59 /// This is the synchronous division factor:
60 /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
61 /// ck_spre must be 1Hz
62 sync_prescaler: u16,
63}
64
65impl Default for RtcConfig {
66 /// LSI with prescalers assuming 32.768 kHz.
67 /// Raw sub-seconds in 1/256.
68 fn default() -> Self {
69 RtcConfig {
70 clock_config: RtcClockSource::LSI,
71 async_prescaler: 127,
72 sync_prescaler: 255,
73 }
74 }
75}
76
77impl RtcConfig {
78 /// Sets the clock source of RTC config
79 pub fn clock_config(mut self, cfg: RtcClockSource) -> Self {
80 self.clock_config = cfg;
81 self
82 }
83
84 /// Set the asynchronous prescaler of RTC config
85 pub fn async_prescaler(mut self, prescaler: u8) -> Self {
86 self.async_prescaler = prescaler;
87 self
88 }
89
90 /// Set the synchronous prescaler of RTC config
91 pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
92 self.sync_prescaler = prescaler;
93 self
94 }
95}
96
97#[derive(Copy, Clone, Debug, PartialEq)]
98#[repr(u8)]
99pub enum RtcCalibrationCyclePeriod {
100 /// 8-second calibration period
101 Seconds8,
102 /// 16-second calibration period
103 Seconds16,
104 /// 32-second calibration period
105 Seconds32,
106}
107
108impl Default for RtcCalibrationCyclePeriod {
109 fn default() -> Self {
110 RtcCalibrationCyclePeriod::Seconds32
111 }
112}
113
114impl<'d, T: Instance> Rtc<'d, T> {
115 pub fn new(_rtc: impl Peripheral<P = T> + 'd, rtc_config: RtcConfig) -> Self {
116 unsafe { enable_peripheral_clk() };
117
118 let mut rtc_struct = Self {
119 phantom: PhantomData,
120 rtc_config,
121 };
122
123 rtc_struct.apply_config(rtc_config);
124
125 rtc_struct
126 }
127
128 /// Set the datetime to a new value.
129 ///
130 /// # Errors
131 ///
132 /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
133 pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
134 self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
135 self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
136
137 Ok(())
138 }
139
140 /// Return the current datetime.
141 ///
142 /// # Errors
143 ///
144 /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
145 pub fn now(&self) -> Result<DateTime, RtcError> {
146 let r = T::regs();
147 unsafe {
148 let tr = r.tr().read();
149 let second = bcd2_to_byte((tr.st(), tr.su()));
150 let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
151 let hour = bcd2_to_byte((tr.ht(), tr.hu()));
152 // Reading either RTC_SSR or RTC_TR locks the values in the higher-order
153 // calendar shadow registers until RTC_DR is read.
154 let dr = r.dr().read();
155
156 let weekday = dr.wdu();
157 let day = bcd2_to_byte((dr.dt(), dr.du()));
158 let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
159 let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
160
161 self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
162 }
163 }
164
165 /// Check if daylight savings time is active.
166 pub fn get_daylight_savings(&self) -> bool {
167 let cr = unsafe { T::regs().cr().read() };
168 cr.bkp()
169 }
170
171 /// Enable/disable daylight savings time.
172 pub fn set_daylight_savings(&mut self, daylight_savings: bool) {
173 self.write(true, |rtc| {
174 unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) };
175 })
176 }
177
178 pub fn get_config(&self) -> RtcConfig {
179 self.rtc_config
180 }
181
182 pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT;
183
184 /// Read content of the backup register.
185 ///
186 /// The registers retain their values during wakes from standby mode or system resets. They also
187 /// retain their value when Vdd is switched off as long as V_BAT is powered.
188 pub fn read_backup_register(&self, register: usize) -> Option<u32> {
189 read_backup_register(&T::regs(), register)
190 }
191
192 /// Set content of the backup register.
193 ///
194 /// The registers retain their values during wakes from standby mode or system resets. They also
195 /// retain their value when Vdd is switched off as long as V_BAT is powered.
196 pub fn write_backup_register(&self, register: usize, value: u32) {
197 write_backup_register(&T::regs(), register, value)
198 }
199}
200
201pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {
202 let mut bcd_high: u8 = 0;
203 let mut value = byte;
204
205 while value >= 10 {
206 bcd_high += 1;
207 value -= 10;
208 }
209
210 (bcd_high, ((bcd_high << 4) | value) as u8)
211}
212
213pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {
214 let value = bcd.1 | bcd.0 << 4;
215
216 let tmp = ((value & 0xF0) >> 0x4) * 10;
217
218 tmp + (value & 0x0F)
219}
220
221pub(crate) mod sealed {
222 pub trait Instance {
223 fn regs() -> crate::pac::rtc::Rtc;
224 }
225}
226
227pub trait Instance: sealed::Instance + 'static {}
228
229impl sealed::Instance for crate::peripherals::RTC {
230 fn regs() -> crate::pac::rtc::Rtc {
231 crate::pac::RTC
232 }
233}
234
235impl Instance for crate::peripherals::RTC {}
diff --git a/embassy-stm32/src/rtc/v2/mod.rs b/embassy-stm32/src/rtc/v2/mod.rs
new file mode 100644
index 000000000..296adae89
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/mod.rs
@@ -0,0 +1,171 @@
1use stm32_metapac::rtc::vals::{Init, Osel, Pol};
2
3use super::{Instance, RtcConfig};
4use crate::pac::rtc::Rtc;
5
6#[cfg_attr(rtc_v2f0, path = "v2f0.rs")]
7#[cfg_attr(rtc_v2f2, path = "v2f2.rs")]
8#[cfg_attr(rtc_v2f3, path = "v2f3.rs")]
9#[cfg_attr(rtc_v2f4, path = "v2f4.rs")]
10#[cfg_attr(rtc_v2f7, path = "v2f7.rs")]
11#[cfg_attr(rtc_v2h7, path = "v2h7.rs")]
12#[cfg_attr(rtc_v2l0, path = "v2l0.rs")]
13#[cfg_attr(rtc_v2l1, path = "v2l1.rs")]
14#[cfg_attr(rtc_v2l4, path = "v2l4.rs")]
15#[cfg_attr(rtc_v2wb, path = "v2wb.rs")]
16mod family;
17
18pub use family::*;
19
20impl<'d, T: Instance> super::Rtc<'d, T> {
21 /// Applies the RTC config
22 /// It this changes the RTC clock source the time will be reset
23 pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
24 // Unlock the backup domain
25 unsafe {
26 unlock_backup_domain(rtc_config.clock_config as u8);
27 }
28
29 self.write(true, |rtc| unsafe {
30 rtc.cr().modify(|w| {
31 #[cfg(rtc_v2f2)]
32 w.set_fmt(false);
33 #[cfg(not(rtc_v2f2))]
34 w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
35 w.set_osel(Osel::DISABLED);
36 w.set_pol(Pol::HIGH);
37 });
38
39 rtc.prer().modify(|w| {
40 w.set_prediv_s(rtc_config.sync_prescaler);
41 w.set_prediv_a(rtc_config.async_prescaler);
42 });
43 });
44
45 self.rtc_config = rtc_config;
46 }
47
48 /// Calibrate the clock drift.
49 ///
50 /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
51 ///
52 /// ### Note
53 ///
54 /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
55 /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
56 #[cfg(not(rtc_v2f2))]
57 pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {
58 const RTC_CALR_MIN_PPM: f32 = -487.1;
59 const RTC_CALR_MAX_PPM: f32 = 488.5;
60 const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
61
62 if clock_drift < RTC_CALR_MIN_PPM {
63 clock_drift = RTC_CALR_MIN_PPM;
64 } else if clock_drift > RTC_CALR_MAX_PPM {
65 clock_drift = RTC_CALR_MAX_PPM;
66 }
67
68 clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
69
70 self.write(false, |rtc| {
71 unsafe {
72 rtc.calr().write(|w| {
73 match period {
74 super::RtcCalibrationCyclePeriod::Seconds8 => {
75 w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
76 }
77 super::RtcCalibrationCyclePeriod::Seconds16 => {
78 w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
79 }
80 super::RtcCalibrationCyclePeriod::Seconds32 => {
81 // Set neither `calw8` nor `calw16` to use 32 seconds
82 }
83 }
84
85 // Extra pulses during calibration cycle period: CALP * 512 - CALM
86 //
87 // CALP sets whether pulses are added or omitted.
88 //
89 // CALM contains how many pulses (out of 512) are masked in a
90 // given calibration cycle period.
91 if clock_drift > 0.0 {
92 // Maximum (about 512.2) rounds to 512.
93 clock_drift += 0.5;
94
95 // When the offset is positive (0 to 512), the opposite of
96 // the offset (512 - offset) is masked, i.e. for the
97 // maximum offset (512), 0 pulses are masked.
98 w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
99 w.set_calm(512 - clock_drift as u16);
100 } else {
101 // Minimum (about -510.7) rounds to -511.
102 clock_drift -= 0.5;
103
104 // When the offset is negative or zero (-511 to 0),
105 // the absolute offset is masked, i.e. for the minimum
106 // offset (-511), 511 pulses are masked.
107 w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
108 w.set_calm((clock_drift * -1.0) as u16);
109 }
110 });
111 }
112 })
113 }
114
115 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
116 where
117 F: FnOnce(&crate::pac::rtc::Rtc) -> R,
118 {
119 let r = T::regs();
120 // Disable write protection.
121 // This is safe, as we're only writin the correct and expected values.
122 unsafe {
123 r.wpr().write(|w| w.set_key(0xca));
124 r.wpr().write(|w| w.set_key(0x53));
125
126 // true if initf bit indicates RTC peripheral is in init mode
127 if init_mode && !r.isr().read().initf() {
128 // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
129 r.isr().modify(|w| w.set_init(Init::INITMODE));
130 // wait till init state entered
131 // ~2 RTCCLK cycles
132 while !r.isr().read().initf() {}
133 }
134 }
135
136 let result = f(&r);
137
138 unsafe {
139 if init_mode {
140 r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
141 }
142
143 // Re-enable write protection.
144 // This is safe, as the field accepts the full range of 8-bit values.
145 r.wpr().write(|w| w.set_key(0xff));
146 }
147 result
148 }
149}
150
151/// Read content of the backup register.
152///
153/// The registers retain their values during wakes from standby mode or system resets. They also
154/// retain their value when Vdd is switched off as long as V_BAT is powered.
155pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
156 if register < BACKUP_REGISTER_COUNT {
157 Some(unsafe { rtc.bkpr(register).read().bkp() })
158 } else {
159 None
160 }
161}
162
163/// Set content of the backup register.
164///
165/// The registers retain their values during wakes from standby mode or system resets. They also
166/// retain their value when Vdd is switched off as long as V_BAT is powered.
167pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
168 if register < BACKUP_REGISTER_COUNT {
169 unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) }
170 }
171}
diff --git a/embassy-stm32/src/rtc/v2/v2f0.rs b/embassy-stm32/src/rtc/v2/v2f0.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f0.rs
@@ -0,0 +1,41 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 // Restore bcdr
25 w.set_lscosel(reg.lscosel());
26 w.set_lscoen(reg.lscoen());
27
28 w.set_lseon(reg.lseon());
29 w.set_lsedrv(reg.lsedrv());
30 w.set_lsebyp(reg.lsebyp());
31 });
32 }
33}
34
35pub(crate) unsafe fn enable_peripheral_clk() {
36 // enable peripheral clock for communication
37 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
38
39 // read to allow the pwr clock to enable
40 crate::pac::PWR.cr1().read();
41}
diff --git a/embassy-stm32/src/rtc/v2/v2f2.rs b/embassy-stm32/src/rtc/v2/v2f2.rs
new file mode 100644
index 000000000..e041f3f4e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f2.rs
@@ -0,0 +1,31 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11
12 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
13 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
14
15 crate::pac::RCC.bdcr().modify(|w| {
16 // Reset
17 w.set_bdrst(false);
18
19 // Select RTC source
20 w.set_rtcsel(Rtcsel(clock_config));
21 w.set_rtcen(true);
22
23 w.set_lseon(reg.lseon());
24 w.set_lsebyp(reg.lsebyp());
25 });
26 }
27}
28
29pub(crate) unsafe fn enable_peripheral_clk() {
30 // Nothing to do
31}
diff --git a/embassy-stm32/src/rtc/v2/v2f3.rs b/embassy-stm32/src/rtc/v2/v2f3.rs
new file mode 100644
index 000000000..e041f3f4e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f3.rs
@@ -0,0 +1,31 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11
12 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
13 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
14
15 crate::pac::RCC.bdcr().modify(|w| {
16 // Reset
17 w.set_bdrst(false);
18
19 // Select RTC source
20 w.set_rtcsel(Rtcsel(clock_config));
21 w.set_rtcen(true);
22
23 w.set_lseon(reg.lseon());
24 w.set_lsebyp(reg.lsebyp());
25 });
26 }
27}
28
29pub(crate) unsafe fn enable_peripheral_clk() {
30 // Nothing to do
31}
diff --git a/embassy-stm32/src/rtc/v2/v2f4.rs b/embassy-stm32/src/rtc/v2/v2f4.rs
new file mode 100644
index 000000000..4dd21cae4
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f4.rs
@@ -0,0 +1,31 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11
12 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
13 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
14
15 crate::pac::RCC.bdcr().modify(|w| {
16 // Reset
17 w.set_bdrst(false);
18
19 // Select RTC source
20 w.set_rtcsel(Rtcsel(clock_config));
21 w.set_rtcen(true);
22
23 w.set_lseon(reg.lseon());
24 w.set_lsebyp(reg.lsebyp());
25 });
26 }
27}
28
29pub(crate) unsafe fn enable_peripheral_clk() {
30 // Nothing to do
31}
diff --git a/embassy-stm32/src/rtc/v2/v2f7.rs b/embassy-stm32/src/rtc/v2/v2f7.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f7.rs
@@ -0,0 +1,41 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 // Restore bcdr
25 w.set_lscosel(reg.lscosel());
26 w.set_lscoen(reg.lscoen());
27
28 w.set_lseon(reg.lseon());
29 w.set_lsedrv(reg.lsedrv());
30 w.set_lsebyp(reg.lsebyp());
31 });
32 }
33}
34
35pub(crate) unsafe fn enable_peripheral_clk() {
36 // enable peripheral clock for communication
37 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
38
39 // read to allow the pwr clock to enable
40 crate::pac::PWR.cr1().read();
41}
diff --git a/embassy-stm32/src/rtc/v2/v2h7.rs b/embassy-stm32/src/rtc/v2/v2h7.rs
new file mode 100644
index 000000000..f3b180683
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2h7.rs
@@ -0,0 +1,33 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 w.set_lseon(reg.lseon());
25 w.set_lsedrv(reg.lsedrv());
26 w.set_lsebyp(reg.lsebyp());
27 });
28 }
29}
30
31pub(crate) unsafe fn enable_peripheral_clk() {
32 // Nothing to do
33}
diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs
new file mode 100644
index 000000000..dbd3b0882
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l0.rs
@@ -0,0 +1,26 @@
1pub const BACKUP_REGISTER_COUNT: usize = 20;
2
3/// Unlock the backup domain
4pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
5 // TODO: Missing from PAC?
6 // crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
7 // while !crate::pac::PWR.cr().read().dbp() {}
8
9 let reg = crate::pac::RCC.csr().read();
10
11 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
12 crate::pac::RCC.csr().modify(|w| {
13 // Select RTC source
14 w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
15 w.set_rtcen(true);
16
17 w.set_lseon(reg.lseon());
18 w.set_lsedrv(reg.lsedrv());
19 w.set_lsebyp(reg.lsebyp());
20 });
21 }
22}
23
24pub(crate) unsafe fn enable_peripheral_clk() {
25 // Nothing to do
26}
diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs
new file mode 100644
index 000000000..1ac78b31a
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l1.rs
@@ -0,0 +1,24 @@
1pub const BACKUP_REGISTER_COUNT: usize = 20;
2
3/// Unlock the backup domain
4pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
5 crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
6 while !crate::pac::PWR.cr().read().dbp() {}
7
8 let reg = crate::pac::RCC.csr().read();
9
10 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
11 crate::pac::RCC.csr().modify(|w| {
12 // Select RTC source
13 w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
14 w.set_rtcen(true);
15
16 w.set_lseon(reg.lseon());
17 w.set_lsebyp(reg.lsebyp());
18 });
19 }
20}
21
22pub(crate) unsafe fn enable_peripheral_clk() {
23 // Nothing to do
24}
diff --git a/embassy-stm32/src/rtc/v2/v2l4.rs b/embassy-stm32/src/rtc/v2/v2l4.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l4.rs
@@ -0,0 +1,41 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 // Restore bcdr
25 w.set_lscosel(reg.lscosel());
26 w.set_lscoen(reg.lscoen());
27
28 w.set_lseon(reg.lseon());
29 w.set_lsedrv(reg.lsedrv());
30 w.set_lsebyp(reg.lsebyp());
31 });
32 }
33}
34
35pub(crate) unsafe fn enable_peripheral_clk() {
36 // enable peripheral clock for communication
37 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
38
39 // read to allow the pwr clock to enable
40 crate::pac::PWR.cr1().read();
41}
diff --git a/embassy-stm32/src/rtc/v2/v2wb.rs b/embassy-stm32/src/rtc/v2/v2wb.rs
new file mode 100644
index 000000000..98761fa60
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2wb.rs
@@ -0,0 +1,39 @@
1pub const BACKUP_REGISTER_COUNT: usize = 20;
2
3/// Unlock the backup domain
4pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
5 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
6 while !crate::pac::PWR.cr1().read().dbp() {}
7
8 let reg = crate::pac::RCC.bdcr().read();
9 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
10
11 if !reg.rtcen() || reg.rtcsel() != clock_config {
12 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
13
14 crate::pac::RCC.bdcr().modify(|w| {
15 // Reset
16 w.set_bdrst(false);
17
18 // Select RTC source
19 w.set_rtcsel(clock_config);
20 w.set_rtcen(true);
21
22 // Restore bcdr
23 w.set_lscosel(reg.lscosel());
24 w.set_lscoen(reg.lscoen());
25
26 w.set_lseon(reg.lseon());
27 w.set_lsedrv(reg.lsedrv());
28 w.set_lsebyp(reg.lsebyp());
29 });
30 }
31}
32
33pub(crate) unsafe fn enable_peripheral_clk() {
34 // enable peripheral clock for communication
35 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
36
37 // read to allow the pwr clock to enable
38 crate::pac::PWR.cr1().read();
39}
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
new file mode 100644
index 000000000..6998c48c2
--- /dev/null
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -0,0 +1,226 @@
1use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
2
3use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig};
4use crate::pac::rtc::Rtc;
5
6impl<'d, T: Instance> super::Rtc<'d, T> {
7 /// Applies the RTC config
8 /// It this changes the RTC clock source the time will be reset
9 pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
10 // Unlock the backup domain
11 unsafe {
12 #[cfg(feature = "stm32g0c1ve")]
13 {
14 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
15 while !crate::pac::PWR.cr1().read().dbp() {}
16 }
17
18 #[cfg(not(any(
19 feature = "stm32g0c1ve",
20 feature = "stm32g491re",
21 feature = "stm32u585zi",
22 feature = "stm32g473cc"
23 )))]
24 {
25 crate::pac::PWR
26 .cr1()
27 .modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED));
28 while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {}
29 }
30
31 let reg = crate::pac::RCC.bdcr().read();
32 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
33
34 let config_rtcsel = rtc_config.clock_config as u8;
35 #[cfg(not(any(
36 feature = "stm32wl54jc-cm0p",
37 feature = "stm32wle5ub",
38 feature = "stm32g0c1ve",
39 feature = "stm32wl55jc-cm4",
40 feature = "stm32wl55uc-cm4",
41 feature = "stm32g491re",
42 feature = "stm32g473cc",
43 feature = "stm32u585zi",
44 feature = "stm32wle5jb"
45 )))]
46 let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
47 #[cfg(feature = "stm32g0c1ve")]
48 let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel);
49
50 if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
51 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
52
53 crate::pac::RCC.bdcr().modify(|w| {
54 // Reset
55 w.set_bdrst(false);
56
57 // Select RTC source
58 w.set_rtcsel(config_rtcsel);
59
60 w.set_rtcen(true);
61
62 // Restore bcdr
63 w.set_lscosel(reg.lscosel());
64 w.set_lscoen(reg.lscoen());
65
66 w.set_lseon(reg.lseon());
67 w.set_lsedrv(reg.lsedrv());
68 w.set_lsebyp(reg.lsebyp());
69 });
70 }
71 }
72
73 self.write(true, |rtc| {
74 unsafe {
75 rtc.cr().modify(|w| {
76 w.set_fmt(Fmt::TWENTYFOURHOUR);
77 w.set_osel(Osel::DISABLED);
78 w.set_pol(Pol::HIGH);
79 });
80
81 rtc.prer().modify(|w| {
82 w.set_prediv_s(rtc_config.sync_prescaler);
83 w.set_prediv_a(rtc_config.async_prescaler);
84 });
85
86 // TODO: configuration for output pins
87 rtc.cr().modify(|w| {
88 w.set_out2en(false);
89 w.set_tampalrm_type(TampalrmType::PUSHPULL);
90 w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
91 });
92 }
93 });
94
95 self.rtc_config = rtc_config;
96 }
97
98 const RTC_CALR_MIN_PPM: f32 = -487.1;
99 const RTC_CALR_MAX_PPM: f32 = 488.5;
100 const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
101
102 /// Calibrate the clock drift.
103 ///
104 /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
105 ///
106 /// ### Note
107 ///
108 /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
109 /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
110 pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {
111 if clock_drift < Self::RTC_CALR_MIN_PPM {
112 clock_drift = Self::RTC_CALR_MIN_PPM;
113 } else if clock_drift > Self::RTC_CALR_MAX_PPM {
114 clock_drift = Self::RTC_CALR_MAX_PPM;
115 }
116
117 clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
118
119 self.write(false, |rtc| {
120 unsafe {
121 rtc.calr().write(|w| {
122 match period {
123 RtcCalibrationCyclePeriod::Seconds8 => {
124 w.set_calw8(Calw8::EIGHTSECONDS);
125 }
126 RtcCalibrationCyclePeriod::Seconds16 => {
127 w.set_calw16(Calw16::SIXTEENSECONDS);
128 }
129 RtcCalibrationCyclePeriod::Seconds32 => {
130 // Set neither `calw8` nor `calw16` to use 32 seconds
131 }
132 }
133
134 // Extra pulses during calibration cycle period: CALP * 512 - CALM
135 //
136 // CALP sets whether pulses are added or omitted.
137 //
138 // CALM contains how many pulses (out of 512) are masked in a
139 // given calibration cycle period.
140 if clock_drift > 0.0 {
141 // Maximum (about 512.2) rounds to 512.
142 clock_drift += 0.5;
143
144 // When the offset is positive (0 to 512), the opposite of
145 // the offset (512 - offset) is masked, i.e. for the
146 // maximum offset (512), 0 pulses are masked.
147 w.set_calp(Calp::INCREASEFREQ);
148 w.set_calm(512 - clock_drift as u16);
149 } else {
150 // Minimum (about -510.7) rounds to -511.
151 clock_drift -= 0.5;
152
153 // When the offset is negative or zero (-511 to 0),
154 // the absolute offset is masked, i.e. for the minimum
155 // offset (-511), 511 pulses are masked.
156 w.set_calp(Calp::NOCHANGE);
157 w.set_calm((clock_drift * -1.0) as u16);
158 }
159 });
160 }
161 })
162 }
163
164 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
165 where
166 F: FnOnce(&crate::pac::rtc::Rtc) -> R,
167 {
168 let r = T::regs();
169 // Disable write protection.
170 // This is safe, as we're only writin the correct and expected values.
171 unsafe {
172 r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
173 r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
174
175 if init_mode && !r.icsr().read().initf() {
176 r.icsr().modify(|w| w.set_init(Init::INITMODE));
177 // wait till init state entered
178 // ~2 RTCCLK cycles
179 while !r.icsr().read().initf() {}
180 }
181 }
182
183 let result = f(&r);
184
185 unsafe {
186 if init_mode {
187 r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
188 }
189
190 // Re-enable write protection.
191 // This is safe, as the field accepts the full range of 8-bit values.
192 r.wpr().write(|w| w.set_key(Key::ACTIVATE));
193 }
194 result
195 }
196}
197
198pub(super) unsafe fn enable_peripheral_clk() {
199 // Nothing to do
200}
201
202pub const BACKUP_REGISTER_COUNT: usize = 32;
203
204/// Read content of the backup register.
205///
206/// The registers retain their values during wakes from standby mode or system resets. They also
207/// retain their value when Vdd is switched off as long as V_BAT is powered.
208pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
209 if register < BACKUP_REGISTER_COUNT {
210 //Some(rtc.bkpr()[register].read().bits())
211 None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
212 } else {
213 None
214 }
215}
216
217/// Set content of the backup register.
218///
219/// The registers retain their values during wakes from standby mode or system resets. They also
220/// retain their value when Vdd is switched off as long as V_BAT is powered.
221pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
222 if register < BACKUP_REGISTER_COUNT {
223 // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
224 //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) }
225 }
226}
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 03d24dcb1..433f73d79 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -41,7 +41,7 @@ impl Default for Signalling {
41} 41}
42 42
43#[repr(align(4))] 43#[repr(align(4))]
44#[derive(Debug, Clone)] 44#[derive(Debug, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))] 45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46pub struct DataBlock(pub [u8; 512]); 46pub struct DataBlock(pub [u8; 512]);
47 47
@@ -61,7 +61,7 @@ impl DerefMut for DataBlock {
61 61
62/// Errors 62/// Errors
63#[non_exhaustive] 63#[non_exhaustive]
64#[derive(Debug, Copy, Clone)] 64#[derive(Debug, Copy, Clone, PartialEq, Eq)]
65#[cfg_attr(feature = "defmt", derive(defmt::Format))] 65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66pub enum Error { 66pub enum Error {
67 Timeout, 67 Timeout,
@@ -135,60 +135,71 @@ enum Response {
135 Long = 3, 135 Long = 3,
136} 136}
137 137
138cfg_if::cfg_if! { 138/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to
139 if #[cfg(sdmmc_v1)] { 139/// `sdmmc_ck` in Hertz.
140 /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to 140///
141 /// `sdmmc_ck` in Hertz. 141/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1),
142 /// 142/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency.
143 /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), 143#[cfg(sdmmc_v1)]
144 /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. 144fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> {
145 fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { 145 // sdmmc_v1 maximum clock is 50 MHz
146 // sdmmc_v1 maximum clock is 50 MHz 146 if sdmmc_ck > 50_000_000 {
147 if sdmmc_ck > 50_000_000 { 147 return Err(Error::BadClock);
148 return Err(Error::BadClock); 148 }
149 }
150 149
151 // bypass divisor 150 // bypass divisor
152 if ker_ck.0 <= sdmmc_ck { 151 if ker_ck.0 <= sdmmc_ck {
153 return Ok((true, 0, ker_ck)); 152 return Ok((true, 0, ker_ck));
154 } 153 }
155 154
156 // `ker_ck / sdmmc_ck` rounded up 155 // `ker_ck / sdmmc_ck` rounded up
157 let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { 156 let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck {
158 0 | 1 => Ok(0), 157 0 | 1 => Ok(0),
159 x @ 2..=258 => { 158 x @ 2..=258 => Ok((x - 2) as u8),
160 Ok((x - 2) as u8) 159 _ => Err(Error::BadClock),
161 } 160 }?;
162 _ => Err(Error::BadClock),
163 }?;
164 161
165 // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] 162 // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2]
166 let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); 163 let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2));
167 Ok((false, clk_div, clk_f)) 164 Ok((false, clk_div, clk_f))
168 } 165}
169 } else if #[cfg(sdmmc_v2)] { 166
170 /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to 167/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to
171 /// `sdmmc_ck` in Hertz. 168/// `sdmmc_ck` in Hertz.
172 /// 169///
173 /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), 170/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1),
174 /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. 171/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency.
175 fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { 172#[cfg(sdmmc_v2)]
176 // `ker_ck / sdmmc_ck` rounded up 173fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> {
177 match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { 174 // `ker_ck / sdmmc_ck` rounded up
178 0 | 1 => Ok((false, 0, ker_ck)), 175 match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck {
179 x @ 2..=2046 => { 176 0 | 1 => Ok((false, 0, ker_ck)),
180 // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] 177 x @ 2..=2046 => {
181 let clk_div = ((x + 1) / 2) as u16; 178 // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2]
182 let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); 179 let clk_div = ((x + 1) / 2) as u16;
183 180 let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2));
184 Ok((false, clk_div, clk)) 181
185 } 182 Ok((false, clk_div, clk))
186 _ => Err(Error::BadClock),
187 }
188 } 183 }
184 _ => Err(Error::BadClock),
189 } 185 }
190} 186}
191 187
188#[cfg(sdmmc_v1)]
189type Transfer<'a, C> = crate::dma::Transfer<'a, C>;
190#[cfg(sdmmc_v2)]
191type Transfer<'a, C> = core::marker::PhantomData<&'a mut C>;
192
193#[cfg(all(sdmmc_v1, dma))]
194const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {
195 pburst: crate::dma::Burst::Incr4,
196 mburst: crate::dma::Burst::Incr4,
197 flow_ctrl: crate::dma::FlowControl::Peripheral,
198 fifo_threshold: Some(crate::dma::FifoThreshold::Full),
199};
200#[cfg(all(sdmmc_v1, not(dma)))]
201const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {};
202
192/// SDMMC configuration 203/// SDMMC configuration
193/// 204///
194/// Default values: 205/// Default values:
@@ -208,9 +219,10 @@ impl Default for Config {
208} 219}
209 220
210/// Sdmmc device 221/// Sdmmc device
211pub struct Sdmmc<'d, T: Instance, Dma = NoDma> { 222pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma<T> = NoDma> {
212 _peri: PeripheralRef<'d, T>, 223 _peri: PeripheralRef<'d, T>,
213 irq: PeripheralRef<'d, T::Interrupt>, 224 irq: PeripheralRef<'d, T::Interrupt>,
225 #[allow(unused)]
214 dma: PeripheralRef<'d, Dma>, 226 dma: PeripheralRef<'d, Dma>,
215 227
216 clk: PeripheralRef<'d, AnyPin>, 228 clk: PeripheralRef<'d, AnyPin>,
@@ -309,49 +321,6 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
309 config, 321 config,
310 ) 322 )
311 } 323 }
312
313 fn new_inner(
314 sdmmc: impl Peripheral<P = T> + 'd,
315 irq: impl Peripheral<P = T::Interrupt> + 'd,
316 dma: impl Peripheral<P = Dma> + 'd,
317 clk: PeripheralRef<'d, AnyPin>,
318 cmd: PeripheralRef<'d, AnyPin>,
319 d0: PeripheralRef<'d, AnyPin>,
320 d1: Option<PeripheralRef<'d, AnyPin>>,
321 d2: Option<PeripheralRef<'d, AnyPin>>,
322 d3: Option<PeripheralRef<'d, AnyPin>>,
323 config: Config,
324 ) -> Self {
325 into_ref!(sdmmc, irq, dma);
326
327 T::enable();
328 T::reset();
329
330 let inner = T::inner();
331 unsafe { inner.new_inner() };
332
333 irq.set_handler(Self::on_interrupt);
334 irq.unpend();
335 irq.enable();
336
337 Self {
338 _peri: sdmmc,
339 irq,
340 dma,
341
342 clk,
343 cmd,
344 d0,
345 d1,
346 d2,
347 d3,
348
349 config,
350 clock: SD_INIT_FREQ,
351 signalling: Default::default(),
352 card: None,
353 }
354 }
355} 324}
356 325
357#[cfg(sdmmc_v2)] 326#[cfg(sdmmc_v2)]
@@ -379,6 +348,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
379 Self::new_inner( 348 Self::new_inner(
380 sdmmc, 349 sdmmc,
381 irq, 350 irq,
351 NoDma.into_ref(),
382 clk.map_into(), 352 clk.map_into(),
383 cmd.map_into(), 353 cmd.map_into(),
384 d0.map_into(), 354 d0.map_into(),
@@ -421,6 +391,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
421 Self::new_inner( 391 Self::new_inner(
422 sdmmc, 392 sdmmc,
423 irq, 393 irq,
394 NoDma.into_ref(),
424 clk.map_into(), 395 clk.map_into(),
425 cmd.map_into(), 396 cmd.map_into(),
426 d0.map_into(), 397 d0.map_into(),
@@ -430,10 +401,13 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
430 config, 401 config,
431 ) 402 )
432 } 403 }
404}
433 405
406impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
434 fn new_inner( 407 fn new_inner(
435 sdmmc: impl Peripheral<P = T> + 'd, 408 sdmmc: impl Peripheral<P = T> + 'd,
436 irq: impl Peripheral<P = T::Interrupt> + 'd, 409 irq: impl Peripheral<P = T::Interrupt> + 'd,
410 dma: impl Peripheral<P = Dma> + 'd,
437 clk: PeripheralRef<'d, AnyPin>, 411 clk: PeripheralRef<'d, AnyPin>,
438 cmd: PeripheralRef<'d, AnyPin>, 412 cmd: PeripheralRef<'d, AnyPin>,
439 d0: PeripheralRef<'d, AnyPin>, 413 d0: PeripheralRef<'d, AnyPin>,
@@ -442,22 +416,41 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
442 d3: Option<PeripheralRef<'d, AnyPin>>, 416 d3: Option<PeripheralRef<'d, AnyPin>>,
443 config: Config, 417 config: Config,
444 ) -> Self { 418 ) -> Self {
445 into_ref!(sdmmc, irq); 419 into_ref!(sdmmc, irq, dma);
446 420
447 T::enable(); 421 T::enable();
448 T::reset(); 422 T::reset();
449 423
450 let inner = T::inner();
451 unsafe { inner.new_inner() };
452
453 irq.set_handler(Self::on_interrupt); 424 irq.set_handler(Self::on_interrupt);
454 irq.unpend(); 425 irq.unpend();
455 irq.enable(); 426 irq.enable();
456 427
428 let regs = T::regs();
429 unsafe {
430 regs.clkcr().write(|w| {
431 w.set_pwrsav(false);
432 w.set_negedge(false);
433
434 // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors.
435 // See chip erratas for more details.
436 #[cfg(sdmmc_v1)]
437 w.set_hwfc_en(false);
438 #[cfg(sdmmc_v2)]
439 w.set_hwfc_en(true);
440
441 #[cfg(sdmmc_v1)]
442 w.set_clken(true);
443 });
444
445 // Power off, writen 00: Clock to the card is stopped;
446 // D[7:0], CMD, and CK are driven high.
447 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));
448 }
449
457 Self { 450 Self {
458 _peri: sdmmc, 451 _peri: sdmmc,
459 irq, 452 irq,
460 dma: NoDma.into_ref(), 453 dma,
461 454
462 clk, 455 clk,
463 cmd, 456 cmd,
@@ -472,593 +465,169 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
472 card: None, 465 card: None,
473 } 466 }
474 } 467 }
475}
476
477impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
478 #[inline(always)]
479 pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> {
480 let inner = T::inner();
481 let freq = freq.into();
482
483 let bus_width = match self.d3.is_some() {
484 true => BusWidth::Four,
485 false => BusWidth::One,
486 };
487
488 inner
489 .init_card(
490 freq,
491 bus_width,
492 &mut self.card,
493 &mut self.signalling,
494 T::kernel_clk(),
495 &mut self.clock,
496 T::state(),
497 self.config.data_transfer_timeout,
498 &mut *self.dma,
499 )
500 .await
501 }
502
503 #[inline(always)]
504 pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
505 let card_capacity = self.card()?.card_type;
506 let inner = T::inner();
507 let state = T::state();
508
509 // NOTE(unsafe) DataBlock uses align 4
510 let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) };
511 inner
512 .read_block(
513 block_idx,
514 buf,
515 card_capacity,
516 state,
517 self.config.data_transfer_timeout,
518 &mut *self.dma,
519 )
520 .await
521 }
522
523 pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
524 let card = self.card.as_mut().ok_or(Error::NoCard)?;
525 let inner = T::inner();
526 let state = T::state();
527
528 // NOTE(unsafe) DataBlock uses align 4
529 let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
530 inner
531 .write_block(
532 block_idx,
533 buf,
534 card,
535 state,
536 self.config.data_transfer_timeout,
537 &mut *self.dma,
538 )
539 .await
540 }
541
542 /// Get a reference to the initialized card
543 ///
544 /// # Errors
545 ///
546 /// Returns Error::NoCard if [`init_card`](#method.init_card)
547 /// has not previously succeeded
548 #[inline(always)]
549 pub fn card(&self) -> Result<&Card, Error> {
550 self.card.as_ref().ok_or(Error::NoCard)
551 }
552
553 /// Get the current SDMMC bus clock
554 pub fn clock(&self) -> Hertz {
555 self.clock
556 }
557
558 #[inline(always)]
559 fn on_interrupt(_: *mut ()) {
560 let regs = T::inner();
561 let state = T::state();
562
563 regs.data_interrupts(false);
564 state.wake();
565 }
566}
567
568impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> {
569 fn drop(&mut self) {
570 self.irq.disable();
571 let inner = T::inner();
572 unsafe { inner.on_drop() };
573
574 critical_section::with(|_| unsafe {
575 self.clk.set_as_disconnected();
576 self.cmd.set_as_disconnected();
577 self.d0.set_as_disconnected();
578 if let Some(x) = &mut self.d1 {
579 x.set_as_disconnected();
580 }
581 if let Some(x) = &mut self.d2 {
582 x.set_as_disconnected();
583 }
584 if let Some(x) = &mut self.d3 {
585 x.set_as_disconnected();
586 }
587 });
588 }
589}
590
591pub struct SdmmcInner(pub(crate) RegBlock);
592
593impl SdmmcInner {
594 /// # Safety
595 ///
596 /// Access to `regs` registers should be exclusive
597 unsafe fn new_inner(&self) {
598 let regs = self.0;
599
600 regs.clkcr().write(|w| {
601 w.set_pwrsav(false);
602 w.set_negedge(false);
603
604 // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors.
605 // See chip erratas for more details.
606 #[cfg(sdmmc_v1)]
607 w.set_hwfc_en(false);
608 #[cfg(sdmmc_v2)]
609 w.set_hwfc_en(true);
610
611 #[cfg(sdmmc_v1)]
612 w.set_clken(true);
613 });
614
615 // Power off, writen 00: Clock to the card is stopped;
616 // D[7:0], CMD, and CK are driven high.
617 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));
618 }
619
620 /// Initializes card (if present) and sets the bus at the
621 /// specified frequency.
622 #[allow(clippy::too_many_arguments)]
623 async fn init_card<T: Instance, Dma: SdmmcDma<T>>(
624 &self,
625 freq: Hertz,
626 bus_width: BusWidth,
627 old_card: &mut Option<Card>,
628 signalling: &mut Signalling,
629 ker_ck: Hertz,
630 clock: &mut Hertz,
631 waker_reg: &AtomicWaker,
632 data_transfer_timeout: u32,
633 dma: &mut Dma,
634 ) -> Result<(), Error> {
635 let regs = self.0;
636
637 // NOTE(unsafe) We have exclusive access to the peripheral
638 unsafe {
639 // While the SD/SDIO card or eMMC is in identification mode,
640 // the SDMMC_CK frequency must be no more than 400 kHz.
641 let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
642 *clock = init_clock;
643
644 // CPSMACT and DPSMACT must be 0 to set WIDBUS
645 self.wait_idle();
646
647 regs.clkcr().modify(|w| {
648 w.set_widbus(0);
649 w.set_clkdiv(clkdiv);
650 #[cfg(sdmmc_v1)]
651 w.set_bypass(_bypass);
652 });
653
654 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
655 self.cmd(Cmd::idle(), false)?;
656
657 // Check if cards supports CMD8 (with pattern)
658 self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?;
659 let r1 = regs.respr(0).read().cardstatus();
660
661 let mut card = if r1 == 0x1AA {
662 // Card echoed back the pattern. Must be at least v2
663 Card::default()
664 } else {
665 return Err(Error::UnsupportedCardVersion);
666 };
667
668 let ocr = loop {
669 // Signal that next command is a app command
670 self.cmd(Cmd::app_cmd(0), false)?; // CMD55
671
672 let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32
673 | CmdAppOper::HIGH_CAPACITY as u32
674 | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32;
675
676 // Initialize card
677 match self.cmd(Cmd::app_op_cmd(arg), false) {
678 // ACMD41
679 Ok(_) => (),
680 Err(Error::Crc) => (),
681 Err(err) => return Err(err),
682 }
683 let ocr: OCR = regs.respr(0).read().cardstatus().into();
684 if !ocr.is_busy() {
685 // Power up done
686 break ocr;
687 }
688 };
689
690 if ocr.high_capacity() {
691 // Card is SDHC or SDXC or SDUC
692 card.card_type = CardCapacity::SDHC;
693 } else {
694 card.card_type = CardCapacity::SDSC;
695 }
696 card.ocr = ocr;
697
698 self.cmd(Cmd::all_send_cid(), false)?; // CMD2
699 let cid0 = regs.respr(0).read().cardstatus() as u128;
700 let cid1 = regs.respr(1).read().cardstatus() as u128;
701 let cid2 = regs.respr(2).read().cardstatus() as u128;
702 let cid3 = regs.respr(3).read().cardstatus() as u128;
703 let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3);
704 card.cid = cid.into();
705
706 self.cmd(Cmd::send_rel_addr(), false)?;
707 card.rca = regs.respr(0).read().cardstatus() >> 16;
708
709 self.cmd(Cmd::send_csd(card.rca << 16), false)?;
710 let csd0 = regs.respr(0).read().cardstatus() as u128;
711 let csd1 = regs.respr(1).read().cardstatus() as u128;
712 let csd2 = regs.respr(2).read().cardstatus() as u128;
713 let csd3 = regs.respr(3).read().cardstatus() as u128;
714 let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
715 card.csd = csd.into();
716
717 self.select_card(Some(&card))?;
718
719 self.get_scr(&mut card, waker_reg, data_transfer_timeout, dma).await?;
720
721 // Set bus width
722 let (width, acmd_arg) = match bus_width {
723 BusWidth::Eight => unimplemented!(),
724 BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2),
725 _ => (BusWidth::One, 0),
726 };
727 self.cmd(Cmd::app_cmd(card.rca << 16), false)?;
728 self.cmd(Cmd::cmd6(acmd_arg), false)?;
729
730 // CPSMACT and DPSMACT must be 0 to set WIDBUS
731 self.wait_idle();
732
733 regs.clkcr().modify(|w| {
734 w.set_widbus(match width {
735 BusWidth::One => 0,
736 BusWidth::Four => 1,
737 BusWidth::Eight => 2,
738 _ => panic!("Invalid Bus Width"),
739 })
740 });
741
742 // Set Clock
743 if freq.0 <= 25_000_000 {
744 // Final clock frequency
745 self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?;
746 } else {
747 // Switch to max clock for SDR12
748 self.clkcr_set_clkdiv(25_000_000, width, ker_ck, clock)?;
749 }
750
751 // Read status
752 self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma)
753 .await?;
754
755 if freq.0 > 25_000_000 {
756 // Switch to SDR25
757 *signalling = self
758 .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout, dma)
759 .await?;
760
761 if *signalling == Signalling::SDR25 {
762 // Set final clock frequency
763 self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?;
764
765 if self.read_status(&card)?.state() != CurrentState::Transfer {
766 return Err(Error::SignalingSwitchFailed);
767 }
768 }
769 }
770 // Read status after signalling change
771 self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma)
772 .await?;
773 old_card.replace(card);
774 }
775
776 Ok(())
777 }
778
779 async fn read_block<T: Instance, Dma: SdmmcDma<T>>(
780 &self,
781 block_idx: u32,
782 buffer: &mut [u32; 128],
783 capacity: CardCapacity,
784 waker_reg: &AtomicWaker,
785 data_transfer_timeout: u32,
786 dma: &mut Dma,
787 ) -> Result<(), Error> {
788 // Always read 1 block of 512 bytes
789 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
790 let address = match capacity {
791 CardCapacity::SDSC => block_idx * 512,
792 _ => block_idx,
793 };
794 self.cmd(Cmd::set_block_length(512), false)?; // CMD16
795
796 let regs = self.0;
797 let on_drop = OnDrop::new(|| unsafe { self.on_drop() });
798
799 unsafe {
800 self.prepare_datapath_read(buffer as *mut [u32; 128], 512, 9, data_transfer_timeout, dma);
801 self.data_interrupts(true);
802 }
803 self.cmd(Cmd::read_single_block(address), true)?;
804
805 let res = poll_fn(|cx| {
806 waker_reg.register(cx.waker());
807 let status = unsafe { regs.star().read() };
808
809 if status.dcrcfail() {
810 return Poll::Ready(Err(Error::Crc));
811 } else if status.dtimeout() {
812 return Poll::Ready(Err(Error::Timeout));
813 } else if status.dataend() {
814 return Poll::Ready(Ok(()));
815 }
816 Poll::Pending
817 })
818 .await;
819 self.clear_interrupt_flags();
820
821 if res.is_ok() {
822 on_drop.defuse();
823 self.stop_datapath();
824 }
825 res
826 }
827
828 async fn write_block<T: Instance, Dma: SdmmcDma<T>>(
829 &self,
830 block_idx: u32,
831 buffer: &[u32; 128],
832 card: &mut Card,
833 waker_reg: &AtomicWaker,
834 data_transfer_timeout: u32,
835 dma: &mut Dma,
836 ) -> Result<(), Error> {
837 // Always read 1 block of 512 bytes
838 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
839 let address = match card.card_type {
840 CardCapacity::SDSC => block_idx * 512,
841 _ => block_idx,
842 };
843 self.cmd(Cmd::set_block_length(512), false)?; // CMD16
844
845 let regs = self.0;
846 let on_drop = OnDrop::new(|| unsafe { self.on_drop() });
847
848 // sdmmc_v1 uses different cmd/dma order than v2, but only for writes
849 #[cfg(sdmmc_v1)]
850 self.cmd(Cmd::write_single_block(address), true)?;
851
852 unsafe {
853 self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma);
854 self.data_interrupts(true);
855 }
856
857 #[cfg(sdmmc_v2)]
858 self.cmd(Cmd::write_single_block(address), true)?;
859
860 let res = poll_fn(|cx| {
861 waker_reg.register(cx.waker());
862 let status = unsafe { regs.star().read() };
863
864 if status.dcrcfail() {
865 return Poll::Ready(Err(Error::Crc));
866 } else if status.dtimeout() {
867 return Poll::Ready(Err(Error::Timeout));
868 } else if status.dataend() {
869 return Poll::Ready(Ok(()));
870 }
871 Poll::Pending
872 })
873 .await;
874 self.clear_interrupt_flags();
875
876 match res {
877 Ok(_) => {
878 on_drop.defuse();
879 self.stop_datapath();
880
881 // TODO: Make this configurable
882 let mut timeout: u32 = 0x00FF_FFFF;
883
884 // Try to read card status (ACMD13)
885 while timeout > 0 {
886 match self.read_sd_status(card, waker_reg, data_transfer_timeout, dma).await {
887 Ok(_) => return Ok(()),
888 Err(Error::Timeout) => (), // Try again
889 Err(e) => return Err(e),
890 }
891 timeout -= 1;
892 }
893 Err(Error::SoftwareTimeout)
894 }
895 Err(e) => Err(e),
896 }
897 }
898 468
899 /// Data transfer is in progress 469 /// Data transfer is in progress
900 #[inline(always)] 470 #[inline(always)]
901 fn data_active(&self) -> bool { 471 fn data_active() -> bool {
902 let regs = self.0; 472 let regs = T::regs();
903 473
904 // NOTE(unsafe) Atomic read with no side-effects 474 // NOTE(unsafe) Atomic read with no side-effects
905 unsafe { 475 unsafe {
906 let status = regs.star().read(); 476 let status = regs.star().read();
907 cfg_if::cfg_if! { 477 #[cfg(sdmmc_v1)]
908 if #[cfg(sdmmc_v1)] { 478 return status.rxact() || status.txact();
909 status.rxact() || status.txact() 479 #[cfg(sdmmc_v2)]
910 } else if #[cfg(sdmmc_v2)] { 480 return status.dpsmact();
911 status.dpsmact()
912 }
913 }
914 } 481 }
915 } 482 }
916 483
917 /// Coammand transfer is in progress 484 /// Coammand transfer is in progress
918 #[inline(always)] 485 #[inline(always)]
919 fn cmd_active(&self) -> bool { 486 fn cmd_active() -> bool {
920 let regs = self.0; 487 let regs = T::regs();
921 488
922 // NOTE(unsafe) Atomic read with no side-effects 489 // NOTE(unsafe) Atomic read with no side-effects
923 unsafe { 490 unsafe {
924 let status = regs.star().read(); 491 let status = regs.star().read();
925 cfg_if::cfg_if! { 492 #[cfg(sdmmc_v1)]
926 if #[cfg(sdmmc_v1)] { 493 return status.cmdact();
927 status.cmdact() 494 #[cfg(sdmmc_v2)]
928 } else if #[cfg(sdmmc_v2)] { 495 return status.cpsmact();
929 status.cpsmact()
930 }
931 }
932 } 496 }
933 } 497 }
934 498
935 /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) 499 /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2)
936 #[inline(always)] 500 #[inline(always)]
937 fn wait_idle(&self) { 501 fn wait_idle() {
938 while self.data_active() || self.cmd_active() {} 502 while Self::data_active() || Self::cmd_active() {}
939 } 503 }
940 504
941 /// # Safety 505 /// # Safety
942 /// 506 ///
943 /// `buffer` must be valid for the whole transfer and word aligned 507 /// `buffer` must be valid for the whole transfer and word aligned
944 unsafe fn prepare_datapath_read<T: Instance, Dma: SdmmcDma<T>>( 508 fn prepare_datapath_read<'a>(
945 &self, 509 &'a mut self,
946 buffer: *mut [u32], 510 buffer: &'a mut [u32],
947 length_bytes: u32, 511 length_bytes: u32,
948 block_size: u8, 512 block_size: u8,
949 data_transfer_timeout: u32, 513 ) -> Transfer<'a, Dma> {
950 #[allow(unused_variables)] dma: &mut Dma,
951 ) {
952 assert!(block_size <= 14, "Block size up to 2^14 bytes"); 514 assert!(block_size <= 14, "Block size up to 2^14 bytes");
953 let regs = self.0; 515 let regs = T::regs();
954 516
955 // Command AND Data state machines must be idle 517 // Command AND Data state machines must be idle
956 self.wait_idle(); 518 Self::wait_idle();
957 self.clear_interrupt_flags(); 519 Self::clear_interrupt_flags();
958 520
959 // NOTE(unsafe) We have exclusive access to the regisers 521 // NOTE(unsafe) We have exclusive access to the regisers
522 unsafe {
523 regs.dtimer()
524 .write(|w| w.set_datatime(self.config.data_transfer_timeout));
525 regs.dlenr().write(|w| w.set_datalength(length_bytes));
960 526
961 regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); 527 #[cfg(sdmmc_v1)]
962 regs.dlenr().write(|w| w.set_datalength(length_bytes)); 528 let transfer = {
963 529 let request = self.dma.request();
964 cfg_if::cfg_if! { 530 Transfer::new_read(
965 if #[cfg(sdmmc_v1)] { 531 &mut self.dma,
966 let request = dma.request(); 532 request,
967 dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { 533 regs.fifor().ptr() as *mut u32,
968 pburst: crate::dma::Burst::Incr4, 534 buffer,
969 mburst: crate::dma::Burst::Incr4, 535 DMA_TRANSFER_OPTIONS,
970 flow_ctrl: crate::dma::FlowControl::Peripheral, 536 )
971 fifo_threshold: Some(crate::dma::FifoThreshold::Full), 537 };
972 ..Default::default() 538 #[cfg(sdmmc_v2)]
973 }); 539 let transfer = {
974 } else if #[cfg(sdmmc_v2)] { 540 regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32));
975 regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32));
976 regs.idmactrlr().modify(|w| w.set_idmaen(true)); 541 regs.idmactrlr().modify(|w| w.set_idmaen(true));
977 } 542 core::marker::PhantomData
978 } 543 };
979 544
980 regs.dctrl().modify(|w| { 545 regs.dctrl().modify(|w| {
981 w.set_dblocksize(block_size); 546 w.set_dblocksize(block_size);
982 w.set_dtdir(true); 547 w.set_dtdir(true);
983 #[cfg(sdmmc_v1)] 548 #[cfg(sdmmc_v1)]
984 { 549 {
985 w.set_dmaen(true); 550 w.set_dmaen(true);
986 w.set_dten(true); 551 w.set_dten(true);
987 } 552 }
988 }); 553 });
554
555 transfer
556 }
989 } 557 }
990 558
991 /// # Safety 559 /// # Safety
992 /// 560 ///
993 /// `buffer` must be valid for the whole transfer and word aligned 561 /// `buffer` must be valid for the whole transfer and word aligned
994 unsafe fn prepare_datapath_write<T: Instance, Dma: SdmmcDma<T>>( 562 fn prepare_datapath_write<'a>(
995 &self, 563 &'a mut self,
996 buffer: *const [u32], 564 buffer: &'a [u32],
997 length_bytes: u32, 565 length_bytes: u32,
998 block_size: u8, 566 block_size: u8,
999 data_transfer_timeout: u32, 567 ) -> Transfer<'a, Dma> {
1000 #[allow(unused_variables)] dma: &mut Dma,
1001 ) {
1002 assert!(block_size <= 14, "Block size up to 2^14 bytes"); 568 assert!(block_size <= 14, "Block size up to 2^14 bytes");
1003 let regs = self.0; 569 let regs = T::regs();
1004 570
1005 // Command AND Data state machines must be idle 571 // Command AND Data state machines must be idle
1006 self.wait_idle(); 572 Self::wait_idle();
1007 self.clear_interrupt_flags(); 573 Self::clear_interrupt_flags();
1008 574
1009 // NOTE(unsafe) We have exclusive access to the regisers 575 // NOTE(unsafe) We have exclusive access to the regisers
576 unsafe {
577 regs.dtimer()
578 .write(|w| w.set_datatime(self.config.data_transfer_timeout));
579 regs.dlenr().write(|w| w.set_datalength(length_bytes));
1010 580
1011 regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); 581 #[cfg(sdmmc_v1)]
1012 regs.dlenr().write(|w| w.set_datalength(length_bytes)); 582 let transfer = {
1013 583 let request = self.dma.request();
1014 cfg_if::cfg_if! { 584 Transfer::new_write(
1015 if #[cfg(sdmmc_v1)] { 585 &mut self.dma,
1016 let request = dma.request(); 586 request,
1017 dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { 587 buffer,
1018 pburst: crate::dma::Burst::Incr4, 588 regs.fifor().ptr() as *mut u32,
1019 mburst: crate::dma::Burst::Incr4, 589 DMA_TRANSFER_OPTIONS,
1020 flow_ctrl: crate::dma::FlowControl::Peripheral, 590 )
1021 fifo_threshold: Some(crate::dma::FifoThreshold::Full), 591 };
1022 ..Default::default() 592 #[cfg(sdmmc_v2)]
1023 }); 593 let transfer = {
1024 } else if #[cfg(sdmmc_v2)] { 594 regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32));
1025 regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *const u32 as u32));
1026 regs.idmactrlr().modify(|w| w.set_idmaen(true)); 595 regs.idmactrlr().modify(|w| w.set_idmaen(true));
1027 } 596 core::marker::PhantomData
1028 } 597 };
1029 598
1030 regs.dctrl().modify(|w| { 599 regs.dctrl().modify(|w| {
1031 w.set_dblocksize(block_size); 600 w.set_dblocksize(block_size);
1032 w.set_dtdir(false); 601 w.set_dtdir(false);
1033 #[cfg(sdmmc_v1)] 602 #[cfg(sdmmc_v1)]
1034 { 603 {
1035 w.set_dmaen(true); 604 w.set_dmaen(true);
1036 w.set_dten(true); 605 w.set_dten(true);
1037 } 606 }
1038 }); 607 });
608
609 transfer
610 }
1039 } 611 }
1040 612
1041 /// Stops the DMA datapath 613 /// Stops the DMA datapath
1042 fn stop_datapath(&self) { 614 fn stop_datapath() {
1043 let regs = self.0; 615 let regs = T::regs();
1044 616
1045 unsafe { 617 unsafe {
1046 cfg_if::cfg_if! { 618 #[cfg(sdmmc_v1)]
1047 if #[cfg(sdmmc_v1)] { 619 regs.dctrl().modify(|w| {
1048 regs.dctrl().modify(|w| { 620 w.set_dmaen(false);
1049 w.set_dmaen(false); 621 w.set_dten(false);
1050 w.set_dten(false); 622 });
1051 }); 623 #[cfg(sdmmc_v2)]
1052 } else if #[cfg(sdmmc_v2)] { 624 regs.idmactrlr().modify(|w| w.set_idmaen(false));
1053 regs.idmactrlr().modify(|w| w.set_idmaen(false));
1054 }
1055 }
1056 } 625 }
1057 } 626 }
1058 627
1059 /// Sets the CLKDIV field in CLKCR. Updates clock field in self 628 /// Sets the CLKDIV field in CLKCR. Updates clock field in self
1060 fn clkcr_set_clkdiv(&self, freq: u32, width: BusWidth, ker_ck: Hertz, clock: &mut Hertz) -> Result<(), Error> { 629 fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> {
1061 let regs = self.0; 630 let regs = T::regs();
1062 631
1063 let width_u32 = match width { 632 let width_u32 = match width {
1064 BusWidth::One => 1u32, 633 BusWidth::One => 1u32,
@@ -1067,18 +636,19 @@ impl SdmmcInner {
1067 _ => panic!("Invalid Bus Width"), 636 _ => panic!("Invalid Bus Width"),
1068 }; 637 };
1069 638
639 let ker_ck = T::kernel_clk();
1070 let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; 640 let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?;
1071 641
1072 // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 642 // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7
1073 // Section 55.5.8 643 // Section 55.5.8
1074 let sdmmc_bus_bandwidth = new_clock.0 * width_u32; 644 let sdmmc_bus_bandwidth = new_clock.0 * width_u32;
1075 assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); 645 assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32);
1076 *clock = new_clock; 646 self.clock = new_clock;
1077 647
1078 // NOTE(unsafe) We have exclusive access to the regblock 648 // NOTE(unsafe) We have exclusive access to the regblock
1079 unsafe { 649 unsafe {
1080 // CPSMACT and DPSMACT must be 0 to set CLKDIV 650 // CPSMACT and DPSMACT must be 0 to set CLKDIV
1081 self.wait_idle(); 651 Self::wait_idle();
1082 regs.clkcr().modify(|w| { 652 regs.clkcr().modify(|w| {
1083 w.set_clkdiv(clkdiv); 653 w.set_clkdiv(clkdiv);
1084 #[cfg(sdmmc_v1)] 654 #[cfg(sdmmc_v1)]
@@ -1094,13 +664,7 @@ impl SdmmcInner {
1094 /// Attempt to set a new signalling mode. The selected 664 /// Attempt to set a new signalling mode. The selected
1095 /// signalling mode is returned. Expects the current clock 665 /// signalling mode is returned. Expects the current clock
1096 /// frequency to be > 12.5MHz. 666 /// frequency to be > 12.5MHz.
1097 async fn switch_signalling_mode<T: Instance, Dma: SdmmcDma<T>>( 667 async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> {
1098 &self,
1099 signalling: Signalling,
1100 waker_reg: &AtomicWaker,
1101 data_transfer_timeout: u32,
1102 dma: &mut Dma,
1103 ) -> Result<Signalling, Error> {
1104 // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not 668 // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not
1105 // necessary" 669 // necessary"
1106 670
@@ -1117,17 +681,15 @@ impl SdmmcInner {
1117 let mut status = [0u32; 16]; 681 let mut status = [0u32; 16];
1118 682
1119 // Arm `OnDrop` after the buffer, so it will be dropped first 683 // Arm `OnDrop` after the buffer, so it will be dropped first
1120 let regs = self.0; 684 let regs = T::regs();
1121 let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); 685 let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
1122 686
1123 unsafe { 687 let transfer = self.prepare_datapath_read(&mut status, 64, 6);
1124 self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); 688 Self::data_interrupts(true);
1125 self.data_interrupts(true); 689 Self::cmd(Cmd::cmd6(set_function), true)?; // CMD6
1126 }
1127 self.cmd(Cmd::cmd6(set_function), true)?; // CMD6
1128 690
1129 let res = poll_fn(|cx| { 691 let res = poll_fn(|cx| {
1130 waker_reg.register(cx.waker()); 692 T::state().register(cx.waker());
1131 let status = unsafe { regs.star().read() }; 693 let status = unsafe { regs.star().read() };
1132 694
1133 if status.dcrcfail() { 695 if status.dcrcfail() {
@@ -1140,7 +702,7 @@ impl SdmmcInner {
1140 Poll::Pending 702 Poll::Pending
1141 }) 703 })
1142 .await; 704 .await;
1143 self.clear_interrupt_flags(); 705 Self::clear_interrupt_flags();
1144 706
1145 // Host is allowed to use the new functions at least 8 707 // Host is allowed to use the new functions at least 8
1146 // clocks after the end of the switch command 708 // clocks after the end of the switch command
@@ -1153,7 +715,8 @@ impl SdmmcInner {
1153 match res { 715 match res {
1154 Ok(_) => { 716 Ok(_) => {
1155 on_drop.defuse(); 717 on_drop.defuse();
1156 self.stop_datapath(); 718 Self::stop_datapath();
719 drop(transfer);
1157 720
1158 // Function Selection of Function Group 1 721 // Function Selection of Function Group 1
1159 let selection = (u32::from_be(status[4]) >> 24) & 0xF; 722 let selection = (u32::from_be(status[4]) >> 24) & 0xF;
@@ -1173,10 +736,10 @@ impl SdmmcInner {
1173 736
1174 /// Query the card status (CMD13, returns R1) 737 /// Query the card status (CMD13, returns R1)
1175 fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { 738 fn read_status(&self, card: &Card) -> Result<CardStatus, Error> {
1176 let regs = self.0; 739 let regs = T::regs();
1177 let rca = card.rca; 740 let rca = card.rca;
1178 741
1179 self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 742 Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13
1180 743
1181 // NOTE(unsafe) Atomic read with no side-effects 744 // NOTE(unsafe) Atomic read with no side-effects
1182 let r1 = unsafe { regs.respr(0).read().cardstatus() }; 745 let r1 = unsafe { regs.respr(0).read().cardstatus() };
@@ -1184,31 +747,25 @@ impl SdmmcInner {
1184 } 747 }
1185 748
1186 /// Reads the SD Status (ACMD13) 749 /// Reads the SD Status (ACMD13)
1187 async fn read_sd_status<T: Instance, Dma: SdmmcDma<T>>( 750 async fn read_sd_status(&mut self) -> Result<(), Error> {
1188 &self, 751 let card = self.card.as_mut().ok_or(Error::NoCard)?;
1189 card: &mut Card,
1190 waker_reg: &AtomicWaker,
1191 data_transfer_timeout: u32,
1192 dma: &mut Dma,
1193 ) -> Result<(), Error> {
1194 let rca = card.rca; 752 let rca = card.rca;
1195 self.cmd(Cmd::set_block_length(64), false)?; // CMD16 753
1196 self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP 754 Self::cmd(Cmd::set_block_length(64), false)?; // CMD16
755 Self::cmd(Cmd::app_cmd(rca << 16), false)?; // APP
1197 756
1198 let mut status = [0u32; 16]; 757 let mut status = [0u32; 16];
1199 758
1200 // Arm `OnDrop` after the buffer, so it will be dropped first 759 // Arm `OnDrop` after the buffer, so it will be dropped first
1201 let regs = self.0; 760 let regs = T::regs();
1202 let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); 761 let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
1203 762
1204 unsafe { 763 let transfer = self.prepare_datapath_read(&mut status, 64, 6);
1205 self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); 764 Self::data_interrupts(true);
1206 self.data_interrupts(true); 765 Self::cmd(Cmd::card_status(0), true)?;
1207 }
1208 self.cmd(Cmd::card_status(0), true)?;
1209 766
1210 let res = poll_fn(|cx| { 767 let res = poll_fn(|cx| {
1211 waker_reg.register(cx.waker()); 768 T::state().register(cx.waker());
1212 let status = unsafe { regs.star().read() }; 769 let status = unsafe { regs.star().read() };
1213 770
1214 if status.dcrcfail() { 771 if status.dcrcfail() {
@@ -1221,16 +778,17 @@ impl SdmmcInner {
1221 Poll::Pending 778 Poll::Pending
1222 }) 779 })
1223 .await; 780 .await;
1224 self.clear_interrupt_flags(); 781 Self::clear_interrupt_flags();
1225 782
1226 if res.is_ok() { 783 if res.is_ok() {
1227 on_drop.defuse(); 784 on_drop.defuse();
1228 self.stop_datapath(); 785 Self::stop_datapath();
786 drop(transfer);
1229 787
1230 for byte in status.iter_mut() { 788 for byte in status.iter_mut() {
1231 *byte = u32::from_be(*byte); 789 *byte = u32::from_be(*byte);
1232 } 790 }
1233 card.status = status.into(); 791 self.card.as_mut().unwrap().status = status.into();
1234 } 792 }
1235 res 793 res
1236 } 794 }
@@ -1243,7 +801,7 @@ impl SdmmcInner {
1243 // Determine Relative Card Address (RCA) of given card 801 // Determine Relative Card Address (RCA) of given card
1244 let rca = card.map(|c| c.rca << 16).unwrap_or(0); 802 let rca = card.map(|c| c.rca << 16).unwrap_or(0);
1245 803
1246 let r = self.cmd(Cmd::sel_desel_card(rca), false); 804 let r = Self::cmd(Cmd::sel_desel_card(rca), false);
1247 match (r, rca) { 805 match (r, rca) {
1248 (Err(Error::Timeout), 0) => Ok(()), 806 (Err(Error::Timeout), 0) => Ok(()),
1249 _ => r, 807 _ => r,
@@ -1252,8 +810,8 @@ impl SdmmcInner {
1252 810
1253 /// Clear flags in interrupt clear register 811 /// Clear flags in interrupt clear register
1254 #[inline(always)] 812 #[inline(always)]
1255 fn clear_interrupt_flags(&self) { 813 fn clear_interrupt_flags() {
1256 let regs = self.0; 814 let regs = T::regs();
1257 // NOTE(unsafe) Atomic write 815 // NOTE(unsafe) Atomic write
1258 unsafe { 816 unsafe {
1259 regs.icr().write(|w| { 817 regs.icr().write(|w| {
@@ -1287,8 +845,8 @@ impl SdmmcInner {
1287 845
1288 /// Enables the interrupts for data transfer 846 /// Enables the interrupts for data transfer
1289 #[inline(always)] 847 #[inline(always)]
1290 fn data_interrupts(&self, enable: bool) { 848 fn data_interrupts(enable: bool) {
1291 let regs = self.0; 849 let regs = T::regs();
1292 // NOTE(unsafe) Atomic write 850 // NOTE(unsafe) Atomic write
1293 unsafe { 851 unsafe {
1294 regs.maskr().write(|w| { 852 regs.maskr().write(|w| {
@@ -1302,31 +860,23 @@ impl SdmmcInner {
1302 } 860 }
1303 } 861 }
1304 862
1305 async fn get_scr<T: Instance, Dma: SdmmcDma<T>>( 863 async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> {
1306 &self,
1307 card: &mut Card,
1308 waker_reg: &AtomicWaker,
1309 data_transfer_timeout: u32,
1310 dma: &mut Dma,
1311 ) -> Result<(), Error> {
1312 // Read the the 64-bit SCR register 864 // Read the the 64-bit SCR register
1313 self.cmd(Cmd::set_block_length(8), false)?; // CMD16 865 Self::cmd(Cmd::set_block_length(8), false)?; // CMD16
1314 self.cmd(Cmd::app_cmd(card.rca << 16), false)?; 866 Self::cmd(Cmd::app_cmd(card.rca << 16), false)?;
1315 867
1316 let mut scr = [0u32; 2]; 868 let mut scr = [0u32; 2];
1317 869
1318 // Arm `OnDrop` after the buffer, so it will be dropped first 870 // Arm `OnDrop` after the buffer, so it will be dropped first
1319 let regs = self.0; 871 let regs = T::regs();
1320 let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); 872 let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
1321 873
1322 unsafe { 874 let transfer = self.prepare_datapath_read(&mut scr[..], 8, 3);
1323 self.prepare_datapath_read(&mut scr as *mut [u32], 8, 3, data_transfer_timeout, dma); 875 Self::data_interrupts(true);
1324 self.data_interrupts(true); 876 Self::cmd(Cmd::cmd51(), true)?;
1325 }
1326 self.cmd(Cmd::cmd51(), true)?;
1327 877
1328 let res = poll_fn(|cx| { 878 let res = poll_fn(|cx| {
1329 waker_reg.register(cx.waker()); 879 T::state().register(cx.waker());
1330 let status = unsafe { regs.star().read() }; 880 let status = unsafe { regs.star().read() };
1331 881
1332 if status.dcrcfail() { 882 if status.dcrcfail() {
@@ -1339,11 +889,12 @@ impl SdmmcInner {
1339 Poll::Pending 889 Poll::Pending
1340 }) 890 })
1341 .await; 891 .await;
1342 self.clear_interrupt_flags(); 892 Self::clear_interrupt_flags();
1343 893
1344 if res.is_ok() { 894 if res.is_ok() {
1345 on_drop.defuse(); 895 on_drop.defuse();
1346 self.stop_datapath(); 896 Self::stop_datapath();
897 drop(transfer);
1347 898
1348 unsafe { 899 unsafe {
1349 let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); 900 let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]);
@@ -1355,14 +906,14 @@ impl SdmmcInner {
1355 906
1356 /// Send command to card 907 /// Send command to card
1357 #[allow(unused_variables)] 908 #[allow(unused_variables)]
1358 fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { 909 fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> {
1359 let regs = self.0; 910 let regs = T::regs();
1360 911
1361 self.clear_interrupt_flags(); 912 Self::clear_interrupt_flags();
1362 // NOTE(safety) Atomic operations 913 // NOTE(safety) Atomic operations
1363 unsafe { 914 unsafe {
1364 // CP state machine must be idle 915 // CP state machine must be idle
1365 while self.cmd_active() {} 916 while Self::cmd_active() {}
1366 917
1367 // Command arg 918 // Command arg
1368 regs.argr().write(|w| w.set_cmdarg(cmd.arg)); 919 regs.argr().write(|w| w.set_cmdarg(cmd.arg));
@@ -1411,13 +962,13 @@ impl SdmmcInner {
1411 /// # Safety 962 /// # Safety
1412 /// 963 ///
1413 /// Ensure that `regs` has exclusive access to the regblocks 964 /// Ensure that `regs` has exclusive access to the regblocks
1414 unsafe fn on_drop(&self) { 965 unsafe fn on_drop() {
1415 let regs = self.0; 966 let regs = T::regs();
1416 if self.data_active() { 967 if Self::data_active() {
1417 self.clear_interrupt_flags(); 968 Self::clear_interrupt_flags();
1418 // Send abort 969 // Send abort
1419 // CP state machine must be idle 970 // CP state machine must be idle
1420 while self.cmd_active() {} 971 while Self::cmd_active() {}
1421 972
1422 // Command arg 973 // Command arg
1423 regs.argr().write(|w| w.set_cmdarg(0)); 974 regs.argr().write(|w| w.set_cmdarg(0));
@@ -1437,11 +988,318 @@ impl SdmmcInner {
1437 }); 988 });
1438 989
1439 // Wait for the abort 990 // Wait for the abort
1440 while self.data_active() {} 991 while Self::data_active() {}
1441 } 992 }
1442 self.data_interrupts(false); 993 Self::data_interrupts(false);
1443 self.clear_interrupt_flags(); 994 Self::clear_interrupt_flags();
1444 self.stop_datapath(); 995 Self::stop_datapath();
996 }
997
998 /// Initializes card (if present) and sets the bus at the
999 /// specified frequency.
1000 pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> {
1001 let regs = T::regs();
1002 let ker_ck = T::kernel_clk();
1003
1004 let bus_width = match self.d3.is_some() {
1005 true => BusWidth::Four,
1006 false => BusWidth::One,
1007 };
1008
1009 // NOTE(unsafe) We have exclusive access to the peripheral
1010 unsafe {
1011 // While the SD/SDIO card or eMMC is in identification mode,
1012 // the SDMMC_CK frequency must be no more than 400 kHz.
1013 let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
1014 self.clock = init_clock;
1015
1016 // CPSMACT and DPSMACT must be 0 to set WIDBUS
1017 Self::wait_idle();
1018
1019 regs.clkcr().modify(|w| {
1020 w.set_widbus(0);
1021 w.set_clkdiv(clkdiv);
1022 #[cfg(sdmmc_v1)]
1023 w.set_bypass(_bypass);
1024 });
1025
1026 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
1027 Self::cmd(Cmd::idle(), false)?;
1028
1029 // Check if cards supports CMD8 (with pattern)
1030 Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?;
1031 let r1 = regs.respr(0).read().cardstatus();
1032
1033 let mut card = if r1 == 0x1AA {
1034 // Card echoed back the pattern. Must be at least v2
1035 Card::default()
1036 } else {
1037 return Err(Error::UnsupportedCardVersion);
1038 };
1039
1040 let ocr = loop {
1041 // Signal that next command is a app command
1042 Self::cmd(Cmd::app_cmd(0), false)?; // CMD55
1043
1044 let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32
1045 | CmdAppOper::HIGH_CAPACITY as u32
1046 | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32;
1047
1048 // Initialize card
1049 match Self::cmd(Cmd::app_op_cmd(arg), false) {
1050 // ACMD41
1051 Ok(_) => (),
1052 Err(Error::Crc) => (),
1053 Err(err) => return Err(err),
1054 }
1055 let ocr: OCR = regs.respr(0).read().cardstatus().into();
1056 if !ocr.is_busy() {
1057 // Power up done
1058 break ocr;
1059 }
1060 };
1061
1062 if ocr.high_capacity() {
1063 // Card is SDHC or SDXC or SDUC
1064 card.card_type = CardCapacity::SDHC;
1065 } else {
1066 card.card_type = CardCapacity::SDSC;
1067 }
1068 card.ocr = ocr;
1069
1070 Self::cmd(Cmd::all_send_cid(), false)?; // CMD2
1071 let cid0 = regs.respr(0).read().cardstatus() as u128;
1072 let cid1 = regs.respr(1).read().cardstatus() as u128;
1073 let cid2 = regs.respr(2).read().cardstatus() as u128;
1074 let cid3 = regs.respr(3).read().cardstatus() as u128;
1075 let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3);
1076 card.cid = cid.into();
1077
1078 Self::cmd(Cmd::send_rel_addr(), false)?;
1079 card.rca = regs.respr(0).read().cardstatus() >> 16;
1080
1081 Self::cmd(Cmd::send_csd(card.rca << 16), false)?;
1082 let csd0 = regs.respr(0).read().cardstatus() as u128;
1083 let csd1 = regs.respr(1).read().cardstatus() as u128;
1084 let csd2 = regs.respr(2).read().cardstatus() as u128;
1085 let csd3 = regs.respr(3).read().cardstatus() as u128;
1086 let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
1087 card.csd = csd.into();
1088
1089 self.select_card(Some(&card))?;
1090
1091 self.get_scr(&mut card).await?;
1092
1093 // Set bus width
1094 let (width, acmd_arg) = match bus_width {
1095 BusWidth::Eight => unimplemented!(),
1096 BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2),
1097 _ => (BusWidth::One, 0),
1098 };
1099 Self::cmd(Cmd::app_cmd(card.rca << 16), false)?;
1100 Self::cmd(Cmd::cmd6(acmd_arg), false)?;
1101
1102 // CPSMACT and DPSMACT must be 0 to set WIDBUS
1103 Self::wait_idle();
1104
1105 regs.clkcr().modify(|w| {
1106 w.set_widbus(match width {
1107 BusWidth::One => 0,
1108 BusWidth::Four => 1,
1109 BusWidth::Eight => 2,
1110 _ => panic!("Invalid Bus Width"),
1111 })
1112 });
1113
1114 // Set Clock
1115 if freq.0 <= 25_000_000 {
1116 // Final clock frequency
1117 self.clkcr_set_clkdiv(freq.0, width)?;
1118 } else {
1119 // Switch to max clock for SDR12
1120 self.clkcr_set_clkdiv(25_000_000, width)?;
1121 }
1122
1123 self.card = Some(card);
1124
1125 // Read status
1126 self.read_sd_status().await?;
1127
1128 if freq.0 > 25_000_000 {
1129 // Switch to SDR25
1130 self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?;
1131
1132 if self.signalling == Signalling::SDR25 {
1133 // Set final clock frequency
1134 self.clkcr_set_clkdiv(freq.0, width)?;
1135
1136 if self.read_status(&card)?.state() != CurrentState::Transfer {
1137 return Err(Error::SignalingSwitchFailed);
1138 }
1139 }
1140 }
1141 // Read status after signalling change
1142 self.read_sd_status().await?;
1143 }
1144
1145 Ok(())
1146 }
1147
1148 #[inline(always)]
1149 pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
1150 let card_capacity = self.card()?.card_type;
1151
1152 // NOTE(unsafe) DataBlock uses align 4
1153 let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) };
1154
1155 // Always read 1 block of 512 bytes
1156 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
1157 let address = match card_capacity {
1158 CardCapacity::SDSC => block_idx * 512,
1159 _ => block_idx,
1160 };
1161 Self::cmd(Cmd::set_block_length(512), false)?; // CMD16
1162
1163 let regs = T::regs();
1164 let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
1165
1166 let transfer = self.prepare_datapath_read(buffer, 512, 9);
1167 Self::data_interrupts(true);
1168 Self::cmd(Cmd::read_single_block(address), true)?;
1169
1170 let res = poll_fn(|cx| {
1171 T::state().register(cx.waker());
1172 let status = unsafe { regs.star().read() };
1173
1174 if status.dcrcfail() {
1175 return Poll::Ready(Err(Error::Crc));
1176 } else if status.dtimeout() {
1177 return Poll::Ready(Err(Error::Timeout));
1178 } else if status.dataend() {
1179 return Poll::Ready(Ok(()));
1180 }
1181 Poll::Pending
1182 })
1183 .await;
1184 Self::clear_interrupt_flags();
1185
1186 if res.is_ok() {
1187 on_drop.defuse();
1188 Self::stop_datapath();
1189 drop(transfer);
1190 }
1191 res
1192 }
1193
1194 pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
1195 let card = self.card.as_mut().ok_or(Error::NoCard)?;
1196
1197 // NOTE(unsafe) DataBlock uses align 4
1198 let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
1199
1200 // Always read 1 block of 512 bytes
1201 // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
1202 let address = match card.card_type {
1203 CardCapacity::SDSC => block_idx * 512,
1204 _ => block_idx,
1205 };
1206 Self::cmd(Cmd::set_block_length(512), false)?; // CMD16
1207
1208 let regs = T::regs();
1209 let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
1210
1211 // sdmmc_v1 uses different cmd/dma order than v2, but only for writes
1212 #[cfg(sdmmc_v1)]
1213 Self::cmd(Cmd::write_single_block(address), true)?;
1214
1215 let transfer = self.prepare_datapath_write(buffer, 512, 9);
1216 Self::data_interrupts(true);
1217
1218 #[cfg(sdmmc_v2)]
1219 Self::cmd(Cmd::write_single_block(address), true)?;
1220
1221 let res = poll_fn(|cx| {
1222 T::state().register(cx.waker());
1223 let status = unsafe { regs.star().read() };
1224
1225 if status.dcrcfail() {
1226 return Poll::Ready(Err(Error::Crc));
1227 } else if status.dtimeout() {
1228 return Poll::Ready(Err(Error::Timeout));
1229 } else if status.dataend() {
1230 return Poll::Ready(Ok(()));
1231 }
1232 Poll::Pending
1233 })
1234 .await;
1235 Self::clear_interrupt_flags();
1236
1237 match res {
1238 Ok(_) => {
1239 on_drop.defuse();
1240 Self::stop_datapath();
1241 drop(transfer);
1242
1243 // TODO: Make this configurable
1244 let mut timeout: u32 = 0x00FF_FFFF;
1245
1246 // Try to read card status (ACMD13)
1247 while timeout > 0 {
1248 match self.read_sd_status().await {
1249 Ok(_) => return Ok(()),
1250 Err(Error::Timeout) => (), // Try again
1251 Err(e) => return Err(e),
1252 }
1253 timeout -= 1;
1254 }
1255 Err(Error::SoftwareTimeout)
1256 }
1257 Err(e) => Err(e),
1258 }
1259 }
1260
1261 /// Get a reference to the initialized card
1262 ///
1263 /// # Errors
1264 ///
1265 /// Returns Error::NoCard if [`init_card`](#method.init_card)
1266 /// has not previously succeeded
1267 #[inline(always)]
1268 pub fn card(&self) -> Result<&Card, Error> {
1269 self.card.as_ref().ok_or(Error::NoCard)
1270 }
1271
1272 /// Get the current SDMMC bus clock
1273 pub fn clock(&self) -> Hertz {
1274 self.clock
1275 }
1276
1277 #[inline(always)]
1278 fn on_interrupt(_: *mut ()) {
1279 Self::data_interrupts(false);
1280 T::state().wake();
1281 }
1282}
1283
1284impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Drop for Sdmmc<'d, T, Dma> {
1285 fn drop(&mut self) {
1286 self.irq.disable();
1287 unsafe { Self::on_drop() };
1288
1289 critical_section::with(|_| unsafe {
1290 self.clk.set_as_disconnected();
1291 self.cmd.set_as_disconnected();
1292 self.d0.set_as_disconnected();
1293 if let Some(x) = &mut self.d1 {
1294 x.set_as_disconnected();
1295 }
1296 if let Some(x) = &mut self.d2 {
1297 x.set_as_disconnected();
1298 }
1299 if let Some(x) = &mut self.d3 {
1300 x.set_as_disconnected();
1301 }
1302 });
1445 } 1303 }
1446} 1304}
1447 1305
@@ -1540,7 +1398,7 @@ pub(crate) mod sealed {
1540 pub trait Instance { 1398 pub trait Instance {
1541 type Interrupt: Interrupt; 1399 type Interrupt: Interrupt;
1542 1400
1543 fn inner() -> SdmmcInner; 1401 fn regs() -> RegBlock;
1544 fn state() -> &'static AtomicWaker; 1402 fn state() -> &'static AtomicWaker;
1545 fn kernel_clk() -> Hertz; 1403 fn kernel_clk() -> Hertz;
1546 } 1404 }
@@ -1560,15 +1418,14 @@ pin_trait!(D5Pin, Instance);
1560pin_trait!(D6Pin, Instance); 1418pin_trait!(D6Pin, Instance);
1561pin_trait!(D7Pin, Instance); 1419pin_trait!(D7Pin, Instance);
1562 1420
1563cfg_if::cfg_if! { 1421#[cfg(sdmmc_v1)]
1564 if #[cfg(sdmmc_v1)] { 1422dma_trait!(SdmmcDma, Instance);
1565 dma_trait!(SdmmcDma, Instance); 1423
1566 } else if #[cfg(sdmmc_v2)] { 1424// SDMMCv2 uses internal DMA
1567 // SDMMCv2 uses internal DMA 1425#[cfg(sdmmc_v2)]
1568 pub trait SdmmcDma<T: Instance> {} 1426pub trait SdmmcDma<T: Instance> {}
1569 impl<T: Instance> SdmmcDma<T> for NoDma {} 1427#[cfg(sdmmc_v2)]
1570 } 1428impl<T: Instance> SdmmcDma<T> for NoDma {}
1571}
1572 1429
1573cfg_if::cfg_if! { 1430cfg_if::cfg_if! {
1574 // TODO, these could not be implemented, because required clocks are not exposed in RCC: 1431 // TODO, these could not be implemented, because required clocks are not exposed in RCC:
@@ -1630,9 +1487,8 @@ foreach_peripheral!(
1630 impl sealed::Instance for peripherals::$inst { 1487 impl sealed::Instance for peripherals::$inst {
1631 type Interrupt = crate::interrupt::$inst; 1488 type Interrupt = crate::interrupt::$inst;
1632 1489
1633 fn inner() -> SdmmcInner { 1490 fn regs() -> RegBlock {
1634 const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); 1491 crate::pac::$inst
1635 INNER
1636 } 1492 }
1637 1493
1638 fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { 1494 fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker {
@@ -1651,8 +1507,6 @@ foreach_peripheral!(
1651 1507
1652#[cfg(feature = "embedded-sdmmc")] 1508#[cfg(feature = "embedded-sdmmc")]
1653mod sdmmc_rs { 1509mod sdmmc_rs {
1654 use core::future::Future;
1655
1656 use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx}; 1510 use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx};
1657 1511
1658 use super::*; 1512 use super::*;
@@ -1660,49 +1514,37 @@ mod sdmmc_rs {
1660 impl<'d, T: Instance, Dma: SdmmcDma<T>> BlockDevice for Sdmmc<'d, T, Dma> { 1514 impl<'d, T: Instance, Dma: SdmmcDma<T>> BlockDevice for Sdmmc<'d, T, Dma> {
1661 type Error = Error; 1515 type Error = Error;
1662 1516
1663 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a 1517 async fn read(
1664 where 1518 &mut self,
1665 Self: 'a; 1519 blocks: &mut [Block],
1666
1667 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
1668 where
1669 Self: 'a;
1670
1671 fn read<'a>(
1672 &'a mut self,
1673 blocks: &'a mut [Block],
1674 start_block_idx: BlockIdx, 1520 start_block_idx: BlockIdx,
1675 _reason: &str, 1521 _reason: &str,
1676 ) -> Self::ReadFuture<'a> { 1522 ) -> Result<(), Self::Error> {
1677 async move { 1523 let mut address = start_block_idx.0;
1678 let mut address = start_block_idx.0;
1679 1524
1680 for block in blocks.iter_mut() { 1525 for block in blocks.iter_mut() {
1681 let block: &mut [u8; 512] = &mut block.contents; 1526 let block: &mut [u8; 512] = &mut block.contents;
1682 1527
1683 // NOTE(unsafe) Block uses align(4) 1528 // NOTE(unsafe) Block uses align(4)
1684 let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; 1529 let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) };
1685 self.read_block(address, block).await?; 1530 self.read_block(address, block).await?;
1686 address += 1; 1531 address += 1;
1687 }
1688 Ok(())
1689 } 1532 }
1533 Ok(())
1690 } 1534 }
1691 1535
1692 fn write<'a>(&'a mut self, blocks: &'a [Block], start_block_idx: BlockIdx) -> Self::WriteFuture<'a> { 1536 async fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
1693 async move { 1537 let mut address = start_block_idx.0;
1694 let mut address = start_block_idx.0;
1695 1538
1696 for block in blocks.iter() { 1539 for block in blocks.iter() {
1697 let block: &[u8; 512] = &block.contents; 1540 let block: &[u8; 512] = &block.contents;
1698 1541
1699 // NOTE(unsafe) DataBlock uses align 4 1542 // NOTE(unsafe) DataBlock uses align 4
1700 let block = unsafe { &*(block as *const _ as *const DataBlock) }; 1543 let block = unsafe { &*(block as *const _ as *const DataBlock) };
1701 self.write_block(address, block).await?; 1544 self.write_block(address, block).await?;
1702 address += 1; 1545 address += 1;
1703 }
1704 Ok(())
1705 } 1546 }
1547 Ok(())
1706 } 1548 }
1707 1549
1708 fn num_blocks(&self) -> Result<BlockCount, Self::Error> { 1550 fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 481ea4abc..492d0649a 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -7,8 +7,7 @@ use embassy_futures::join::join;
7use embassy_hal_common::{into_ref, PeripheralRef}; 7use embassy_hal_common::{into_ref, PeripheralRef};
8pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; 8pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
9 9
10use self::sealed::WordSize; 10use crate::dma::{slice_ptr_parts, word, Transfer};
11use crate::dma::{slice_ptr_parts, Transfer};
12use crate::gpio::sealed::{AFType, Pin as _}; 11use crate::gpio::sealed::{AFType, Pin as _};
13use crate::gpio::{AnyPin, Pull}; 12use crate::gpio::{AnyPin, Pull};
14use crate::pac::spi::{regs, vals, Spi as Regs}; 13use crate::pac::spi::{regs, vals, Spi as Regs};
@@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> {
78 miso: Option<PeripheralRef<'d, AnyPin>>, 77 miso: Option<PeripheralRef<'d, AnyPin>>,
79 txdma: PeripheralRef<'d, Tx>, 78 txdma: PeripheralRef<'d, Tx>,
80 rxdma: PeripheralRef<'d, Rx>, 79 rxdma: PeripheralRef<'d, Rx>,
81 current_word_size: WordSize, 80 current_word_size: word_impl::Config,
82} 81}
83 82
84impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { 83impl<'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> {
178 ) 177 )
179 } 178 }
180 179
180 pub fn new_txonly_nosck(
181 peri: impl Peripheral<P = T> + 'd,
182 mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
183 txdma: impl Peripheral<P = Tx> + 'd,
184 rxdma: impl Peripheral<P = Rx> + 'd, // TODO: remove
185 freq: Hertz,
186 config: Config,
187 ) -> Self {
188 into_ref!(mosi);
189 unsafe {
190 mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down);
191 mosi.set_speed(crate::gpio::Speed::Medium);
192 }
193
194 Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config)
195 }
196
181 /// Useful for on chip peripherals like SUBGHZ which are hardwired. 197 /// Useful for on chip peripherals like SUBGHZ which are hardwired.
182 /// The bus can optionally be exposed externally with `Spi::new()` still. 198 /// The bus can optionally be exposed externally with `Spi::new()` still.
183 #[allow(dead_code)] 199 #[allow(dead_code)]
@@ -234,14 +250,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
234 if mosi.is_none() { 250 if mosi.is_none() {
235 w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); 251 w.set_rxonly(vals::Rxonly::OUTPUTDISABLED);
236 } 252 }
237 w.set_dff(WordSize::EightBit.dff()) 253 w.set_dff(<u8 as sealed::Word>::CONFIG)
238 }); 254 });
239 } 255 }
240 #[cfg(spi_v2)] 256 #[cfg(spi_v2)]
241 unsafe { 257 unsafe {
242 T::REGS.cr2().modify(|w| { 258 T::REGS.cr2().modify(|w| {
243 w.set_frxth(WordSize::EightBit.frxth()); 259 let (ds, frxth) = <u8 as sealed::Word>::CONFIG;
244 w.set_ds(WordSize::EightBit.ds()); 260 w.set_frxth(frxth);
261 w.set_ds(ds);
245 w.set_ssoe(false); 262 w.set_ssoe(false);
246 }); 263 });
247 T::REGS.cr1().modify(|w| { 264 T::REGS.cr1().modify(|w| {
@@ -279,7 +296,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
279 T::REGS.cfg1().modify(|w| { 296 T::REGS.cfg1().modify(|w| {
280 w.set_crcen(false); 297 w.set_crcen(false);
281 w.set_mbr(br); 298 w.set_mbr(br);
282 w.set_dsize(WordSize::EightBit.dsize()); 299 w.set_dsize(<u8 as sealed::Word>::CONFIG);
300 w.set_fthlv(vals::Fthlv::ONEFRAME);
283 }); 301 });
284 T::REGS.cr2().modify(|w| { 302 T::REGS.cr2().modify(|w| {
285 w.set_tsize(0); 303 w.set_tsize(0);
@@ -297,7 +315,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
297 miso, 315 miso,
298 txdma, 316 txdma,
299 rxdma, 317 rxdma,
300 current_word_size: WordSize::EightBit, 318 current_word_size: <u8 as sealed::Word>::CONFIG,
301 } 319 }
302 } 320 }
303 321
@@ -355,7 +373,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
355 } 373 }
356 } 374 }
357 375
358 fn set_word_size(&mut self, word_size: WordSize) { 376 fn set_word_size(&mut self, word_size: word_impl::Config) {
359 if self.current_word_size == word_size { 377 if self.current_word_size == word_size {
360 return; 378 return;
361 } 379 }
@@ -364,7 +382,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
364 unsafe { 382 unsafe {
365 T::REGS.cr1().modify(|reg| { 383 T::REGS.cr1().modify(|reg| {
366 reg.set_spe(false); 384 reg.set_spe(false);
367 reg.set_dff(word_size.dff()) 385 reg.set_dff(word_size)
368 }); 386 });
369 T::REGS.cr1().modify(|reg| { 387 T::REGS.cr1().modify(|reg| {
370 reg.set_spe(true); 388 reg.set_spe(true);
@@ -376,8 +394,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
376 w.set_spe(false); 394 w.set_spe(false);
377 }); 395 });
378 T::REGS.cr2().modify(|w| { 396 T::REGS.cr2().modify(|w| {
379 w.set_frxth(word_size.frxth()); 397 w.set_frxth(word_size.1);
380 w.set_ds(word_size.ds()); 398 w.set_ds(word_size.0);
381 }); 399 });
382 T::REGS.cr1().modify(|w| { 400 T::REGS.cr1().modify(|w| {
383 w.set_spe(true); 401 w.set_spe(true);
@@ -393,7 +411,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
393 w.set_spe(false); 411 w.set_spe(false);
394 }); 412 });
395 T::REGS.cfg1().modify(|w| { 413 T::REGS.cfg1().modify(|w| {
396 w.set_dsize(word_size.dsize()); 414 w.set_dsize(word_size);
397 }); 415 });
398 T::REGS.cr1().modify(|w| { 416 T::REGS.cr1().modify(|w| {
399 w.set_csusp(false); 417 w.set_csusp(false);
@@ -412,7 +430,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
412 return Ok(()); 430 return Ok(());
413 } 431 }
414 432
415 self.set_word_size(W::WORDSIZE); 433 self.set_word_size(W::CONFIG);
416 unsafe { 434 unsafe {
417 T::REGS.cr1().modify(|w| { 435 T::REGS.cr1().modify(|w| {
418 w.set_spe(false); 436 w.set_spe(false);
@@ -421,8 +439,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
421 439
422 let tx_request = self.txdma.request(); 440 let tx_request = self.txdma.request();
423 let tx_dst = T::REGS.tx_ptr(); 441 let tx_dst = T::REGS.tx_ptr();
424 unsafe { self.txdma.start_write(tx_request, data, tx_dst, Default::default()) } 442 let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) };
425 let tx_f = Transfer::new(&mut self.txdma);
426 443
427 unsafe { 444 unsafe {
428 set_txdmaen(T::REGS, true); 445 set_txdmaen(T::REGS, true);
@@ -451,7 +468,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
451 return Ok(()); 468 return Ok(());
452 } 469 }
453 470
454 self.set_word_size(W::WORDSIZE); 471 self.set_word_size(W::CONFIG);
455 unsafe { 472 unsafe {
456 T::REGS.cr1().modify(|w| { 473 T::REGS.cr1().modify(|w| {
457 w.set_spe(false); 474 w.set_spe(false);
@@ -468,13 +485,21 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
468 485
469 let rx_request = self.rxdma.request(); 486 let rx_request = self.rxdma.request();
470 let rx_src = T::REGS.rx_ptr(); 487 let rx_src = T::REGS.rx_ptr();
471 unsafe { self.rxdma.start_read(rx_request, rx_src, data, Default::default()) }; 488 let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) };
472 let rx_f = Transfer::new(&mut self.rxdma);
473 489
474 let tx_request = self.txdma.request(); 490 let tx_request = self.txdma.request();
475 let tx_dst = T::REGS.tx_ptr(); 491 let tx_dst = T::REGS.tx_ptr();
476 let clock_byte = 0x00u8; 492 let clock_byte = 0x00u8;
477 let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); 493 let tx_f = unsafe {
494 Transfer::new_write_repeated(
495 &mut self.txdma,
496 tx_request,
497 &clock_byte,
498 clock_byte_count,
499 tx_dst,
500 Default::default(),
501 )
502 };
478 503
479 unsafe { 504 unsafe {
480 set_txdmaen(T::REGS, true); 505 set_txdmaen(T::REGS, true);
@@ -506,7 +531,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
506 return Ok(()); 531 return Ok(());
507 } 532 }
508 533
509 self.set_word_size(W::WORDSIZE); 534 self.set_word_size(W::CONFIG);
510 unsafe { 535 unsafe {
511 T::REGS.cr1().modify(|w| { 536 T::REGS.cr1().modify(|w| {
512 w.set_spe(false); 537 w.set_spe(false);
@@ -521,13 +546,11 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
521 546
522 let rx_request = self.rxdma.request(); 547 let rx_request = self.rxdma.request();
523 let rx_src = T::REGS.rx_ptr(); 548 let rx_src = T::REGS.rx_ptr();
524 unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) }; 549 let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) };
525 let rx_f = Transfer::new(&mut self.rxdma);
526 550
527 let tx_request = self.txdma.request(); 551 let tx_request = self.txdma.request();
528 let tx_dst = T::REGS.tx_ptr(); 552 let tx_dst = T::REGS.tx_ptr();
529 unsafe { self.txdma.start_write(tx_request, write, tx_dst, Default::default()) } 553 let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) };
530 let tx_f = Transfer::new(&mut self.txdma);
531 554
532 unsafe { 555 unsafe {
533 set_txdmaen(T::REGS, true); 556 set_txdmaen(T::REGS, true);
@@ -566,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
566 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { 589 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
567 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } 590 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
568 flush_rx_fifo(T::REGS); 591 flush_rx_fifo(T::REGS);
569 self.set_word_size(W::WORDSIZE); 592 self.set_word_size(W::CONFIG);
570 for word in words.iter() { 593 for word in words.iter() {
571 let _ = transfer_word(T::REGS, *word)?; 594 let _ = transfer_word(T::REGS, *word)?;
572 } 595 }
@@ -576,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
576 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 599 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
577 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } 600 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
578 flush_rx_fifo(T::REGS); 601 flush_rx_fifo(T::REGS);
579 self.set_word_size(W::WORDSIZE); 602 self.set_word_size(W::CONFIG);
580 for word in words.iter_mut() { 603 for word in words.iter_mut() {
581 *word = transfer_word(T::REGS, W::default())?; 604 *word = transfer_word(T::REGS, W::default())?;
582 } 605 }
@@ -586,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
586 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 609 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
587 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } 610 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
588 flush_rx_fifo(T::REGS); 611 flush_rx_fifo(T::REGS);
589 self.set_word_size(W::WORDSIZE); 612 self.set_word_size(W::CONFIG);
590 for word in words.iter_mut() { 613 for word in words.iter_mut() {
591 *word = transfer_word(T::REGS, *word)?; 614 *word = transfer_word(T::REGS, *word)?;
592 } 615 }
@@ -596,7 +619,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
596 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 619 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
597 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } 620 unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
598 flush_rx_fifo(T::REGS); 621 flush_rx_fifo(T::REGS);
599 self.set_word_size(W::WORDSIZE); 622 self.set_word_size(W::CONFIG);
600 let len = read.len().max(write.len()); 623 let len = read.len().max(write.len());
601 for i in 0..len { 624 for i in 0..len {
602 let wb = write.get(i).copied().unwrap_or_default(); 625 let wb = write.get(i).copied().unwrap_or_default();
@@ -928,70 +951,89 @@ pub(crate) mod sealed {
928 const REGS: Regs; 951 const REGS: Regs;
929 } 952 }
930 953
931 pub trait Word: Copy + 'static { 954 pub trait Word {
932 const WORDSIZE: WordSize; 955 const CONFIG: word_impl::Config;
933 }
934
935 impl Word for u8 {
936 const WORDSIZE: WordSize = WordSize::EightBit;
937 }
938 impl Word for u16 {
939 const WORDSIZE: WordSize = WordSize::SixteenBit;
940 } 956 }
957}
941 958
942 #[derive(Copy, Clone, PartialOrd, PartialEq)] 959pub trait Word: word::Word + sealed::Word {}
943 pub enum WordSize {
944 EightBit,
945 SixteenBit,
946 }
947 960
948 impl WordSize { 961macro_rules! impl_word {
949 #[cfg(any(spi_v1, spi_f1))] 962 ($T:ty, $config:expr) => {
950 pub fn dff(&self) -> vals::Dff { 963 impl sealed::Word for $T {
951 match self { 964 const CONFIG: Config = $config;
952 WordSize::EightBit => vals::Dff::EIGHTBIT,
953 WordSize::SixteenBit => vals::Dff::SIXTEENBIT,
954 }
955 } 965 }
966 impl Word for $T {}
967 };
968}
956 969
957 #[cfg(spi_v2)] 970#[cfg(any(spi_v1, spi_f1))]
958 pub fn ds(&self) -> vals::Ds { 971mod word_impl {
959 match self { 972 use super::*;
960 WordSize::EightBit => vals::Ds::EIGHTBIT,
961 WordSize::SixteenBit => vals::Ds::SIXTEENBIT,
962 }
963 }
964 973
965 #[cfg(spi_v2)] 974 pub type Config = vals::Dff;
966 pub fn frxth(&self) -> vals::Frxth {
967 match self {
968 WordSize::EightBit => vals::Frxth::QUARTER,
969 WordSize::SixteenBit => vals::Frxth::HALF,
970 }
971 }
972 975
973 #[cfg(any(spi_v3, spi_v4, spi_v5))] 976 impl_word!(u8, vals::Dff::EIGHTBIT);
974 pub fn dsize(&self) -> u8 { 977 impl_word!(u16, vals::Dff::SIXTEENBIT);
975 match self { 978}
976 WordSize::EightBit => 0b0111,
977 WordSize::SixteenBit => 0b1111,
978 }
979 }
980 979
981 #[cfg(any(spi_v3, spi_v4, spi_v5))] 980#[cfg(any(spi_v2))]
982 pub fn _frxth(&self) -> vals::Fthlv { 981mod word_impl {
983 match self { 982 use super::*;
984 WordSize::EightBit => vals::Fthlv::ONEFRAME, 983
985 WordSize::SixteenBit => vals::Fthlv::ONEFRAME, 984 pub type Config = (vals::Ds, vals::Frxth);
986 } 985
987 } 986 impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER));
988 } 987 impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER));
988 impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER));
989 impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER));
990 impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER));
991 impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF));
992 impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF));
993 impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF));
994 impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF));
995 impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF));
996 impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF));
997 impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF));
998 impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF));
989} 999}
990 1000
991pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} 1001#[cfg(any(spi_v3, spi_v4, spi_v5))]
1002mod word_impl {
1003 use super::*;
992 1004
993impl Word for u8 {} 1005 pub type Config = u8;
994impl Word for u16 {} 1006
1007 impl_word!(word::U4, 4 - 1);
1008 impl_word!(word::U5, 5 - 1);
1009 impl_word!(word::U6, 6 - 1);
1010 impl_word!(word::U7, 7 - 1);
1011 impl_word!(u8, 8 - 1);
1012 impl_word!(word::U9, 9 - 1);
1013 impl_word!(word::U10, 10 - 1);
1014 impl_word!(word::U11, 11 - 1);
1015 impl_word!(word::U12, 12 - 1);
1016 impl_word!(word::U13, 13 - 1);
1017 impl_word!(word::U14, 14 - 1);
1018 impl_word!(word::U15, 15 - 1);
1019 impl_word!(u16, 16 - 1);
1020 impl_word!(word::U17, 17 - 1);
1021 impl_word!(word::U18, 18 - 1);
1022 impl_word!(word::U19, 19 - 1);
1023 impl_word!(word::U20, 20 - 1);
1024 impl_word!(word::U21, 21 - 1);
1025 impl_word!(word::U22, 22 - 1);
1026 impl_word!(word::U23, 23 - 1);
1027 impl_word!(word::U24, 24 - 1);
1028 impl_word!(word::U25, 25 - 1);
1029 impl_word!(word::U26, 26 - 1);
1030 impl_word!(word::U27, 27 - 1);
1031 impl_word!(word::U28, 28 - 1);
1032 impl_word!(word::U29, 29 - 1);
1033 impl_word!(word::U30, 30 - 1);
1034 impl_word!(word::U31, 31 - 1);
1035 impl_word!(u32, 32 - 1);
1036}
995 1037
996pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} 1038pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
997pin_trait!(SckPin, Instance); 1039pin_trait!(SckPin, Instance);
diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/traits.rs
index 45cc4e725..ffce7bd42 100644
--- a/embassy-stm32/src/traits.rs
+++ b/embassy-stm32/src/traits.rs
@@ -34,7 +34,7 @@ macro_rules! dma_trait_impl {
34 (crate::$mod:ident::$trait:ident, $instance:ident, {dmamux: $dmamux:ident}, $request:expr) => { 34 (crate::$mod:ident::$trait:ident, $instance:ident, {dmamux: $dmamux:ident}, $request:expr) => {
35 impl<T> crate::$mod::$trait<crate::peripherals::$instance> for T 35 impl<T> crate::$mod::$trait<crate::peripherals::$instance> for T
36 where 36 where
37 T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux>, 37 T: crate::dma::Channel + crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
38 { 38 {
39 fn request(&self) -> crate::dma::Request { 39 fn request(&self) -> crate::dma::Request {
40 $request 40 $request
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index a42eede18..b8656b586 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -6,11 +6,11 @@ use core::sync::atomic::{compiler_fence, Ordering};
6use core::task::Poll; 6use core::task::Poll;
7 7
8use embassy_cortex_m::interrupt::InterruptExt; 8use embassy_cortex_m::interrupt::InterruptExt;
9use embassy_futures::select::{select, Either};
10use embassy_hal_common::drop::OnDrop; 9use embassy_hal_common::drop::OnDrop;
11use embassy_hal_common::{into_ref, PeripheralRef}; 10use embassy_hal_common::{into_ref, PeripheralRef};
11use futures::future::{select, Either};
12 12
13use crate::dma::NoDma; 13use crate::dma::{NoDma, Transfer};
14use crate::gpio::sealed::AFType; 14use crate::gpio::sealed::AFType;
15#[cfg(any(lpuart_v1, lpuart_v2))] 15#[cfg(any(lpuart_v1, lpuart_v2))]
16use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; 16use crate::pac::lpuart::{regs, vals, Lpuart as Regs};
@@ -91,7 +91,7 @@ enum ReadCompletionEvent {
91 // DMA Read transfer completed first 91 // DMA Read transfer completed first
92 DmaCompleted, 92 DmaCompleted,
93 // Idle line detected first 93 // Idle line detected first
94 Idle, 94 Idle(usize),
95} 95}
96 96
97pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { 97pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
@@ -183,7 +183,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
183 } 183 }
184 // If we don't assign future to a variable, the data register pointer 184 // If we don't assign future to a variable, the data register pointer
185 // is held across an await and makes the future non-Send. 185 // is held across an await and makes the future non-Send.
186 let transfer = crate::dma::write(ch, request, buffer, tdr(T::regs())); 186 let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) };
187 transfer.await; 187 transfer.await;
188 Ok(()) 188 Ok(())
189 } 189 }
@@ -430,10 +430,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
430 let ch = &mut self.rx_dma; 430 let ch = &mut self.rx_dma;
431 let request = ch.request(); 431 let request = ch.request();
432 432
433 let buffer_len = buffer.len();
434
433 // Start USART DMA 435 // Start USART DMA
434 // will not do anything yet because DMAR is not yet set 436 // will not do anything yet because DMAR is not yet set
435 // future which will complete when DMA Read request completes 437 // future which will complete when DMA Read request completes
436 let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); 438 let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) };
437 439
438 // SAFETY: The only way we might have a problem is using split rx and tx 440 // SAFETY: The only way we might have a problem is using split rx and tx
439 // here we only modify or read Rx related flags, interrupts and DMA channel 441 // 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> {
565 // when transfer is dropped, it will stop the DMA request 567 // when transfer is dropped, it will stop the DMA request
566 let r = match select(transfer, idle).await { 568 let r = match select(transfer, idle).await {
567 // DMA transfer completed first 569 // DMA transfer completed first
568 Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), 570 Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted),
569 571
570 // Idle line detected first 572 // Idle line detected first
571 Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle), 573 Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle(
574 buffer_len - transfer.get_remaining_transfers() as usize,
575 )),
572 576
573 // error occurred 577 // error occurred
574 Either::Second(Err(e)) => Err(e), 578 Either::Right((Err(e), _)) => Err(e),
575 }; 579 };
576 580
577 drop(on_drop); 581 drop(on_drop);
@@ -594,14 +598,9 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
594 // wait for DMA to complete or IDLE line detection if requested 598 // wait for DMA to complete or IDLE line detection if requested
595 let res = self.inner_read_run(buffer, enable_idle_line_detection).await; 599 let res = self.inner_read_run(buffer, enable_idle_line_detection).await;
596 600
597 let ch = &mut self.rx_dma;
598
599 match res { 601 match res {
600 Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), 602 Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),
601 Ok(ReadCompletionEvent::Idle) => { 603 Ok(ReadCompletionEvent::Idle(n)) => Ok(n),
602 let n = buffer_len - (ch.remaining_transfers() as usize);
603 Ok(n)
604 }
605 Err(e) => Err(e), 604 Err(e) => Err(e),
606 } 605 }
607 } 606 }
@@ -973,73 +972,6 @@ mod eio {
973 } 972 }
974} 973}
975 974
976#[cfg(all(
977 feature = "unstable-traits",
978 feature = "nightly",
979 feature = "_todo_embedded_hal_serial"
980))]
981mod eha {
982 use core::future::Future;
983
984 use super::*;
985
986 impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma>
987 where
988 TxDma: crate::usart::TxDma<T>,
989 {
990 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
991
992 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
993 self.write(buf)
994 }
995
996 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
997
998 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
999 async move { Ok(()) }
1000 }
1001 }
1002
1003 impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma>
1004 where
1005 RxDma: crate::usart::RxDma<T>,
1006 {
1007 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1008
1009 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
1010 self.read(buf)
1011 }
1012 }
1013
1014 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma>
1015 where
1016 TxDma: crate::usart::TxDma<T>,
1017 {
1018 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1019
1020 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
1021 self.write(buf)
1022 }
1023
1024 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1025
1026 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
1027 async move { Ok(()) }
1028 }
1029 }
1030
1031 impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma>
1032 where
1033 RxDma: crate::usart::RxDma<T>,
1034 {
1035 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1036
1037 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
1038 self.read(buf)
1039 }
1040 }
1041}
1042
1043#[cfg(feature = "nightly")] 975#[cfg(feature = "nightly")]
1044pub use buffered::*; 976pub use buffered::*;
1045#[cfg(feature = "nightly")] 977#[cfg(feature = "nightly")]
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index cda1917b3..d6a30602d 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -18,9 +18,9 @@ defmt-rtt = { version = "0.4", optional = true }
18panic-reset = { version = "0.1.1" } 18panic-reset = { version = "0.1.1" }
19embedded-hal = { version = "0.2.6" } 19embedded-hal = { version = "0.2.6" }
20 20
21cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 21cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
22cortex-m-rt = "0.7.0" 22cortex-m-rt = "0.7.0"
23 23
24[features] 24[features]
25ed25519-dalek = ["embassy-boot/ed25519-dalek"] 25ed25519-dalek = ["embassy-boot/ed25519-dalek"]
26ed25519-salty = ["embassy-boot/ed25519-salty"] \ No newline at end of file 26ed25519-salty = ["embassy-boot/ed25519-salty"]
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index 9d34a3691..62ef42d6b 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -18,7 +18,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"], optional = true }
18panic-reset = { version = "0.1.1", optional = true } 18panic-reset = { version = "0.1.1", optional = true }
19embedded-hal = { version = "0.2.6" } 19embedded-hal = { version = "0.2.6" }
20 20
21cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 21cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
22cortex-m-rt = "0.7.0" 22cortex-m-rt = "0.7.0"
23 23
24[features] 24[features]
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml
index 8e978eb24..e5fb1b01d 100644
--- a/examples/boot/application/stm32f3/Cargo.toml
+++ b/examples/boot/application/stm32f3/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml
index fb55b166d..a6ac1cadf 100644
--- a/examples/boot/application/stm32f7/Cargo.toml
+++ b/examples/boot/application/stm32f7/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index ea6b905a4..5b8ee555f 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml
index 6ba18564d..05ce5c10a 100644
--- a/examples/boot/application/stm32l0/Cargo.toml
+++ b/examples/boot/application/stm32l0/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml
index d5b8e3e01..14af99e96 100644
--- a/examples/boot/application/stm32l1/Cargo.toml
+++ b/examples/boot/application/stm32l1/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml
index ccd1fe2ee..90ae97725 100644
--- a/examples/boot/application/stm32l4/Cargo.toml
+++ b/examples/boot/application/stm32l4/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml
index 128afd51e..08403a4ec 100644
--- a/examples/boot/application/stm32wl/Cargo.toml
+++ b/examples/boot/application/stm32wl/Cargo.toml
@@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true }
17panic-reset = { version = "0.1.1" } 17panic-reset = { version = "0.1.1" }
18embedded-hal = { version = "0.2.6" } 18embedded-hal = { version = "0.2.6" }
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22 22
23[features] 23[features]
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml
index 8a6f53643..cd0be5b48 100644
--- a/examples/boot/bootloader/nrf/Cargo.toml
+++ b/examples/boot/bootloader/nrf/Cargo.toml
@@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true }
11 11
12embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } 12embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] }
13embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } 13embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false }
14cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15cortex-m-rt = { version = "0.7" } 15cortex-m-rt = { version = "0.7" }
16cfg-if = "1.0.0" 16cfg-if = "1.0.0"
17 17
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml
index a16cebe31..b4167bcd8 100644
--- a/examples/boot/bootloader/rp/Cargo.toml
+++ b/examples/boot/bootloader/rp/Cargo.toml
@@ -13,7 +13,7 @@ embassy-rp = { path = "../../../../embassy-rp", default-features = false, featur
13embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } 13embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false }
14embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } 14embassy-time = { path = "../../../../embassy-time", features = ["nightly"] }
15 15
16cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
17cortex-m-rt = { version = "0.7" } 17cortex-m-rt = { version = "0.7" }
18embedded-storage = "0.3.0" 18embedded-storage = "0.3.0"
19embedded-storage-async = "0.4.0" 19embedded-storage-async = "0.4.0"
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml
index b1791620f..f2675aa73 100644
--- a/examples/boot/bootloader/stm32/Cargo.toml
+++ b/examples/boot/bootloader/stm32/Cargo.toml
@@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true }
11 11
12embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } 12embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] }
13embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } 13embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false }
14cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 14cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
15cortex-m-rt = { version = "0.7" } 15cortex-m-rt = { version = "0.7" }
16embedded-storage = "0.3.0" 16embedded-storage = "0.3.0"
17embedded-storage-async = "0.4.0" 17embedded-storage-async = "0.4.0"
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml
index 6f7cb8875..0a7141c4e 100644
--- a/examples/nrf-rtos-trace/Cargo.toml
+++ b/examples/nrf-rtos-trace/Cargo.toml
@@ -21,7 +21,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
21embassy-time = { version = "0.1.0", path = "../../embassy-time" } 21embassy-time = { version = "0.1.0", path = "../../embassy-time" }
22embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 22embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
23 23
24cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 24cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
25cortex-m-rt = "0.7.0" 25cortex-m-rt = "0.7.0"
26panic-probe = { version = "0.3" } 26panic-probe = { version = "0.3" }
27futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 27futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index fc614cb80..10c269a76 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -20,14 +20,14 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
20embedded-io = "0.4.0" 20embedded-io = "0.4.0"
21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } 21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true }
22 22
23lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } 23lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true }
24lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } 24lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true }
25 25
26defmt = "0.3" 26defmt = "0.3"
27defmt-rtt = "0.4" 27defmt-rtt = "0.4"
28 28
29static_cell = "1.0" 29static_cell = "1.0"
30cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 30cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
31cortex-m-rt = "0.7.0" 31cortex-m-rt = "0.7.0"
32panic-probe = { version = "0.3", features = ["print-defmt"] } 32panic-probe = { version = "0.3", features = ["print-defmt"] }
33futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 33futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml
index 2c3a12964..ebbc25bc6 100644
--- a/examples/nrf5340/Cargo.toml
+++ b/examples/nrf5340/Cargo.toml
@@ -43,7 +43,7 @@ defmt = "0.3"
43defmt-rtt = "0.4" 43defmt-rtt = "0.4"
44 44
45static_cell = "1.0" 45static_cell = "1.0"
46cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 46cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
47cortex-m-rt = "0.7.0" 47cortex-m-rt = "0.7.0"
48panic-probe = { version = "0.3", features = ["print-defmt"] } 48panic-probe = { version = "0.3", features = ["print-defmt"] }
49futures = { version = "0.3.17", default-features = false, features = [ 49futures = { version = "0.3.17", default-features = false, features = [
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index f0fd27991..8067f7ba5 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -20,7 +20,7 @@ defmt = "0.3"
20defmt-rtt = "0.4" 20defmt-rtt = "0.4"
21 21
22#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 22#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
23cortex-m = { version = "0.7.6" } 23cortex-m = { version = "0.7.6", features = ["inline-asm"] }
24cortex-m-rt = "0.7.0" 24cortex-m-rt = "0.7.0"
25panic-probe = { version = "0.3", features = ["print-defmt"] } 25panic-probe = { version = "0.3", features = ["print-defmt"] }
26futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } 26futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml
index 2ff252622..e969538d3 100644
--- a/examples/stm32f0/Cargo.toml
+++ b/examples/stm32f0/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8 8
9[dependencies] 9[dependencies]
10cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 10cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
11cortex-m-rt = "0.7.0" 11cortex-m-rt = "0.7.0"
12defmt = "0.3" 12defmt = "0.3"
13defmt-rtt = "0.4" 13defmt-rtt = "0.4"
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index d08e00b0b..706b5a722 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15defmt = "0.3" 15defmt = "0.3"
16defmt-rtt = "0.4" 16defmt-rtt = "0.4"
17 17
18cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 18cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
19cortex-m-rt = "0.7.0" 19cortex-m-rt = "0.7.0"
20embedded-hal = "0.2.6" 20embedded-hal = "0.2.6"
21panic-probe = { version = "0.3", features = ["print-defmt"] } 21panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml
index 02045a79f..62947bf49 100644
--- a/examples/stm32f2/Cargo.toml
+++ b/examples/stm32f2/Cargo.toml
@@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.4" 14defmt-rtt = "0.4"
15 15
16cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
17cortex-m-rt = "0.7.0" 17cortex-m-rt = "0.7.0"
18embedded-hal = "0.2.6" 18embedded-hal = "0.2.6"
19panic-probe = { version = "0.3", features = ["print-defmt"] } 19panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index a62eba9ec..7ba9ff0c1 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
15defmt = "0.3" 15defmt = "0.3"
16defmt-rtt = "0.4" 16defmt-rtt = "0.4"
17 17
18cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 18cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
19cortex-m-rt = "0.7.0" 19cortex-m-rt = "0.7.0"
20embedded-hal = "0.2.6" 20embedded-hal = "0.2.6"
21panic-probe = { version = "0.3", features = ["print-defmt"] } 21panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 4b2f3d21c..77985a017 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -8,14 +8,14 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-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"] } 9embassy-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"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-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"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
13embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } 13embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
14 14
15defmt = "0.3" 15defmt = "0.3"
16defmt-rtt = "0.4" 16defmt-rtt = "0.4"
17 17
18cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 18cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
19cortex-m-rt = "0.7.0" 19cortex-m-rt = "0.7.0"
20embedded-hal = "0.2.6" 20embedded-hal = "0.2.6"
21embedded-io = "0.4.0" 21embedded-io = "0.4.0"
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs
index ebdfdb22d..eeecbd321 100644
--- a/examples/stm32f4/src/bin/sdmmc.rs
+++ b/examples/stm32f4/src/bin/sdmmc.rs
@@ -39,7 +39,18 @@ async fn main(_spawner: Spawner) {
39 // Should print 400kHz for initialization 39 // Should print 400kHz for initialization
40 info!("Configured clock: {}", sdmmc.clock().0); 40 info!("Configured clock: {}", sdmmc.clock().0);
41 41
42 unwrap!(sdmmc.init_card(mhz(48)).await); 42 let mut err = None;
43 loop {
44 match sdmmc.init_card(mhz(24)).await {
45 Ok(_) => break,
46 Err(e) => {
47 if err != Some(e) {
48 info!("waiting for card error, retrying: {:?}", e);
49 err = Some(e);
50 }
51 }
52 }
53 }
43 54
44 let card = unwrap!(sdmmc.card()); 55 let card = unwrap!(sdmmc.card());
45 56
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 898e05c12..d9e9d668c 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
16defmt = "0.3" 16defmt = "0.3"
17defmt-rtt = "0.4" 17defmt-rtt = "0.4"
18 18
19cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 19cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
20cortex-m-rt = "0.7.0" 20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22panic-probe = { version = "0.3", features = ["print-defmt"] } 22panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml
index a522fb422..6bbd3a53f 100644
--- a/examples/stm32g0/Cargo.toml
+++ b/examples/stm32g0/Cargo.toml
@@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.4" 14defmt-rtt = "0.4"
15 15
16cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
17cortex-m-rt = "0.7.0" 17cortex-m-rt = "0.7.0"
18embedded-hal = "0.2.6" 18embedded-hal = "0.2.6"
19panic-probe = { version = "0.3", features = ["print-defmt"] } 19panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs
new file mode 100644
index 000000000..81fdd15cb
--- /dev/null
+++ b/examples/stm32g0/src/bin/spi_neopixel.rs
@@ -0,0 +1,101 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::dma::word::U5;
8use embassy_stm32::dma::NoDma;
9use embassy_stm32::spi::{Config, Spi};
10use embassy_stm32::time::Hertz;
11use embassy_time::{Duration, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14const NR_PIXELS: usize = 15;
15const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw
16const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL;
17
18struct RGB {
19 r: u8,
20 g: u8,
21 b: u8,
22}
23impl Default for RGB {
24 fn default() -> RGB {
25 RGB { r: 0, g: 0, b: 0 }
26 }
27}
28pub struct Ws2812 {
29 // Note that the U5 type controls the selection of 5 bits to output
30 bitbuffer: [U5; TOTAL_BITS],
31}
32
33impl Ws2812 {
34 pub fn new() -> Ws2812 {
35 Ws2812 {
36 bitbuffer: [U5(0); TOTAL_BITS],
37 }
38 }
39 fn len(&self) -> usize {
40 return NR_PIXELS;
41 }
42 fn set(&mut self, idx: usize, rgb: RGB) {
43 self.render_color(idx, 0, rgb.g);
44 self.render_color(idx, 8, rgb.r);
45 self.render_color(idx, 16, rgb.b);
46 }
47 // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern
48 fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) {
49 let mut bits = color as usize;
50 let mut idx = pixel_idx * BITS_PER_PIXEL + offset;
51
52 // render one bit in one spi byte. High time first, then the low time
53 // clock should be 4 Mhz, 5 bits, each bit is 0.25 us.
54 // a one bit is send as a pulse of 0.75 high -- 0.50 low
55 // a zero bit is send as a pulse of 0.50 high -- 0.75 low
56 // clock frequency for the neopixel is exact 800 khz
57 // note that the mosi output should have a resistor to ground of 10k,
58 // to assure that between the bursts the line is low
59 for _i in 0..8 {
60 if idx >= TOTAL_BITS {
61 return;
62 }
63 let pattern = match bits & 0x80 {
64 0x80 => 0b0000_1110,
65 _ => 0b000_1100,
66 };
67 bits = bits << 1;
68 self.bitbuffer[idx] = U5(pattern);
69 idx += 1;
70 }
71 }
72}
73
74#[embassy_executor::main]
75async fn main(_spawner: Spawner) {
76 let p = embassy_stm32::init(Default::default());
77 info!("Start test using spi as neopixel driver");
78
79 let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default());
80
81 let mut neopixels = Ws2812::new();
82
83 loop {
84 let mut cnt: usize = 0;
85 for _i in 0..10 {
86 for idx in 0..neopixels.len() {
87 let color = match (cnt + idx) % 3 {
88 0 => RGB { r: 0x21, g: 0, b: 0 },
89 1 => RGB { r: 0, g: 0x31, b: 0 },
90 _ => RGB { r: 0, g: 0, b: 0x41 },
91 };
92 neopixels.set(idx, color);
93 }
94 cnt += 1;
95 // start sending the neopixel bit patters over spi to the neopixel string
96 spi.write(&neopixels.bitbuffer).await.ok();
97 Timer::after(Duration::from_millis(500)).await;
98 }
99 Timer::after(Duration::from_millis(1000)).await;
100 }
101}
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
index 017e89e41..8f7842ed7 100644
--- a/examples/stm32g4/src/bin/pwm.rs
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -15,8 +15,8 @@ async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 15 let p = embassy_stm32::init(Default::default());
16 info!("Hello World!"); 16 info!("Hello World!");
17 17
18 let ch1 = PwmPin::new_ch1(p.PA5); 18 let ch1 = PwmPin::new_ch1(p.PC0);
19 let mut pwm = SimplePwm::new(p.TIM2, Some(ch1), None, None, None, khz(10)); 19 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10));
20 let max = pwm.get_max_duty(); 20 let max = pwm.get_max_duty();
21 pwm.enable(Channel::Ch1); 21 pwm.enable(Channel::Ch1);
22 22
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml
index 149f8a58b..67e6c76a7 100644
--- a/examples/stm32h5/Cargo.toml
+++ b/examples/stm32h5/Cargo.toml
@@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
16defmt = "0.3" 16defmt = "0.3"
17defmt-rtt = "0.4" 17defmt-rtt = "0.4"
18 18
19cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 19cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
20cortex-m-rt = "0.7.0" 20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } 22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 8316498ca..bd175a5b1 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
16defmt = "0.3" 16defmt = "0.3"
17defmt-rtt = "0.4" 17defmt-rtt = "0.4"
18 18
19cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 19cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
20cortex-m-rt = "0.7.0" 20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } 22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index d446d41b2..d08e2b61a 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} 16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
17 17
18lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } 18lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true }
19lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } 19lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true }
20 20
21defmt = "0.3" 21defmt = "0.3"
22defmt-rtt = "0.4" 22defmt-rtt = "0.4"
@@ -24,7 +24,7 @@ defmt-rtt = "0.4"
24embedded-storage = "0.3.0" 24embedded-storage = "0.3.0"
25embedded-io = "0.4.0" 25embedded-io = "0.4.0"
26 26
27cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 27cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
28cortex-m-rt = "0.7.0" 28cortex-m-rt = "0.7.0"
29panic-probe = { version = "0.3", features = ["print-defmt"] } 29panic-probe = { version = "0.3", features = ["print-defmt"] }
30futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 30futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs
index 27d7c29c2..ea01f610c 100644
--- a/examples/stm32l0/src/bin/lorawan.rs
+++ b/examples/stm32l0/src/bin/lorawan.rs
@@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) {
45 45
46 let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); 46 let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap();
47 47
48 let region = region::EU868::default().into(); 48 let region = region::Configuration::new(region::Region::EU868);
49 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); 49 let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG));
50 50
51 defmt::info!("Joining LoRaWAN network"); 51 defmt::info!("Joining LoRaWAN network");
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index e071c5d27..ff95571e6 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.4" 14defmt-rtt = "0.4"
15 15
16cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
17cortex-m-rt = "0.7.0" 17cortex-m-rt = "0.7.0"
18embedded-hal = "0.2.6" 18embedded-hal = "0.2.6"
19panic-probe = { version = "0.3", features = ["print-defmt"] } 19panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index af305a19d..d3dee5250 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -18,7 +18,7 @@ defmt = "0.3"
18defmt-rtt = "0.4" 18defmt-rtt = "0.4"
19panic-probe = { version = "0.3", features = ["print-defmt"] } 19panic-probe = { version = "0.3", features = ["print-defmt"] }
20 20
21cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 21cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
22cortex-m-rt = "0.7.0" 22cortex-m-rt = "0.7.0"
23embedded-hal = "0.2.6" 23embedded-hal = "0.2.6"
24futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 24futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index 65fc1b988..86bc83dab 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -14,7 +14,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
14defmt = "0.3" 14defmt = "0.3"
15defmt-rtt = "0.4" 15defmt-rtt = "0.4"
16 16
17cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
18cortex-m-rt = "0.7.0" 18cortex-m-rt = "0.7.0"
19embedded-hal = "0.2.6" 19embedded-hal = "0.2.6"
20panic-probe = { version = "0.3", features = ["print-defmt"] } 20panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index 835985ec3..18b27b28e 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.4" 14defmt-rtt = "0.4"
15 15
16cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 16cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
17cortex-m-rt = "0.7.0" 17cortex-m-rt = "0.7.0"
18embedded-hal = "0.2.6" 18embedded-hal = "0.2.6"
19panic-probe = { version = "0.3", features = ["print-defmt"] } 19panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index df295ca49..07f136b40 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -11,13 +11,13 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] }
12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } 12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
13 13
14lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } 14lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] }
15lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } 15lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] }
16 16
17defmt = "0.3" 17defmt = "0.3"
18defmt-rtt = "0.4" 18defmt-rtt = "0.4"
19 19
20cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0" 21cortex-m-rt = "0.7.0"
22embedded-hal = "0.2.6" 22embedded-hal = "0.2.6"
23embedded-storage = "0.3.0" 23embedded-storage = "0.3.0"
diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs
index 7f34dd306..32f29cc5d 100644
--- a/examples/stm32wl/src/bin/lorawan.rs
+++ b/examples/stm32wl/src/bin/lorawan.rs
@@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) {
63 radio_config.calibrate_image = CalibrateImage::ISM_863_870; 63 radio_config.calibrate_image = CalibrateImage::ISM_863_870;
64 let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); 64 let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap();
65 65
66 let mut region: region::Configuration = region::EU868::default().into(); 66 let mut region = region::Configuration::new(region::Region::EU868);
67 67
68 // NOTE: This is specific for TTN, as they have a special RX1 delay 68 // NOTE: This is specific for TTN, as they have a special RX1 delay
69 region.set_receive_delay1(5000); 69 region.set_receive_delay1(5000);
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index f7183d167..2301ddc8d 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,7 +1,7 @@
1# Before upgrading check that everything is available on all tier1 targets here: 1# Before upgrading check that everything is available on all tier1 targets here:
2# https://rust-lang.github.io/rustup-components-history 2# https://rust-lang.github.io/rustup-components-history
3[toolchain] 3[toolchain]
4channel = "nightly-2023-04-11" 4channel = "nightly-2023-04-18"
5components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] 5components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
6targets = [ 6targets = [
7 "thumbv7em-none-eabi", 7 "thumbv7em-none-eabi",
diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml
index 9611db3a0..e1744c703 100644
--- a/tests/rp/.cargo/config.toml
+++ b/tests/rp/.cargo/config.toml
@@ -1,6 +1,8 @@
1[unstable] 1[unstable]
2build-std = ["core"] 2# enabling these breaks the float tests during linking, with intrinsics
3build-std-features = ["panic_immediate_abort"] 3# duplicated between embassy-rp and compilter_builtins
4#build-std = ["core"]
5#build-std-features = ["panic_immediate_abort"]
4 6
5[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 7[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6#runner = "teleprobe client run --target rpi-pico --elf" 8#runner = "teleprobe client run --target rpi-pico --elf"
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 36ff735ec..6778f53d7 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } 11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] }
12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
13 13
14defmt = "0.3.0" 14defmt = "0.3.0"
diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs
new file mode 100644
index 000000000..6715271e6
--- /dev/null
+++ b/tests/rp/src/bin/float.rs
@@ -0,0 +1,53 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::pac;
8use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 embassy_rp::init(Default::default());
14 info!("Hello World!");
15
16 const PI_F: f32 = 3.1415926535f32;
17 const PI_D: f64 = 3.14159265358979323846f64;
18
19 unsafe {
20 pac::BUSCTRL
21 .perfsel(0)
22 .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM));
23 }
24
25 for i in 0..=360 {
26 let rad_f = (i as f32) * PI_F / 180.0;
27 info!(
28 "{}° float: {=f32} / {=f32} / {=f32} / {=f32}",
29 i,
30 rad_f,
31 rad_f - PI_F,
32 rad_f + PI_F,
33 rad_f % PI_F
34 );
35 let rad_d = (i as f64) * PI_D / 180.0;
36 info!(
37 "{}° double: {=f64} / {=f64} / {=f64} / {=f64}",
38 i,
39 rad_d,
40 rad_d - PI_D,
41 rad_d + PI_D,
42 rad_d % PI_D
43 );
44 Timer::after(Duration::from_millis(10)).await;
45 }
46
47 let rom_accesses = unsafe { pac::BUSCTRL.perfctr(0).read().perfctr() };
48 // every float operation used here uses at least 10 cycles
49 defmt::assert!(rom_accesses >= 360 * 12 * 10);
50
51 info!("Test OK");
52 cortex_m::asm::bkpt();
53}
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 8b70a1015..3047c34ce 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[features] 7[features]
8stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill 8stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill
9stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo 9stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo
10stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo 10stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo
11stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo 11stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo
12stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo 12stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo
@@ -15,6 +15,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo
15stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo 15stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo
16stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board 16stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
17 17
18sdmmc = []
19
18[dependencies] 20[dependencies]
19embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 21embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
20embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 22embassy-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" }
31embedded-hal-async = { version = "=0.2.0-alpha.1" } 33embedded-hal-async = { version = "=0.2.0-alpha.1" }
32panic-probe = { version = "0.3.0", features = ["print-defmt"] } 34panic-probe = { version = "0.3.0", features = ["print-defmt"] }
33 35
36# BEGIN TESTS
37# Generated by gen_test.py. DO NOT EDIT.
38[[bin]]
39name = "gpio"
40path = "src/bin/gpio.rs"
41required-features = []
42
43[[bin]]
44name = "sdmmc"
45path = "src/bin/sdmmc.rs"
46required-features = [ "sdmmc",]
47
48[[bin]]
49name = "spi"
50path = "src/bin/spi.rs"
51required-features = []
52
53[[bin]]
54name = "spi_dma"
55path = "src/bin/spi_dma.rs"
56required-features = []
57
58[[bin]]
59name = "timer"
60path = "src/bin/timer.rs"
61required-features = []
62
63[[bin]]
64name = "usart"
65path = "src/bin/usart.rs"
66required-features = []
67
68[[bin]]
69name = "usart_dma"
70path = "src/bin/usart_dma.rs"
71required-features = []
72
73# END TESTS
74
34[profile.dev] 75[profile.dev]
35debug = 2 76debug = 2
36debug-assertions = true 77debug-assertions = true
diff --git a/tests/stm32/gen_test.py b/tests/stm32/gen_test.py
new file mode 100644
index 000000000..8ff156c0e
--- /dev/null
+++ b/tests/stm32/gen_test.py
@@ -0,0 +1,44 @@
1import os
2import toml
3from glob import glob
4
5abspath = os.path.abspath(__file__)
6dname = os.path.dirname(abspath)
7os.chdir(dname)
8
9# ======= load test list
10tests = {}
11for f in sorted(glob('./src/bin/*.rs')):
12 name = os.path.splitext(os.path.basename(f))[0]
13 features = []
14 with open(f, 'r') as f:
15 for line in f:
16 if line.startswith('// required-features:'):
17 features = line.split(':', 2)[1].strip().split(',')
18
19 tests[name] = features
20
21# ========= Update Cargo.toml
22
23things = {
24 'bin': [
25 {
26 'name': f'{name}',
27 'path': f'src/bin/{name}.rs',
28 'required-features': features,
29 }
30 for name, features in tests.items()
31 ]
32}
33
34SEPARATOR_START = '# BEGIN TESTS\n'
35SEPARATOR_END = '# END TESTS\n'
36HELP = '# Generated by gen_test.py. DO NOT EDIT.\n'
37with open('Cargo.toml', 'r') as f:
38 data = f.read()
39before, data = data.split(SEPARATOR_START, maxsplit=1)
40_, after = data.split(SEPARATOR_END, maxsplit=1)
41data = before + SEPARATOR_START + HELP + \
42 toml.dumps(things) + SEPARATOR_END + after
43with open('Cargo.toml', 'w') as f:
44 f.write(data)
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs
new file mode 100644
index 000000000..c4e50cb4a
--- /dev/null
+++ b/tests/stm32/src/bin/sdmmc.rs
@@ -0,0 +1,148 @@
1// required-features: sdmmc
2#![no_std]
3#![no_main]
4#![feature(type_alias_impl_trait)]
5
6use defmt::{assert_eq, *};
7use embassy_executor::Spawner;
8use embassy_stm32::sdmmc::{DataBlock, Sdmmc};
9use embassy_stm32::time::mhz;
10use embassy_stm32::{interrupt, Config};
11use {defmt_rtt as _, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 info!("Hello World!");
16
17 let mut config = Config::default();
18 config.rcc.sys_ck = Some(mhz(48));
19 config.rcc.pll48 = true;
20 let p = embassy_stm32::init(config);
21
22 #[cfg(feature = "stm32f429zi")]
23 let (mut sdmmc, mut irq, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = (
24 p.SDIO,
25 interrupt::take!(SDIO),
26 p.DMA2_CH3,
27 p.PC12,
28 p.PD2,
29 p.PC8,
30 p.PC9,
31 p.PC10,
32 p.PC11,
33 );
34
35 // Arbitrary block index
36 let block_idx = 16;
37
38 let mut pattern1 = DataBlock([0u8; 512]);
39 let mut pattern2 = DataBlock([0u8; 512]);
40 for i in 0..512 {
41 pattern1[i] = i as u8;
42 pattern2[i] = !i as u8;
43 }
44
45 let mut block = DataBlock([0u8; 512]);
46
47 // ======== Try 4bit. ==============
48 info!("initializing in 4-bit mode...");
49 let mut s = Sdmmc::new_4bit(
50 &mut sdmmc,
51 &mut irq,
52 &mut dma,
53 &mut clk,
54 &mut cmd,
55 &mut d0,
56 &mut d1,
57 &mut d2,
58 &mut d3,
59 Default::default(),
60 );
61
62 let mut err = None;
63 loop {
64 match s.init_card(mhz(24)).await {
65 Ok(_) => break,
66 Err(e) => {
67 if err != Some(e) {
68 info!("waiting for card: {:?}", e);
69 err = Some(e);
70 }
71 }
72 }
73 }
74
75 let card = unwrap!(s.card());
76
77 info!("Card: {:#?}", Debug2Format(card));
78 info!("Clock: {}", s.clock());
79
80 info!("writing pattern1...");
81 s.write_block(block_idx, &pattern1).await.unwrap();
82
83 info!("reading...");
84 s.read_block(block_idx, &mut block).await.unwrap();
85 assert_eq!(block, pattern1);
86
87 info!("writing pattern2...");
88 s.write_block(block_idx, &pattern2).await.unwrap();
89
90 info!("reading...");
91 s.read_block(block_idx, &mut block).await.unwrap();
92 assert_eq!(block, pattern2);
93
94 drop(s);
95
96 // ======== Try 1bit. ==============
97 info!("initializing in 1-bit mode...");
98 let mut s = Sdmmc::new_1bit(
99 &mut sdmmc,
100 &mut irq,
101 &mut dma,
102 &mut clk,
103 &mut cmd,
104 &mut d0,
105 Default::default(),
106 );
107
108 let mut err = None;
109 loop {
110 match s.init_card(mhz(24)).await {
111 Ok(_) => break,
112 Err(e) => {
113 if err != Some(e) {
114 info!("waiting for card: {:?}", e);
115 err = Some(e);
116 }
117 }
118 }
119 }
120
121 let card = unwrap!(s.card());
122
123 info!("Card: {:#?}", Debug2Format(card));
124 info!("Clock: {}", s.clock());
125
126 info!("reading pattern2 written in 4bit mode...");
127 s.read_block(block_idx, &mut block).await.unwrap();
128 assert_eq!(block, pattern2);
129
130 info!("writing pattern1...");
131 s.write_block(block_idx, &pattern1).await.unwrap();
132
133 info!("reading...");
134 s.read_block(block_idx, &mut block).await.unwrap();
135 assert_eq!(block, pattern1);
136
137 info!("writing pattern2...");
138 s.write_block(block_idx, &pattern2).await.unwrap();
139
140 info!("reading...");
141 s.read_block(block_idx, &mut block).await.unwrap();
142 assert_eq!(block, pattern2);
143
144 drop(s);
145
146 info!("Test OK");
147 cortex_m::asm::bkpt();
148}