diff options
| author | sander <[email protected]> | 2023-04-20 08:45:58 +0200 |
|---|---|---|
| committer | sander <[email protected]> | 2023-04-20 08:45:58 +0200 |
| commit | 510ae7e3dc5bb6a43b1e83e1daed44a2f3546e46 (patch) | |
| tree | 925c3b427316cbb6a6694fed12cbccb790cd91f0 | |
| parent | 3002ee0dcf0ae186867b2fd869daed609d7a4a23 (diff) | |
| parent | 54fe50c685a37c7edaf7bd0fcd2d473109d1374d (diff) | |
Merge commit 'eecc41c2e4911c5f1cd232339999424760de9f06'
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 |
| @@ -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")] | ||
| 135 | impl<T, E> embedded_hal_async::serial::Read for BlockingAsync<T> | ||
| 136 | where | ||
| 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")] | ||
| 160 | impl<T, E> embedded_hal_async::serial::Write for BlockingAsync<T> | ||
| 161 | where | ||
| 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 |
| 177 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 135 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 178 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | 136 | use 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; | |||
| 4 | use atomic_polyfill::{AtomicPtr, Ordering}; | 4 | use atomic_polyfill::{AtomicPtr, Ordering}; |
| 5 | 5 | ||
| 6 | use super::{TaskHeader, TaskRef}; | 6 | use super::{TaskHeader, TaskRef}; |
| 7 | use crate::raw::util::SyncUnsafeCell; | ||
| 7 | 8 | ||
| 8 | pub(crate) struct RunQueueItem { | 9 | pub(crate) struct RunQueueItem { |
| 9 | next: AtomicPtr<TaskHeader>, | 10 | next: SyncUnsafeCell<Option<TaskRef>>, |
| 10 | } | 11 | } |
| 11 | 12 | ||
| 12 | impl RunQueueItem { | 13 | impl 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] |
| 2 | macro_rules! peripherals { | 2 | macro_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] | ||
| 33 | macro_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] |
| 85 | macro_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] | ||
| 80 | macro_rules! into_ref { | 103 | macro_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 | |||
| 38 | embedded-hal = { version = "0.2", features = ["unproven"] } | 38 | embedded-hal = { version = "0.2", features = ["unproven"] } |
| 39 | bit_field = { version = "0.10" } | 39 | bit_field = { version = "0.10" } |
| 40 | 40 | ||
| 41 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } | 41 | lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } |
| 42 | lorawan = { version = "0.7.1", default-features = false } | 42 | lorawan = { 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. |
| 2 | use core::future::{poll_fn, Future}; | 2 | use core::future::poll_fn; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 5 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| @@ -241,14 +241,12 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf | |||
| 241 | impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { | 241 | impl<'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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | |||
| 3 | use defmt::Format; | 1 | use defmt::Format; |
| 4 | use embedded_hal::digital::v2::OutputPin; | 2 | use embedded_hal::digital::v2::OutputPin; |
| 5 | use embedded_hal_async::digital::Wait; | 3 | use 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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | |||
| 3 | use embedded_hal::digital::v2::OutputPin; | 1 | use embedded_hal::digital::v2::OutputPin; |
| 4 | use embedded_hal_async::digital::Wait; | 2 | use embedded_hal_async::digital::Wait; |
| 5 | use embedded_hal_async::spi::*; | 3 | use 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; | |||
| 27 | use embassy_time::{Instant, Timer}; | 27 | use embassy_time::{Instant, Timer}; |
| 28 | use futures::pin_mut; | 28 | use futures::pin_mut; |
| 29 | use heapless::Vec; | 29 | use heapless::Vec; |
| 30 | use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; | ||
| 30 | #[cfg(feature = "dhcpv4")] | 31 | #[cfg(feature = "dhcpv4")] |
| 31 | use smoltcp::iface::SocketHandle; | 32 | use smoltcp::socket::dhcpv4::{self, RetryConfig}; |
| 32 | use smoltcp::iface::{Interface, SocketSet, SocketStorage}; | ||
| 33 | #[cfg(feature = "dhcpv4")] | 33 | #[cfg(feature = "dhcpv4")] |
| 34 | use smoltcp::socket::dhcpv4; | ||
| 35 | use smoltcp::socket::dhcpv4::RetryConfig; | ||
| 36 | use smoltcp::time::Duration; | 34 | use smoltcp::time::Duration; |
| 37 | // smoltcp reexports | 35 | // smoltcp reexports |
| 38 | pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; | 36 | pub 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)] |
| 80 | pub struct DhcpConfig { | 79 | pub 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")] | ||
| 91 | impl Default for DhcpConfig { | 91 | impl 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 | ))] | ||
| 1001 | mod 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 } | |||
| 61 | embedded-storage = { version = "0.3" } | 61 | embedded-storage = { version = "0.3" } |
| 62 | rand_core = "0.6.4" | 62 | rand_core = "0.6.4" |
| 63 | 63 | ||
| 64 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } | 64 | rp-pac = { version = "1", features = ["rt"] } |
| 65 | #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } | ||
| 66 | 65 | ||
| 67 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 66 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 68 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} | 67 | embedded-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 | |||
| 4 | use super::{Float, Int}; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMAdd { | ||
| 8 | fn rom_add(self, b: Self) -> Self; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMAdd for f32 { | ||
| 12 | fn rom_add(self, b: Self) -> Self { | ||
| 13 | rom_data::float_funcs::fadd(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMAdd for f64 { | ||
| 18 | fn rom_add(self, b: Self) -> Self { | ||
| 19 | rom_data::double_funcs::dadd(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn 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 | |||
| 54 | intrinsics! { | ||
| 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 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMCmp { | ||
| 8 | fn rom_cmp(self, b: Self) -> i32; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMCmp for f32 { | ||
| 12 | fn rom_cmp(self, b: Self) -> i32 { | ||
| 13 | rom_data::float_funcs::fcmp(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMCmp for f64 { | ||
| 18 | fn rom_cmp(self, b: Self) -> i32 { | ||
| 19 | rom_data::double_funcs::dcmp(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn 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 | |||
| 31 | fn 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 | |||
| 39 | intrinsics! { | ||
| 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 | |||
| 4 | use super::Float; | ||
| 5 | use 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 | |||
| 11 | intrinsics! { | ||
| 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 | |||
| 4 | use super::Float; | ||
| 5 | use 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] | ||
| 14 | fn save_divider_and_call<F, R>(f: F) -> R | ||
| 15 | where | ||
| 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 | |||
| 61 | fn save_divider<F, R>(f: F) -> R | ||
| 62 | where | ||
| 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 | |||
| 75 | trait ROMDiv { | ||
| 76 | fn rom_div(self, b: Self) -> Self; | ||
| 77 | } | ||
| 78 | |||
| 79 | impl 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 | |||
| 86 | impl 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 | |||
| 93 | fn 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 | |||
| 128 | intrinsics! { | ||
| 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 | |||
| 4 | use crate::float::{Float, Int}; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait 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 | |||
| 19 | impl 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 | |||
| 59 | impl 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 | |||
| 98 | fn 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 | |||
| 108 | fn 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 | |||
| 116 | fn 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 | |||
| 124 | fn exp<F: Float + ROMFunctions>(f: F) -> F { | ||
| 125 | if f.is_nan() { | ||
| 126 | F::NAN | ||
| 127 | } else { | ||
| 128 | f.exp() | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | fn 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 | |||
| 140 | fn 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 | |||
| 148 | fn 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 | |||
| 156 | fn 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 | ||
| 165 | mod 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 | |||
| 4 | use core::ops; | ||
| 5 | |||
| 6 | // Borrowed and simplified from compiler-builtins so we can use bit ops | ||
| 7 | // on floating point without macro soup. | ||
| 8 | pub(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 | |||
| 33 | macro_rules! int_impl { | ||
| 34 | ($ty:ty) => { | ||
| 35 | impl Int for $ty { | ||
| 36 | const ZERO: Self = 0; | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | int_impl!(u32); | ||
| 42 | int_impl!(u64); | ||
| 43 | |||
| 44 | pub(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 | |||
| 112 | macro_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 | |||
| 141 | float_impl!(f32, u32, 32, 23); | ||
| 142 | float_impl!(f64, u64, 64, 52); | ||
| 143 | |||
| 144 | mod add_sub; | ||
| 145 | mod cmp; | ||
| 146 | mod conv; | ||
| 147 | mod div; | ||
| 148 | mod functions; | ||
| 149 | mod 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 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMMul { | ||
| 8 | fn rom_mul(self, b: Self) -> Self; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMMul for f32 { | ||
| 12 | fn rom_mul(self, b: Self) -> Self { | ||
| 13 | rom_data::float_funcs::fmul(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMMul for f64 { | ||
| 18 | fn rom_mul(self, b: Self) -> Self { | ||
| 19 | rom_data::double_funcs::dmul(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn 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 | |||
| 57 | intrinsics! { | ||
| 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")] | ||
| 288 | core::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 | |||
| 355 | macro_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 | |||
| 405 | division_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 | |||
| 412 | division_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 | |||
| 419 | fn 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 | |||
| 427 | fn 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 | ||
| 437 | struct 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 | |||
| 444 | intrinsics! { | ||
| 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 | ||
| 13 | pub mod adc; | 13 | pub mod adc; |
| 14 | pub mod dma; | 14 | pub mod dma; |
| 15 | mod float; | ||
| 15 | pub mod gpio; | 16 | pub mod gpio; |
| 16 | pub mod i2c; | 17 | pub mod i2c; |
| 17 | pub mod interrupt; | 18 | pub mod interrupt; |
| @@ -44,9 +45,9 @@ pub use embassy_cortex_m::executor; | |||
| 44 | pub use embassy_cortex_m::interrupt::_export::interrupt; | 45 | pub use embassy_cortex_m::interrupt::_export::interrupt; |
| 45 | pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 46 | pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| 46 | #[cfg(feature = "unstable-pac")] | 47 | #[cfg(feature = "unstable-pac")] |
| 47 | pub use rp2040_pac2 as pac; | 48 | pub use rp_pac as pac; |
| 48 | #[cfg(not(feature = "unstable-pac"))] | 49 | #[cfg(not(feature = "unstable-pac"))] |
| 49 | pub(crate) use rp2040_pac2 as pac; | 50 | pub(crate) use rp_pac as pac; |
| 50 | 51 | ||
| 51 | embassy_hal_common::peripherals! { | 52 | embassy_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))] | ||
| 372 | pub fn soft_float_table() -> *const usize { | 364 | pub 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))] | ||
| 382 | pub fn soft_double_table() -> *const usize { | 375 | pub 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 | ))] | ||
| 735 | mod 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}; | |||
| 5 | use crate::dma::{AnyChannel, Channel}; | 5 | use crate::dma::{AnyChannel, Channel}; |
| 6 | use crate::gpio::sealed::Pin; | 6 | use crate::gpio::sealed::Pin; |
| 7 | use crate::gpio::AnyPin; | 7 | use crate::gpio::AnyPin; |
| 8 | use crate::pac::io::vals::{Inover, Outover}; | ||
| 8 | use crate::{pac, peripherals, Peripheral}; | 9 | use 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 | ||
| 58 | impl Default for Config { | 67 | impl 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 | ||
| 169 | impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { | 182 | impl<'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 | ))] | ||
| 659 | mod 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 | |||
| 709 | mod sealed { | 695 | mod 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" | |||
| 55 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 55 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 56 | rand_core = "0.6.3" | 56 | rand_core = "0.6.3" |
| 57 | sdio-host = "0.5.0" | 57 | sdio-host = "0.5.0" |
| 58 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } | 58 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } |
| 59 | critical-section = "1.1" | 59 | critical-section = "1.1" |
| 60 | atomic-polyfill = "1.0.1" | 60 | atomic-polyfill = "1.0.1" |
| 61 | stm32-metapac = "6" | 61 | stm32-metapac = "6" |
| @@ -66,6 +66,7 @@ stm32-fmc = "0.2.4" | |||
| 66 | seq-macro = "0.3.0" | 66 | seq-macro = "0.3.0" |
| 67 | cfg-if = "1.0.0" | 67 | cfg-if = "1.0.0" |
| 68 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | 68 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } |
| 69 | chrono = { version = "^0.4", default-features = false, optional = true} | ||
| 69 | 70 | ||
| 70 | [dev-dependencies] | 71 | [dev-dependencies] |
| 71 | critical-section = { version = "1.1", features = ["std"] } | 72 | critical-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; | |||
| 4 | use embassy_hal_common::{into_ref, PeripheralRef}; | 4 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 5 | use embassy_sync::waitqueue::AtomicWaker; | 5 | use embassy_sync::waitqueue::AtomicWaker; |
| 6 | 6 | ||
| 7 | use crate::dma::Transfer; | ||
| 7 | use crate::gpio::sealed::AFType; | 8 | use crate::gpio::sealed::AFType; |
| 8 | use crate::gpio::Speed; | 9 | use crate::gpio::Speed; |
| 9 | use crate::interrupt::{Interrupt, InterruptExt}; | 10 | use 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 | ||
| 3 | use core::future::Future; | ||
| 4 | use core::pin::Pin; | ||
| 3 | use core::sync::atomic::{fence, Ordering}; | 5 | use core::sync::atomic::{fence, Ordering}; |
| 4 | use core::task::Waker; | 6 | use core::task::{Context, Poll}; |
| 5 | 7 | ||
| 6 | use embassy_cortex_m::interrupt::Priority; | 8 | use embassy_cortex_m::interrupt::Priority; |
| 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | ||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 11 | ||
| 9 | use super::{TransferOptions, Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::Dir; | ||
| 10 | use crate::_generated::BDMA_CHANNEL_COUNT; | 14 | use crate::_generated::BDMA_CHANNEL_COUNT; |
| 11 | use crate::dma::Request; | ||
| 12 | use crate::interrupt::{Interrupt, InterruptExt}; | 15 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 13 | use crate::pac; | 16 | use crate::pac; |
| 14 | use crate::pac::bdma::vals; | 17 | use crate::pac::bdma::vals; |
| 15 | 18 | ||
| 19 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 21 | #[non_exhaustive] | ||
| 22 | pub struct TransferOptions {} | ||
| 23 | |||
| 24 | impl Default for TransferOptions { | ||
| 25 | fn default() -> Self { | ||
| 26 | Self {} | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 16 | impl From<WordSize> for vals::Size { | 30 | impl 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 | ||
| 40 | impl 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 | |||
| 26 | struct State { | 49 | struct 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"); | 101 | pub(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);} | 115 | pub type Request = u8; |
| 148 | } | 116 | #[cfg(not(any(bdma_v2, dmamux)))] |
| 117 | pub 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)} | 120 | pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {} |
| 152 | } | 121 | #[cfg(not(dmamux))] |
| 153 | fn remaining_transfers(&mut self) -> u16 { | 122 | pub 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) { | 124 | pub(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 | }; | 136 | pub struct Transfer<'a, C: Channel> { |
| 137 | channel: PeripheralRef<'a, C>, | ||
| 170 | } | 138 | } |
| 171 | 139 | ||
| 172 | mod low_level_api { | 140 | impl<'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 | 322 | impl<'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) { | 332 | impl<'a, C: Channel> Unpin for Transfer<'a, C> {} |
| 275 | panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); | 333 | impl<'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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::pin::Pin; | ||
| 1 | use core::sync::atomic::{fence, Ordering}; | 4 | use core::sync::atomic::{fence, Ordering}; |
| 2 | use core::task::Waker; | 5 | use core::task::{Context, Poll, Waker}; |
| 3 | 6 | ||
| 4 | use embassy_cortex_m::interrupt::Priority; | 7 | use embassy_cortex_m::interrupt::Priority; |
| 8 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | ||
| 5 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use pac::dma::regs; | ||
| 6 | 11 | ||
| 7 | use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::Dir; | ||
| 8 | use crate::_generated::DMA_CHANNEL_COUNT; | 14 | use crate::_generated::DMA_CHANNEL_COUNT; |
| 9 | use crate::interrupt::{Interrupt, InterruptExt}; | 15 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 10 | use crate::pac::dma::{regs, vals}; | 16 | use crate::pac::dma::vals; |
| 11 | use crate::{interrupt, pac}; | 17 | use crate::{interrupt, pac}; |
| 12 | 18 | ||
| 19 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 21 | #[non_exhaustive] | ||
| 22 | pub 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 | |||
| 33 | impl 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 | |||
| 13 | impl From<WordSize> for vals::Size { | 44 | impl 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 | ||
| 54 | impl 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))] | ||
| 65 | pub 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 | |||
| 23 | impl From<Burst> for vals::Burst { | 76 | impl 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))] | ||
| 89 | pub enum FlowControl { | ||
| 90 | /// Flow control by DMA | ||
| 91 | Dma, | ||
| 92 | /// Flow control by peripheral | ||
| 93 | Peripheral, | ||
| 94 | } | ||
| 95 | |||
| 34 | impl From<FlowControl> for vals::Pfctrl { | 96 | impl 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))] | ||
| 107 | pub 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 | |||
| 43 | impl From<FifoThreshold> for vals::Fth { | 118 | impl 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 | ||
| 54 | struct ChannelState { | ||
| 55 | waker: AtomicWaker, | ||
| 56 | } | ||
| 57 | |||
| 58 | impl ChannelState { | ||
| 59 | const fn new() -> Self { | ||
| 60 | Self { | ||
| 61 | waker: AtomicWaker::new(), | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | struct State { | 129 | struct State { |
| 67 | channels: [ChannelState; DMA_CHANNEL_COUNT], | 130 | ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT], |
| 68 | } | 131 | } |
| 69 | 132 | ||
| 70 | impl State { | 133 | impl 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 | ||
| 93 | foreach_dma_channel! { | 156 | foreach_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); | 178 | pub(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)} | 194 | pub type Request = u8; |
| 201 | } | 195 | #[cfg(not(any(dma_v2, dmamux)))] |
| 196 | pub 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)} | 199 | pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {} |
| 205 | } | 200 | #[cfg(not(dmamux))] |
| 201 | pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {} | ||
| 206 | 202 | ||
| 207 | fn set_waker(&mut self, waker: &Waker) { | 203 | pub(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 | ||
| 221 | mod low_level_api { | 214 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 222 | use super::*; | 215 | pub struct Transfer<'a, C: Channel> { |
| 216 | channel: PeripheralRef<'a, C>, | ||
| 217 | } | ||
| 218 | |||
| 219 | impl<'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 | |||
| 422 | impl<'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 | |||
| 432 | impl<'a, C: Channel> Unpin for Transfer<'a, C> {} | ||
| 433 | impl<'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"] | ||
| 449 | pub struct DoubleBuffered<'a, C: Channel, W: Word> { | ||
| 450 | channel: PeripheralRef<'a, C>, | ||
| 451 | _phantom: PhantomData<W>, | ||
| 452 | } | ||
| 453 | |||
| 454 | impl<'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 | 592 | impl<'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 | ||
| 3 | use crate::{pac, peripherals}; | 3 | use crate::{pac, peripherals}; |
| 4 | 4 | ||
| 5 | pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_ch_num: u8, request: u8) { | 5 | pub(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 | ||
| 17 | pub(crate) mod sealed { | 17 | pub(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)] |
| 27 | pub struct DMAMUX2; | 27 | pub struct DMAMUX2; |
| 28 | 28 | ||
| 29 | pub trait MuxChannel: sealed::MuxChannel + super::Channel { | 29 | pub trait MuxChannel: dmamux_sealed::MuxChannel { |
| 30 | type Mux; | 30 | type Mux; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | foreach_dma_channel! { | 33 | foreach_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 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::pin::Pin; | ||
| 1 | use core::sync::atomic::{fence, Ordering}; | 5 | use core::sync::atomic::{fence, Ordering}; |
| 2 | use core::task::Waker; | 6 | use core::task::{Context, Poll}; |
| 3 | 7 | ||
| 8 | use embassy_cortex_m::interrupt::Priority; | ||
| 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 5 | 11 | ||
| 6 | use super::{Request, TransferOptions, Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::Dir; | ||
| 7 | use crate::_generated::GPDMA_CHANNEL_COUNT; | 14 | use crate::_generated::GPDMA_CHANNEL_COUNT; |
| 8 | use crate::interrupt::{Interrupt, InterruptExt}; | 15 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 9 | use crate::pac::gpdma::{vals, Gpdma}; | 16 | use crate::pac; |
| 10 | use crate::{interrupt, pac}; | 17 | use crate::pac::gpdma::vals; |
| 18 | |||
| 19 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 21 | #[non_exhaustive] | ||
| 22 | pub struct TransferOptions {} | ||
| 23 | |||
| 24 | impl Default for TransferOptions { | ||
| 25 | fn default() -> Self { | ||
| 26 | Self {} | ||
| 27 | } | ||
| 28 | } | ||
| 11 | 29 | ||
| 12 | impl From<WordSize> for vals::ChTr1Dw { | 30 | impl 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 | ||
| 22 | struct ChannelState { | ||
| 23 | waker: AtomicWaker, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl ChannelState { | ||
| 27 | const fn new() -> Self { | ||
| 28 | Self { | ||
| 29 | waker: AtomicWaker::new(), | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | struct State { | 40 | struct State { |
| 35 | channels: [ChannelState; GPDMA_CHANNEL_COUNT], | 41 | ch_wakers: [AtomicWaker; GPDMA_CHANNEL_COUNT], |
| 36 | } | 42 | } |
| 37 | 43 | ||
| 38 | impl State { | 44 | impl 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 { | |||
| 47 | static STATE: State = State::new(); | 53 | static STATE: State = State::new(); |
| 48 | 54 | ||
| 49 | /// safety: must be called only once | 55 | /// safety: must be called only once |
| 50 | pub(crate) unsafe fn init() { | 56 | pub(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 | ||
| 59 | foreach_dma_channel! { | 67 | foreach_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"); | 89 | pub(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 { | 115 | pub 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 )} | 118 | pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {} |
| 147 | } | 119 | #[cfg(not(dmamux))] |
| 120 | pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {} | ||
| 148 | 121 | ||
| 149 | fn on_irq() { | 122 | pub(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 | ||
| 159 | mod low_level_api { | 133 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 160 | use super::*; | 134 | pub struct Transfer<'a, C: Channel> { |
| 135 | channel: PeripheralRef<'a, C>, | ||
| 136 | } | ||
| 161 | 137 | ||
| 162 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 138 | impl<'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() { | 334 | impl<'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() { | 344 | impl<'a, C: Channel> Unpin for Transfer<'a, C> {} |
| 298 | // disable all xxIEs to prevent the irq from firing again. | 345 | impl<'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)] | ||
| 2 | pub(crate) mod bdma; | ||
| 3 | #[cfg(dma)] | 1 | #[cfg(dma)] |
| 4 | pub(crate) mod dma; | 2 | pub(crate) mod dma; |
| 3 | #[cfg(dma)] | ||
| 4 | pub 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))] | ||
| 9 | pub mod bdma; | ||
| 10 | |||
| 11 | #[cfg(all(bdma, not(dma)))] | ||
| 12 | pub(crate) mod bdma; | ||
| 13 | #[cfg(all(bdma, not(dma)))] | ||
| 14 | pub use bdma::*; | ||
| 15 | |||
| 16 | #[cfg(gpdma)] | ||
| 17 | pub(crate) mod gpdma; | ||
| 18 | #[cfg(gpdma)] | ||
| 19 | pub use gpdma::*; | ||
| 20 | |||
| 5 | #[cfg(dmamux)] | 21 | #[cfg(dmamux)] |
| 6 | mod dmamux; | 22 | mod dmamux; |
| 7 | #[cfg(gpdma)] | ||
| 8 | mod gpdma; | ||
| 9 | 23 | ||
| 10 | use core::future::Future; | 24 | pub mod word; |
| 25 | |||
| 11 | use core::mem; | 26 | use core::mem; |
| 12 | use core::pin::Pin; | ||
| 13 | use core::task::{Context, Poll, Waker}; | ||
| 14 | 27 | ||
| 15 | #[cfg(any(dma, bdma))] | ||
| 16 | use embassy_cortex_m::interrupt::Priority; | 28 | use embassy_cortex_m::interrupt::Priority; |
| 17 | use embassy_hal_common::{impl_peripheral, into_ref}; | 29 | use embassy_hal_common::impl_peripheral; |
| 18 | 30 | ||
| 19 | #[cfg(dmamux)] | 31 | #[cfg(dmamux)] |
| 20 | pub use self::dmamux::*; | 32 | pub use self::dmamux::*; |
| 21 | use crate::Peripheral; | ||
| 22 | |||
| 23 | #[cfg(feature = "unstable-pac")] | ||
| 24 | pub mod low_level { | ||
| 25 | pub use super::transfers::*; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub(crate) use transfers::*; | ||
| 29 | |||
| 30 | #[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))] | ||
| 31 | pub type Request = u8; | ||
| 32 | #[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))] | ||
| 33 | pub type Request = (); | ||
| 34 | |||
| 35 | pub(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))] | ||
| 126 | pub enum WordSize { | ||
| 127 | OneByte, | ||
| 128 | TwoBytes, | ||
| 129 | FourBytes, | ||
| 130 | } | ||
| 131 | |||
| 132 | impl 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 | |||
| 142 | pub trait Word: sealed::Word { | ||
| 143 | fn bits() -> WordSize; | ||
| 144 | } | ||
| 145 | |||
| 146 | impl sealed::Word for u8 {} | ||
| 147 | impl Word for u8 { | ||
| 148 | fn bits() -> WordSize { | ||
| 149 | WordSize::OneByte | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl sealed::Word for u16 {} | ||
| 154 | impl Word for u16 { | ||
| 155 | fn bits() -> WordSize { | ||
| 156 | WordSize::TwoBytes | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | impl sealed::Word for u32 {} | ||
| 161 | impl 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))] | ||
| 169 | pub 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))] |
| 182 | pub enum FlowControl { | 36 | enum 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))] | ||
| 191 | pub 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))] | ||
| 204 | pub 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 | |||
| 215 | impl 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 | |||
| 226 | mod 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 | |||
| 309 | pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {} | ||
| 310 | |||
| 311 | pub struct NoDma; | 41 | pub struct NoDma; |
| 312 | 42 | ||
| 313 | impl_peripheral!(NoDma); | 43 | impl_peripheral!(NoDma); |
| 314 | 44 | ||
| 315 | // safety: must be called only once at startup | ||
| 316 | pub(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)] |
| 329 | pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) { | 47 | pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) { |
| @@ -334,3 +52,19 @@ pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) { | |||
| 334 | pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) { | 52 | pub(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 | ||
| 57 | pub(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))] | ||
| 3 | pub enum WordSize { | ||
| 4 | OneByte, | ||
| 5 | TwoBytes, | ||
| 6 | FourBytes, | ||
| 7 | } | ||
| 8 | |||
| 9 | impl 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 | |||
| 19 | mod sealed { | ||
| 20 | pub trait Word {} | ||
| 21 | } | ||
| 22 | |||
| 23 | pub trait Word: sealed::Word + Default + Copy + 'static { | ||
| 24 | fn size() -> WordSize; | ||
| 25 | fn bits() -> usize; | ||
| 26 | } | ||
| 27 | |||
| 28 | macro_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 | |||
| 48 | impl_word!(U1, u8, 1, OneByte); | ||
| 49 | impl_word!(U2, u8, 2, OneByte); | ||
| 50 | impl_word!(U3, u8, 3, OneByte); | ||
| 51 | impl_word!(U4, u8, 4, OneByte); | ||
| 52 | impl_word!(U5, u8, 5, OneByte); | ||
| 53 | impl_word!(U6, u8, 6, OneByte); | ||
| 54 | impl_word!(U7, u8, 7, OneByte); | ||
| 55 | impl_word!(_, u8, 8, OneByte); | ||
| 56 | impl_word!(U9, u16, 9, TwoBytes); | ||
| 57 | impl_word!(U10, u16, 10, TwoBytes); | ||
| 58 | impl_word!(U11, u16, 11, TwoBytes); | ||
| 59 | impl_word!(U12, u16, 12, TwoBytes); | ||
| 60 | impl_word!(U13, u16, 13, TwoBytes); | ||
| 61 | impl_word!(U14, u16, 14, TwoBytes); | ||
| 62 | impl_word!(U15, u16, 15, TwoBytes); | ||
| 63 | impl_word!(_, u16, 16, TwoBytes); | ||
| 64 | impl_word!(U17, u32, 17, FourBytes); | ||
| 65 | impl_word!(U18, u32, 18, FourBytes); | ||
| 66 | impl_word!(U19, u32, 19, FourBytes); | ||
| 67 | impl_word!(U20, u32, 20, FourBytes); | ||
| 68 | impl_word!(U21, u32, 21, FourBytes); | ||
| 69 | impl_word!(U22, u32, 22, FourBytes); | ||
| 70 | impl_word!(U23, u32, 23, FourBytes); | ||
| 71 | impl_word!(U24, u32, 24, FourBytes); | ||
| 72 | impl_word!(U25, u32, 25, FourBytes); | ||
| 73 | impl_word!(U26, u32, 26, FourBytes); | ||
| 74 | impl_word!(U27, u32, 27, FourBytes); | ||
| 75 | impl_word!(U28, u32, 28, FourBytes); | ||
| 76 | impl_word!(U29, u32, 29, FourBytes); | ||
| 77 | impl_word!(U30, u32, 30, FourBytes); | ||
| 78 | impl_word!(U31, u32, 31, FourBytes); | ||
| 79 | impl_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}; | |||
| 2 | use embassy_hal_common::drop::OnDrop; | 2 | use embassy_hal_common::drop::OnDrop; |
| 3 | use embassy_hal_common::{into_ref, PeripheralRef}; | 3 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 4 | 4 | ||
| 5 | use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; | 5 | use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; |
| 6 | use crate::flash::FlashBank; | 6 | use crate::flash::FlashBank; |
| 7 | use crate::Peripheral; | 7 | use crate::Peripheral; |
| 8 | 8 | ||
| @@ -162,6 +162,35 @@ impl FlashRegion { | |||
| 162 | } | 162 | } |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | impl embedded_storage::nor_flash::ErrorType for Flash<'_> { | ||
| 166 | type Error = Error; | ||
| 167 | } | ||
| 168 | |||
| 169 | impl 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 | |||
| 181 | impl 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 | |||
| 165 | foreach_flash_region! { | 194 | foreach_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; | |||
| 7 | pub use common::*; | 7 | pub use common::*; |
| 8 | 8 | ||
| 9 | pub use crate::_generated::flash_regions::*; | 9 | pub use crate::_generated::flash_regions::*; |
| 10 | pub use crate::_generated::MAX_ERASE_SIZE; | ||
| 10 | pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; | 11 | pub 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 @@ | |||
| 1 | use core::cmp; | 1 | use core::cmp; |
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::sync::atomic::{AtomicUsize, Ordering}; | ||
| 4 | use core::task::Poll; | 3 | use core::task::Poll; |
| 5 | 4 | ||
| 6 | use embassy_embedded_hal::SetConfig; | 5 | use embassy_embedded_hal::SetConfig; |
| @@ -8,7 +7,7 @@ use embassy_hal_common::drop::OnDrop; | |||
| 8 | use embassy_hal_common::{into_ref, PeripheralRef}; | 7 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 8 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 9 | ||
| 11 | use crate::dma::NoDma; | 10 | use crate::dma::{NoDma, Transfer}; |
| 12 | use crate::gpio::sealed::AFType; | 11 | use crate::gpio::sealed::AFType; |
| 13 | use crate::gpio::Pull; | 12 | use crate::gpio::Pull; |
| 14 | use crate::i2c::{Error, Instance, SclPin, SdaPin}; | 13 | use crate::i2c::{Error, Instance, SclPin, SdaPin}; |
| @@ -35,14 +34,12 @@ impl Default for Config { | |||
| 35 | 34 | ||
| 36 | pub struct State { | 35 | pub struct State { |
| 37 | waker: AtomicWaker, | 36 | waker: AtomicWaker, |
| 38 | chunks_transferred: AtomicUsize, | ||
| 39 | } | 37 | } |
| 40 | 38 | ||
| 41 | impl State { | 39 | impl 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; | |||
| 49 | pub mod qspi; | 49 | pub mod qspi; |
| 50 | #[cfg(rng)] | 50 | #[cfg(rng)] |
| 51 | pub mod rng; | 51 | pub mod rng; |
| 52 | #[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))] | ||
| 53 | pub mod rtc; | ||
| 52 | #[cfg(sdmmc)] | 54 | #[cfg(sdmmc)] |
| 53 | pub mod sdmmc; | 55 | pub mod sdmmc; |
| 54 | #[cfg(spi)] | 56 | #[cfg(spi)] |
| @@ -76,7 +78,6 @@ pub(crate) mod _generated { | |||
| 76 | // Reexports | 78 | // Reexports |
| 77 | pub use _generated::{peripherals, Peripherals}; | 79 | pub use _generated::{peripherals, Peripherals}; |
| 78 | pub use embassy_cortex_m::executor; | 80 | pub use embassy_cortex_m::executor; |
| 79 | #[cfg(any(dma, bdma))] | ||
| 80 | use embassy_cortex_m::interrupt::Priority; | 81 | use embassy_cortex_m::interrupt::Priority; |
| 81 | pub use embassy_cortex_m::interrupt::_export::interrupt; | 82 | pub use embassy_cortex_m::interrupt::_export::interrupt; |
| 82 | pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 83 | pub 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 | ||
| 99 | impl Default for Config { | 102 | impl 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; | |||
| 5 | use embassy_hal_common::{into_ref, PeripheralRef}; | 5 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 6 | use enums::*; | 6 | use enums::*; |
| 7 | 7 | ||
| 8 | use crate::dma::TransferOptions; | 8 | use crate::dma::Transfer; |
| 9 | use crate::gpio::sealed::AFType; | 9 | use crate::gpio::sealed::AFType; |
| 10 | use crate::gpio::AnyPin; | 10 | use crate::gpio::AnyPin; |
| 11 | use crate::pac::quadspi::Quadspi as Regs; | 11 | use 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 | ||
| 35 | unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults { | 38 | #[cfg(stm32f410)] |
| 39 | unsafe 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))] | ||
| 45 | unsafe 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)))] | ||
| 50 | unsafe 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 | |||
| 85 | unsafe 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 | ||
| 421 | mod max { | 498 | mod 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")] | ||
| 2 | use core::convert::From; | ||
| 3 | |||
| 4 | #[cfg(feature = "chrono")] | ||
| 5 | use chrono::{self, Datelike, NaiveDate, Timelike, Weekday}; | ||
| 6 | |||
| 7 | use super::byte_to_bcd2; | ||
| 8 | use crate::pac::rtc::Rtc; | ||
| 9 | |||
| 10 | /// Errors regarding the [`DateTime`] struct. | ||
| 11 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
| 12 | pub 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 | ||
| 33 | pub 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")] | ||
| 51 | impl 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")] | ||
| 66 | impl 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)] | ||
| 83 | pub 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")] | ||
| 94 | impl 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")] | ||
| 101 | impl 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 | |||
| 115 | fn 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 | |||
| 128 | pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { | ||
| 129 | dotw as u8 | ||
| 130 | } | ||
| 131 | |||
| 132 | pub(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 | |||
| 150 | pub(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 | |||
| 184 | pub(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 @@ | |||
| 1 | use chrono::{Datelike, Timelike}; | ||
| 2 | |||
| 3 | use super::byte_to_bcd2; | ||
| 4 | use crate::pac::rtc::Rtc; | ||
| 5 | |||
| 6 | /// Alias for [`chrono::NaiveDateTime`] | ||
| 7 | pub type DateTime = chrono::NaiveDateTime; | ||
| 8 | /// Alias for [`chrono::Weekday`] | ||
| 9 | pub 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)] | ||
| 15 | pub 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 | |||
| 24 | pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { | ||
| 25 | dotw.num_days_from_monday() as u8 | ||
| 26 | } | ||
| 27 | |||
| 28 | pub(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 | |||
| 38 | pub(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 | |||
| 72 | pub(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 | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | mod datetime; | ||
| 4 | |||
| 5 | pub 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")] | ||
| 16 | mod versions; | ||
| 17 | use embassy_hal_common::Peripheral; | ||
| 18 | pub use versions::*; | ||
| 19 | |||
| 20 | /// Errors that can occur on methods on [RtcClock] | ||
| 21 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
| 22 | pub 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 | ||
| 31 | pub 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)] | ||
| 38 | pub 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)] | ||
| 50 | pub 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 | |||
| 65 | impl 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 | |||
| 77 | impl 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)] | ||
| 99 | pub 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 | |||
| 108 | impl Default for RtcCalibrationCyclePeriod { | ||
| 109 | fn default() -> Self { | ||
| 110 | RtcCalibrationCyclePeriod::Seconds32 | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | impl<'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 | |||
| 201 | pub(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 | |||
| 213 | pub(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 | |||
| 221 | pub(crate) mod sealed { | ||
| 222 | pub trait Instance { | ||
| 223 | fn regs() -> crate::pac::rtc::Rtc; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | pub trait Instance: sealed::Instance + 'static {} | ||
| 228 | |||
| 229 | impl sealed::Instance for crate::peripherals::RTC { | ||
| 230 | fn regs() -> crate::pac::rtc::Rtc { | ||
| 231 | crate::pac::RTC | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | impl 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 @@ | |||
| 1 | use stm32_metapac::rtc::vals::{Init, Osel, Pol}; | ||
| 2 | |||
| 3 | use super::{Instance, RtcConfig}; | ||
| 4 | use 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")] | ||
| 16 | mod family; | ||
| 17 | |||
| 18 | pub use family::*; | ||
| 19 | |||
| 20 | impl<'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. | ||
| 155 | pub 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. | ||
| 167 | pub 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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 35 | pub(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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 29 | pub(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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 29 | pub(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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 29 | pub(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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 35 | pub(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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 31 | pub(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 @@ | |||
| 1 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 2 | |||
| 3 | /// Unlock the backup domain | ||
| 4 | pub(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 | |||
| 24 | pub(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 @@ | |||
| 1 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 2 | |||
| 3 | /// Unlock the backup domain | ||
| 4 | pub(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 | |||
| 22 | pub(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 @@ | |||
| 1 | use stm32_metapac::rcc::vals::Rtcsel; | ||
| 2 | |||
| 3 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 4 | |||
| 5 | /// Unlock the backup domain | ||
| 6 | pub(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 | |||
| 35 | pub(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 @@ | |||
| 1 | pub const BACKUP_REGISTER_COUNT: usize = 20; | ||
| 2 | |||
| 3 | /// Unlock the backup domain | ||
| 4 | pub(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 | |||
| 33 | pub(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 @@ | |||
| 1 | use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; | ||
| 2 | |||
| 3 | use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig}; | ||
| 4 | use crate::pac::rtc::Rtc; | ||
| 5 | |||
| 6 | impl<'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 | |||
| 198 | pub(super) unsafe fn enable_peripheral_clk() { | ||
| 199 | // Nothing to do | ||
| 200 | } | ||
| 201 | |||
| 202 | pub 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. | ||
| 208 | pub 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. | ||
| 221 | pub 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))] |
| 46 | pub struct DataBlock(pub [u8; 512]); | 46 | pub 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))] |
| 66 | pub enum Error { | 66 | pub enum Error { |
| 67 | Timeout, | 67 | Timeout, |
| @@ -135,60 +135,71 @@ enum Response { | |||
| 135 | Long = 3, | 135 | Long = 3, |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | cfg_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. | 144 | fn 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 | 173 | fn 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)] | ||
| 189 | type Transfer<'a, C> = crate::dma::Transfer<'a, C>; | ||
| 190 | #[cfg(sdmmc_v2)] | ||
| 191 | type Transfer<'a, C> = core::marker::PhantomData<&'a mut C>; | ||
| 192 | |||
| 193 | #[cfg(all(sdmmc_v1, dma))] | ||
| 194 | const 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)))] | ||
| 201 | const 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 |
| 211 | pub struct Sdmmc<'d, T: Instance, Dma = NoDma> { | 222 | pub 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 | ||
| 406 | impl<'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 | |||
| 477 | impl<'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 | |||
| 568 | impl<'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 | |||
| 591 | pub struct SdmmcInner(pub(crate) RegBlock); | ||
| 592 | |||
| 593 | impl 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 | |||
| 1284 | impl<'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); | |||
| 1560 | pin_trait!(D6Pin, Instance); | 1418 | pin_trait!(D6Pin, Instance); |
| 1561 | pin_trait!(D7Pin, Instance); | 1419 | pin_trait!(D7Pin, Instance); |
| 1562 | 1420 | ||
| 1563 | cfg_if::cfg_if! { | 1421 | #[cfg(sdmmc_v1)] |
| 1564 | if #[cfg(sdmmc_v1)] { | 1422 | dma_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> {} | 1426 | pub trait SdmmcDma<T: Instance> {} |
| 1569 | impl<T: Instance> SdmmcDma<T> for NoDma {} | 1427 | #[cfg(sdmmc_v2)] |
| 1570 | } | 1428 | impl<T: Instance> SdmmcDma<T> for NoDma {} |
| 1571 | } | ||
| 1572 | 1429 | ||
| 1573 | cfg_if::cfg_if! { | 1430 | cfg_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")] |
| 1653 | mod sdmmc_rs { | 1509 | mod 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; | |||
| 7 | use embassy_hal_common::{into_ref, PeripheralRef}; | 7 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 8 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 8 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; |
| 9 | 9 | ||
| 10 | use self::sealed::WordSize; | 10 | use crate::dma::{slice_ptr_parts, word, Transfer}; |
| 11 | use crate::dma::{slice_ptr_parts, Transfer}; | ||
| 12 | use crate::gpio::sealed::{AFType, Pin as _}; | 11 | use crate::gpio::sealed::{AFType, Pin as _}; |
| 13 | use crate::gpio::{AnyPin, Pull}; | 12 | use crate::gpio::{AnyPin, Pull}; |
| 14 | use crate::pac::spi::{regs, vals, Spi as Regs}; | 13 | use 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 | ||
| 84 | impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | 83 | impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { |
| @@ -178,6 +177,23 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { | |||
| 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)] | 959 | pub trait Word: word::Word + sealed::Word {} |
| 943 | pub enum WordSize { | ||
| 944 | EightBit, | ||
| 945 | SixteenBit, | ||
| 946 | } | ||
| 947 | 960 | ||
| 948 | impl WordSize { | 961 | macro_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 { | 971 | mod 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 { | 981 | mod 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 | ||
| 991 | pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} | 1001 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 1002 | mod word_impl { | ||
| 1003 | use super::*; | ||
| 992 | 1004 | ||
| 993 | impl Word for u8 {} | 1005 | pub type Config = u8; |
| 994 | impl 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 | ||
| 996 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | 1038 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} |
| 997 | pin_trait!(SckPin, Instance); | 1039 | pin_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}; | |||
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_cortex_m::interrupt::InterruptExt; | 8 | use embassy_cortex_m::interrupt::InterruptExt; |
| 9 | use embassy_futures::select::{select, Either}; | ||
| 10 | use embassy_hal_common::drop::OnDrop; | 9 | use embassy_hal_common::drop::OnDrop; |
| 11 | use embassy_hal_common::{into_ref, PeripheralRef}; | 10 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 11 | use futures::future::{select, Either}; | ||
| 12 | 12 | ||
| 13 | use crate::dma::NoDma; | 13 | use crate::dma::{NoDma, Transfer}; |
| 14 | use crate::gpio::sealed::AFType; | 14 | use crate::gpio::sealed::AFType; |
| 15 | #[cfg(any(lpuart_v1, lpuart_v2))] | 15 | #[cfg(any(lpuart_v1, lpuart_v2))] |
| 16 | use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; | 16 | use 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 | ||
| 97 | pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { | 97 | pub 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 | ))] | ||
| 981 | mod 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")] |
| 1044 | pub use buffered::*; | 976 | pub 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 } | |||
| 18 | panic-reset = { version = "0.1.1" } | 18 | panic-reset = { version = "0.1.1" } |
| 19 | embedded-hal = { version = "0.2.6" } | 19 | embedded-hal = { version = "0.2.6" } |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | 23 | ||
| 24 | [features] | 24 | [features] |
| 25 | ed25519-dalek = ["embassy-boot/ed25519-dalek"] | 25 | ed25519-dalek = ["embassy-boot/ed25519-dalek"] |
| 26 | ed25519-salty = ["embassy-boot/ed25519-salty"] \ No newline at end of file | 26 | ed25519-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 } | |||
| 18 | panic-reset = { version = "0.1.1", optional = true } | 18 | panic-reset = { version = "0.1.1", optional = true } |
| 19 | embedded-hal = { version = "0.2.6" } | 19 | embedded-hal = { version = "0.2.6" } |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 } | |||
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-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 | ||
| 12 | embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } | 12 | embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } |
| 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } | 13 | embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } |
| 14 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | cortex-m-rt = { version = "0.7" } | 15 | cortex-m-rt = { version = "0.7" } |
| 16 | cfg-if = "1.0.0" | 16 | cfg-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 | |||
| 13 | embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } | 13 | embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } |
| 14 | embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } | 14 | embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = { version = "0.7" } | 17 | cortex-m-rt = { version = "0.7" } |
| 18 | embedded-storage = "0.3.0" | 18 | embedded-storage = "0.3.0" |
| 19 | embedded-storage-async = "0.4.0" | 19 | embedded-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 | ||
| 12 | embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } | 12 | embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } |
| 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } | 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } |
| 14 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | cortex-m-rt = { version = "0.7" } | 15 | cortex-m-rt = { version = "0.7" } |
| 16 | embedded-storage = "0.3.0" | 16 | embedded-storage = "0.3.0" |
| 17 | embedded-storage-async = "0.4.0" | 17 | embedded-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 | |||
| 21 | embassy-time = { version = "0.1.0", path = "../../embassy-time" } | 21 | embassy-time = { version = "0.1.0", path = "../../embassy-time" } |
| 22 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 22 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 23 | 23 | ||
| 24 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 24 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 25 | cortex-m-rt = "0.7.0" | 25 | cortex-m-rt = "0.7.0" |
| 26 | panic-probe = { version = "0.3" } | 26 | panic-probe = { version = "0.3" } |
| 27 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 27 | futures = { 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 | |||
| 20 | embedded-io = "0.4.0" | 20 | embedded-io = "0.4.0" |
| 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } | 21 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } |
| 22 | 22 | ||
| 23 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } | 23 | lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } |
| 24 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | 24 | lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } |
| 25 | 25 | ||
| 26 | defmt = "0.3" | 26 | defmt = "0.3" |
| 27 | defmt-rtt = "0.4" | 27 | defmt-rtt = "0.4" |
| 28 | 28 | ||
| 29 | static_cell = "1.0" | 29 | static_cell = "1.0" |
| 30 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 30 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 31 | cortex-m-rt = "0.7.0" | 31 | cortex-m-rt = "0.7.0" |
| 32 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 32 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 33 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 33 | futures = { 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" | |||
| 43 | defmt-rtt = "0.4" | 43 | defmt-rtt = "0.4" |
| 44 | 44 | ||
| 45 | static_cell = "1.0" | 45 | static_cell = "1.0" |
| 46 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 46 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 47 | cortex-m-rt = "0.7.0" | 47 | cortex-m-rt = "0.7.0" |
| 48 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 48 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 49 | futures = { version = "0.3.17", default-features = false, features = [ | 49 | futures = { 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" | |||
| 20 | defmt-rtt = "0.4" | 20 | defmt-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"] } |
| 23 | cortex-m = { version = "0.7.6" } | 23 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } |
| 24 | cortex-m-rt = "0.7.0" | 24 | cortex-m-rt = "0.7.0" |
| 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } | 26 | futures = { 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] |
| 10 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 10 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 11 | cortex-m-rt = "0.7.0" | 11 | cortex-m-rt = "0.7.0" |
| 12 | defmt = "0.3" | 12 | defmt = "0.3" |
| 13 | defmt-rtt = "0.4" | 13 | defmt-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" } | |||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "0.4" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-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 = [" | |||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.4" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 18 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-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" } | |||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "0.4" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-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" | |||
| 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 13 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } | 13 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "0.4" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | embedded-io = "0.4.0" | 21 | embedded-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 | |||
| 16 | defmt = "0.3" | 16 | defmt = "0.3" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "0.4" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-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 = [" | |||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.4" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 18 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::word::U5; | ||
| 8 | use embassy_stm32::dma::NoDma; | ||
| 9 | use embassy_stm32::spi::{Config, Spi}; | ||
| 10 | use embassy_stm32::time::Hertz; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | const NR_PIXELS: usize = 15; | ||
| 15 | const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw | ||
| 16 | const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL; | ||
| 17 | |||
| 18 | struct RGB { | ||
| 19 | r: u8, | ||
| 20 | g: u8, | ||
| 21 | b: u8, | ||
| 22 | } | ||
| 23 | impl Default for RGB { | ||
| 24 | fn default() -> RGB { | ||
| 25 | RGB { r: 0, g: 0, b: 0 } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | pub struct Ws2812 { | ||
| 29 | // Note that the U5 type controls the selection of 5 bits to output | ||
| 30 | bitbuffer: [U5; TOTAL_BITS], | ||
| 31 | } | ||
| 32 | |||
| 33 | impl 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] | ||
| 75 | async 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 | |||
| 16 | defmt = "0.3" | 16 | defmt = "0.3" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "0.4" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } | 22 | embedded-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 | |||
| 16 | defmt = "0.3" | 16 | defmt = "0.3" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "0.4" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } | 22 | embedded-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 | |||
| 15 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 15 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 16 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} | 16 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} |
| 17 | 17 | ||
| 18 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } | 18 | lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } |
| 19 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | 19 | lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } |
| 20 | 20 | ||
| 21 | defmt = "0.3" | 21 | defmt = "0.3" |
| 22 | defmt-rtt = "0.4" | 22 | defmt-rtt = "0.4" |
| @@ -24,7 +24,7 @@ defmt-rtt = "0.4" | |||
| 24 | embedded-storage = "0.3.0" | 24 | embedded-storage = "0.3.0" |
| 25 | embedded-io = "0.4.0" | 25 | embedded-io = "0.4.0" |
| 26 | 26 | ||
| 27 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 27 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 28 | cortex-m-rt = "0.7.0" | 28 | cortex-m-rt = "0.7.0" |
| 29 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 29 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 30 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 30 | futures = { 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 = [" | |||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.4" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 18 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-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" | |||
| 18 | defmt-rtt = "0.4" | 18 | defmt-rtt = "0.4" |
| 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 24 | futures = { 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 | |||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "0.4" |
| 16 | 16 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 18 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-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 = [" | |||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.4" | 14 | defmt-rtt = "0.4" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 18 | embedded-hal = "0.2.6" | 18 | embedded-hal = "0.2.6" |
| 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-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 | |||
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } |
| 12 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } | 12 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } |
| 13 | 13 | ||
| 14 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } | 14 | lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } |
| 15 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } | 15 | lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] } |
| 16 | 16 | ||
| 17 | defmt = "0.3" | 17 | defmt = "0.3" |
| 18 | defmt-rtt = "0.4" | 18 | defmt-rtt = "0.4" |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 22 | embedded-hal = "0.2.6" | 22 | embedded-hal = "0.2.6" |
| 23 | embedded-storage = "0.3.0" | 23 | embedded-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] |
| 4 | channel = "nightly-2023-04-11" | 4 | channel = "nightly-2023-04-18" |
| 5 | components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] | 5 | components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] |
| 6 | targets = [ | 6 | targets = [ |
| 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] |
| 2 | build-std = ["core"] | 2 | # enabling these breaks the float tests during linking, with intrinsics |
| 3 | build-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" | |||
| 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } |
| 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } | 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] } |
| 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 13 | 13 | ||
| 14 | defmt = "0.3.0" | 14 | defmt = "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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::pac; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async 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] |
| 8 | stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill | 8 | stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill |
| 9 | stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo | 9 | stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo |
| 10 | stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo | 10 | stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo |
| 11 | stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo | 11 | stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo |
| 12 | stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo | 12 | stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo |
| @@ -15,6 +15,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo | |||
| 15 | stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo | 15 | stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo |
| 16 | stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board | 16 | stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board |
| 17 | 17 | ||
| 18 | sdmmc = [] | ||
| 19 | |||
| 18 | [dependencies] | 20 | [dependencies] |
| 19 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | 21 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 20 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 22 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| @@ -31,6 +33,45 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } | |||
| 31 | embedded-hal-async = { version = "=0.2.0-alpha.1" } | 33 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 32 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } | 34 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } |
| 33 | 35 | ||
| 36 | # BEGIN TESTS | ||
| 37 | # Generated by gen_test.py. DO NOT EDIT. | ||
| 38 | [[bin]] | ||
| 39 | name = "gpio" | ||
| 40 | path = "src/bin/gpio.rs" | ||
| 41 | required-features = [] | ||
| 42 | |||
| 43 | [[bin]] | ||
| 44 | name = "sdmmc" | ||
| 45 | path = "src/bin/sdmmc.rs" | ||
| 46 | required-features = [ "sdmmc",] | ||
| 47 | |||
| 48 | [[bin]] | ||
| 49 | name = "spi" | ||
| 50 | path = "src/bin/spi.rs" | ||
| 51 | required-features = [] | ||
| 52 | |||
| 53 | [[bin]] | ||
| 54 | name = "spi_dma" | ||
| 55 | path = "src/bin/spi_dma.rs" | ||
| 56 | required-features = [] | ||
| 57 | |||
| 58 | [[bin]] | ||
| 59 | name = "timer" | ||
| 60 | path = "src/bin/timer.rs" | ||
| 61 | required-features = [] | ||
| 62 | |||
| 63 | [[bin]] | ||
| 64 | name = "usart" | ||
| 65 | path = "src/bin/usart.rs" | ||
| 66 | required-features = [] | ||
| 67 | |||
| 68 | [[bin]] | ||
| 69 | name = "usart_dma" | ||
| 70 | path = "src/bin/usart_dma.rs" | ||
| 71 | required-features = [] | ||
| 72 | |||
| 73 | # END TESTS | ||
| 74 | |||
| 34 | [profile.dev] | 75 | [profile.dev] |
| 35 | debug = 2 | 76 | debug = 2 |
| 36 | debug-assertions = true | 77 | debug-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 @@ | |||
| 1 | import os | ||
| 2 | import toml | ||
| 3 | from glob import glob | ||
| 4 | |||
| 5 | abspath = os.path.abspath(__file__) | ||
| 6 | dname = os.path.dirname(abspath) | ||
| 7 | os.chdir(dname) | ||
| 8 | |||
| 9 | # ======= load test list | ||
| 10 | tests = {} | ||
| 11 | for 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 | |||
| 23 | things = { | ||
| 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 | |||
| 34 | SEPARATOR_START = '# BEGIN TESTS\n' | ||
| 35 | SEPARATOR_END = '# END TESTS\n' | ||
| 36 | HELP = '# Generated by gen_test.py. DO NOT EDIT.\n' | ||
| 37 | with open('Cargo.toml', 'r') as f: | ||
| 38 | data = f.read() | ||
| 39 | before, data = data.split(SEPARATOR_START, maxsplit=1) | ||
| 40 | _, after = data.split(SEPARATOR_END, maxsplit=1) | ||
| 41 | data = before + SEPARATOR_START + HELP + \ | ||
| 42 | toml.dumps(things) + SEPARATOR_END + after | ||
| 43 | with 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 | |||
| 6 | use defmt::{assert_eq, *}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | ||
| 9 | use embassy_stm32::time::mhz; | ||
| 10 | use embassy_stm32::{interrupt, Config}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async 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 | } | ||
