Compare commits
124 Commits
embassy-sy
...
embassy-ex
Author | SHA1 | Date | |
---|---|---|---|
3e730aa8b0 | |||
28a3454846 | |||
03d6363d5a | |||
31b54e0fbd | |||
278818395e | |||
42a8f1671d | |||
1cf26f0eb3 | |||
d91c37dae3 | |||
759d911b50 | |||
a277deeaa5 | |||
cb00fb18cb | |||
f8b359dc5a | |||
edef790e1a | |||
18af9f304a | |||
deadf40c85 | |||
405649ddc7 | |||
e2410cbb6a | |||
52decfb16c | |||
91047c61b9 | |||
054ca17f66 | |||
f729d2d060 | |||
73f25093c7 | |||
a3f727e2e1 | |||
0dea7b02d6 | |||
a4866ad278 | |||
d78edba0d4 | |||
b283f213d9 | |||
ba47fe9c41 | |||
8285263fc2 | |||
cc5bca8e83 | |||
0a2f7b4661 | |||
02c86bca52 | |||
fb27594b2e | |||
a73f9474a0 | |||
3bf41e9a06 | |||
0e01b28d5e | |||
8aca324c2d | |||
7ee9e8322c | |||
43c20dbe65 | |||
8cd117fd5d | |||
f64d1131b6 | |||
b153a5b0d7 | |||
9b51c8f4d4 | |||
510ae7e3dc | |||
f67eb84ec7 | |||
5de6bb3adf | |||
54fe50c685 | |||
837cdacd16 | |||
41e90e22e2 | |||
64b80c2e4d | |||
26f4d7d283 | |||
fdd6e08ed6 | |||
7d64de153f | |||
37181c79d9 | |||
a2ac1eed1b | |||
bfa3cbaf30 | |||
216b120f15 | |||
08d9e5981e | |||
8a9136e4e4 | |||
3260f6b2af | |||
2080d8bb6d | |||
a673b9aa29 | |||
38c5b97df0 | |||
21ea98810a | |||
fbd6eeb748 | |||
dbded7a6ce | |||
efc70debb3 | |||
173c65b543 | |||
a86a100879 | |||
bba8b0ded5 | |||
095f5ef279 | |||
1c68c62ebd | |||
f5216624bb | |||
46227bec1e | |||
e63a34ba21 | |||
82dd7a5f8c | |||
0dfa192992 | |||
e14fa11fc3 | |||
df7ef1d98f | |||
9202dbf32a | |||
4044d728a6 | |||
6acc361109 | |||
27ec29e2c5 | |||
90c1422381 | |||
9e1ddeac86 | |||
99dcbf00c4 | |||
776e001b5b | |||
6ba2bb1a7f | |||
a258e15c23 | |||
7a682ec02a | |||
e9ede443bc | |||
bc550cbfda | |||
8da9c07a65 | |||
bd6bb2d248 | |||
81f10e136a | |||
1fdce6e52a | |||
f395ec44e8 | |||
63941432e3 | |||
be0f93ff37 | |||
224eaaf797 | |||
f681b9d4e5 | |||
650589ab3f | |||
b9fc2a6b33 | |||
3002ee0dcf | |||
ce0e1a5db3 | |||
a3ecf5caf6 | |||
a7629299f4 | |||
e7ff759f1c | |||
6a6c673c5f | |||
c1d5f86871 | |||
0289630fe4 | |||
1b86570cfd | |||
f51cbebffd | |||
c309797488 | |||
6b2aaacf83 | |||
ba9afbc26d | |||
5e74926907 | |||
218b44652c | |||
86113e199f | |||
9223b67306 | |||
aff265a7f5 | |||
79cee74151 | |||
62c0b18f10 | |||
a83560c6b1 |
3
.github/workflows/doc.yml
vendored
3
.github/workflows/doc.yml
vendored
@ -61,6 +61,7 @@ jobs:
|
||||
mkdir crates
|
||||
builder ./embassy-boot/boot crates/embassy-boot/git.zup
|
||||
builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup
|
||||
builder ./embassy-boot/rp crates/embassy-boot-rp/git.zup
|
||||
builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup
|
||||
builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup
|
||||
builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup
|
||||
@ -84,5 +85,3 @@ jobs:
|
||||
echo "${{secrets.KUBECONFIG}}" > ~/.kube/config
|
||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||
kubectl cp crates $POD:/data
|
||||
|
||||
|
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@ -71,7 +71,7 @@ jobs:
|
||||
|
||||
- name: Test boot
|
||||
working-directory: ./embassy-boot/boot
|
||||
run: cargo test && cargo test --features "ed25519-dalek" && cargo test --features "ed25519-salty"
|
||||
run: cargo test && cargo test --features nightly && cargo test --features "ed25519-dalek,nightly" && cargo test --features "ed25519-salty,nightly"
|
||||
|
||||
- name: Test sync
|
||||
working-directory: ./embassy-sync
|
||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -16,10 +16,11 @@
|
||||
// "embassy-executor/Cargo.toml",
|
||||
// "embassy-sync/Cargo.toml",
|
||||
"examples/nrf52840/Cargo.toml",
|
||||
//"examples/nrf5340/Cargo.toml",
|
||||
// "examples/nrf5340/Cargo.toml",
|
||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||
// "examples/rp/Cargo.toml",
|
||||
// "examples/std/Cargo.toml",
|
||||
// "examples/stm32c0/Cargo.toml",
|
||||
// "examples/stm32f0/Cargo.toml",
|
||||
// "examples/stm32f1/Cargo.toml",
|
||||
// "examples/stm32f2/Cargo.toml",
|
||||
@ -28,6 +29,7 @@
|
||||
// "examples/stm32f7/Cargo.toml",
|
||||
// "examples/stm32g0/Cargo.toml",
|
||||
// "examples/stm32g4/Cargo.toml",
|
||||
// "examples/stm32h5/Cargo.toml",
|
||||
// "examples/stm32h7/Cargo.toml",
|
||||
// "examples/stm32l0/Cargo.toml",
|
||||
// "examples/stm32l1/Cargo.toml",
|
||||
@ -35,9 +37,7 @@
|
||||
// "examples/stm32l5/Cargo.toml",
|
||||
// "examples/stm32u5/Cargo.toml",
|
||||
// "examples/stm32wb/Cargo.toml",
|
||||
// "examples/stm32wb55/Cargo.toml",
|
||||
// "examples/stm32wl/Cargo.toml",
|
||||
// "examples/stm32wl55/Cargo.toml",
|
||||
// "examples/wasm/Cargo.toml",
|
||||
],
|
||||
}
|
@ -106,10 +106,10 @@ git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
- Install `probe-run` with defmt support.
|
||||
- Install `probe-rs-cli` with defmt support.
|
||||
|
||||
```bash
|
||||
cargo install probe-run
|
||||
cargo install probe-rs-cli
|
||||
```
|
||||
|
||||
- Change directory to the sample's base directory. For example:
|
||||
|
9
ci.sh
9
ci.sh
@ -22,6 +22,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
|
||||
@ -68,10 +69,10 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,nightly \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,nightly \
|
||||
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4,nightly \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
|
||||
|
@ -9,10 +9,15 @@ export DEFMT_LOG=trace
|
||||
sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
|
||||
|
||||
cargo batch \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
||||
|
@ -6,7 +6,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.2.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
cortex-m = "0.7"
|
||||
cortex-m-rt = "0.7"
|
||||
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false }
|
||||
embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.2.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] }
|
||||
|
||||
defmt = "0.3.0"
|
||||
defmt-rtt = "0.3.0"
|
||||
|
@ -29,7 +29,7 @@ log = { version = "0.4", optional = true }
|
||||
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
||||
embassy-sync = { version = "0.2.0", path = "../../embassy-sync" }
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = "0.4.0"
|
||||
embedded-storage-async = { version = "0.4.0", optional = true}
|
||||
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
||||
signature = { version = "1.6.4", default-features = false }
|
||||
|
||||
@ -48,5 +48,7 @@ features = ["rand", "std", "u32_backend"]
|
||||
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
|
||||
ed25519-salty = ["dep:salty", "_verify"]
|
||||
|
||||
nightly = ["dep:embedded-storage-async"]
|
||||
|
||||
#Internal features
|
||||
_verify = []
|
||||
|
@ -13,11 +13,12 @@ By design, the bootloader does not provide any network capabilities. Networking
|
||||
The bootloader supports different hardware in separate crates:
|
||||
|
||||
* `embassy-boot-nrf` - for the nRF microcontrollers.
|
||||
* `embassy-boot-rp` - for the RP2040 microcontrollers.
|
||||
* `embassy-boot-stm32` - for the STM32 microcontrollers.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||
`embassy-boot` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use digest::Digest;
|
||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
|
||||
#[cfg(feature = "nightly")]
|
||||
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
|
||||
|
||||
use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
|
||||
@ -78,6 +79,7 @@ impl FirmwareUpdater {
|
||||
/// This is useful to check if the bootloader has just done a swap, in order
|
||||
/// to do verifications and self-tests of the new image before calling
|
||||
/// `mark_booted`.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn get_state<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
state_flash: &mut F,
|
||||
@ -108,7 +110,7 @@ impl FirmwareUpdater {
|
||||
///
|
||||
/// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||
/// and written to.
|
||||
#[cfg(feature = "_verify")]
|
||||
#[cfg(all(feature = "_verify", feature = "nightly"))]
|
||||
pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
_state_and_dfu_flash: &mut F,
|
||||
@ -172,6 +174,7 @@ impl FirmwareUpdater {
|
||||
}
|
||||
|
||||
/// Verify the update in DFU with any digest.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn hash<F: AsyncNorFlash, D: Digest>(
|
||||
&mut self,
|
||||
dfu_flash: &mut F,
|
||||
@ -194,7 +197,7 @@ impl FirmwareUpdater {
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
||||
#[cfg(not(feature = "_verify"))]
|
||||
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||
pub async fn mark_updated<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
state_flash: &mut F,
|
||||
@ -209,6 +212,7 @@ impl FirmwareUpdater {
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn mark_booted<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
state_flash: &mut F,
|
||||
@ -218,6 +222,7 @@ impl FirmwareUpdater {
|
||||
self.set_magic(aligned, BOOT_MAGIC, state_flash).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
async fn set_magic<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
aligned: &mut [u8],
|
||||
@ -258,6 +263,7 @@ impl FirmwareUpdater {
|
||||
/// # Safety
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn write_firmware<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
@ -280,6 +286,7 @@ impl FirmwareUpdater {
|
||||
///
|
||||
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||
/// exchange for added complexity.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn prepare_update<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
dfu_flash: &mut F,
|
||||
@ -513,6 +520,7 @@ mod tests {
|
||||
use crate::mem_flash::MemFlash;
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "nightly")]
|
||||
fn can_verify_sha1() {
|
||||
const STATE: Partition = Partition::new(0, 4096);
|
||||
const DFU: Partition = Partition::new(65536, 131072);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||
#![allow(incomplete_features)]
|
||||
#![no_std]
|
||||
#![warn(missing_docs)]
|
||||
@ -83,7 +83,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "_verify"))]
|
||||
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||
fn test_swap_state() {
|
||||
const STATE: Partition = Partition::new(0, 4096);
|
||||
const ACTIVE: Partition = Partition::new(4096, 61440);
|
||||
@ -136,7 +136,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "_verify"))]
|
||||
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||
fn test_separate_flash_active_page_biggest() {
|
||||
const STATE: Partition = Partition::new(2048, 4096);
|
||||
const ACTIVE: Partition = Partition::new(4096, 16384);
|
||||
@ -173,7 +173,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "_verify"))]
|
||||
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||
fn test_separate_flash_dfu_page_biggest() {
|
||||
const STATE: Partition = Partition::new(2048, 4096);
|
||||
const ACTIVE: Partition = Partition::new(4096, 16384);
|
||||
@ -212,7 +212,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "_verify")]
|
||||
#[cfg(all(feature = "nightly", feature = "_verify"))]
|
||||
fn test_verify() {
|
||||
// The following key setup is based on:
|
||||
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
||||
|
@ -3,6 +3,7 @@
|
||||
use core::ops::{Bound, Range, RangeBounds};
|
||||
|
||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
||||
#[cfg(feature = "nightly")]
|
||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||
|
||||
pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
|
||||
@ -134,6 +135,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFla
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
|
||||
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
||||
{
|
||||
@ -148,6 +150,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
|
||||
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
||||
#[cfg(feature = "nightly")]
|
||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||
|
||||
/// A region in flash used by the bootloader.
|
||||
@ -23,6 +24,7 @@ impl Partition {
|
||||
}
|
||||
|
||||
/// Read from the partition on the provided flash
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn read<F: AsyncReadNorFlash>(
|
||||
&self,
|
||||
flash: &mut F,
|
||||
@ -34,6 +36,7 @@ impl Partition {
|
||||
}
|
||||
|
||||
/// Write to the partition on the provided flash
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
|
||||
let offset = self.from as u32 + offset;
|
||||
flash.write(offset, bytes).await?;
|
||||
@ -42,6 +45,7 @@ impl Partition {
|
||||
}
|
||||
|
||||
/// Erase part of the partition on the provided flash
|
||||
#[cfg(feature = "nightly")]
|
||||
pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
|
||||
let from = self.from as u32 + from;
|
||||
let to = self.from as u32 + to;
|
||||
@ -51,6 +55,7 @@ impl Partition {
|
||||
}
|
||||
|
||||
/// Erase the entire partition
|
||||
#[cfg(feature = "nightly")]
|
||||
pub(crate) async fn wipe<F: AsyncNorFlash>(&self, flash: &mut F) -> Result<(), F::Error> {
|
||||
let from = self.from as u32;
|
||||
let to = self.to as u32;
|
||||
|
@ -17,12 +17,12 @@ target = "thumbv7em-none-eabi"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
||||
embassy-sync = { path = "../../embassy-sync" }
|
||||
embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] }
|
||||
embassy-nrf = { path = "../../embassy-nrf", default-features = false }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = "0.4.0"
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true }
|
||||
@ -36,3 +36,8 @@ defmt = [
|
||||
softdevice = [
|
||||
"nrf-softdevice-mbr",
|
||||
]
|
||||
nightly = [
|
||||
"dep:embedded-storage-async",
|
||||
"embassy-boot/nightly",
|
||||
"embassy-nrf/nightly"
|
||||
]
|
||||
|
@ -13,7 +13,7 @@ An adaptation of `embassy-boot` for nRF.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot-nrf` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||
`embassy-boot-nrf` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
mod fmt;
|
||||
|
@ -18,14 +18,14 @@ defmt-rtt = { version = "0.4", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
|
||||
embassy-sync = { path = "../../embassy-sync" }
|
||||
embassy-rp = { path = "../../embassy-rp", default-features = false, features = ["nightly"] }
|
||||
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
embassy-time = { path = "../../embassy-time", features = ["nightly"] }
|
||||
embassy-time = { path = "../../embassy-time" }
|
||||
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = "0.4.0"
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
[features]
|
||||
@ -40,6 +40,12 @@ log = [
|
||||
"embassy-rp/log",
|
||||
]
|
||||
debug = ["defmt-rtt"]
|
||||
nightly = [
|
||||
"dep:embedded-storage-async",
|
||||
"embassy-boot/nightly",
|
||||
"embassy-rp/nightly",
|
||||
"embassy-time/nightly"
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
debug = 2
|
||||
|
@ -13,7 +13,7 @@ NOTE: The applications using this bootloader should not link with the `link-rp.x
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot-rp` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||
`embassy-boot-rp` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
mod fmt;
|
||||
|
@ -19,12 +19,12 @@ defmt-rtt = { version = "0.4", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
|
||||
embassy-sync = { path = "../../embassy-sync" }
|
||||
embassy-stm32 = { path = "../../embassy-stm32", default-features = false, features = ["nightly"] }
|
||||
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = "0.4.0"
|
||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
[features]
|
||||
@ -39,6 +39,11 @@ log = [
|
||||
"embassy-stm32/log",
|
||||
]
|
||||
debug = ["defmt-rtt"]
|
||||
nightly = [
|
||||
"dep:embedded-storage-async",
|
||||
"embassy-boot/nightly",
|
||||
"embassy-stm32/nightly"
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
debug = 2
|
||||
|
@ -11,7 +11,7 @@ An adaptation of `embassy-boot` for STM32.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot-stm32` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||
`embassy-boot-stm32` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
mod fmt;
|
||||
|
@ -37,8 +37,8 @@ defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor"}
|
||||
embassy-macros = { version = "0.1.0", path = "../embassy-macros"}
|
||||
embassy-executor = { version = "0.2.0", path = "../embassy-executor"}
|
||||
embassy-macros = { version = "0.2.0", path = "../embassy-macros"}
|
||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"}
|
||||
atomic-polyfill = "1.0.1"
|
||||
critical-section = "1.1"
|
||||
|
@ -131,48 +131,6 @@ where
|
||||
type Error = E;
|
||||
}
|
||||
|
||||
#[cfg(feature = "_todo_embedded_hal_serial")]
|
||||
impl<T, E> embedded_hal_async::serial::Read for BlockingAsync<T>
|
||||
where
|
||||
T: serial::Read<u8, Error = E>,
|
||||
E: embedded_hal_1::serial::Error + 'static,
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
async move {
|
||||
let mut pos = 0;
|
||||
while pos < buf.len() {
|
||||
match self.wrapped.read() {
|
||||
Err(nb::Error::WouldBlock) => {}
|
||||
Err(nb::Error::Other(e)) => return Err(e),
|
||||
Ok(b) => {
|
||||
buf[pos] = b;
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "_todo_embedded_hal_serial")]
|
||||
impl<T, E> embedded_hal_async::serial::Write for BlockingAsync<T>
|
||||
where
|
||||
T: blocking::serial::Write<u8, Error = E> + serial::Read<u8, Error = E>,
|
||||
E: embedded_hal_1::serial::Error + 'static,
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
async move { self.wrapped.bwrite_all(buf) }
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where T: 'a;
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
async move { self.wrapped.bflush() }
|
||||
}
|
||||
}
|
||||
|
||||
/// NOR flash wrapper
|
||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||
|
23
embassy-executor/CHANGELOG.md
Normal file
23
embassy-executor/CHANGELOG.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 0.2.0 - 2023-04-27
|
||||
|
||||
- Replace unnecessary atomics in runqueue
|
||||
- add Pender, rework Cargo features.
|
||||
- add support for turbo-wakers.
|
||||
- Allow TaskStorage to auto-implement `Sync`
|
||||
- Use AtomicPtr for signal_ctx, removes 1 unsafe.
|
||||
- Replace unsound critical sections with atomics
|
||||
|
||||
## 0.1.1 - 2022-11-23
|
||||
|
||||
- Fix features for documentation
|
||||
|
||||
## 0.1.0 - 2022-11-23
|
||||
|
||||
- First release
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "embassy-executor"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "async/await executor designed for embedded usage"
|
||||
@ -61,7 +61,7 @@ log = { version = "0.4.14", optional = true }
|
||||
rtos-trace = { version = "0.1.2", optional = true }
|
||||
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
embassy-macros = { version = "0.1.0", path = "../embassy-macros" }
|
||||
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
|
||||
atomic-polyfill = "1.0.1"
|
||||
critical-section = "1.1"
|
||||
|
@ -4,15 +4,16 @@ use core::ptr::NonNull;
|
||||
use atomic_polyfill::{AtomicPtr, Ordering};
|
||||
|
||||
use super::{TaskHeader, TaskRef};
|
||||
use crate::raw::util::SyncUnsafeCell;
|
||||
|
||||
pub(crate) struct RunQueueItem {
|
||||
next: AtomicPtr<TaskHeader>,
|
||||
next: SyncUnsafeCell<Option<TaskRef>>,
|
||||
}
|
||||
|
||||
impl RunQueueItem {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
next: AtomicPtr::new(ptr::null_mut()),
|
||||
next: SyncUnsafeCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,7 +52,12 @@ impl RunQueue {
|
||||
self.head
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
|
||||
was_empty = prev.is_null();
|
||||
task.header().run_queue_item.next.store(prev, Ordering::Relaxed);
|
||||
unsafe {
|
||||
// safety: the pointer is either null or valid
|
||||
let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr()));
|
||||
// safety: there are no concurrent accesses to `next`
|
||||
task.header().run_queue_item.next.set(prev);
|
||||
}
|
||||
Some(task.as_ptr() as *mut _)
|
||||
})
|
||||
.ok();
|
||||
@ -64,18 +70,19 @@ impl RunQueue {
|
||||
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
|
||||
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
|
||||
// Atomically empty the queue.
|
||||
let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
|
||||
// safety: the pointer is either null or valid
|
||||
let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) };
|
||||
|
||||
// Iterate the linked list of tasks that were previously in the queue.
|
||||
while let Some(task) = NonNull::new(ptr) {
|
||||
let task = unsafe { TaskRef::from_ptr(task.as_ptr()) };
|
||||
while let Some(task) = next {
|
||||
// If the task re-enqueues itself, the `next` pointer will get overwritten.
|
||||
// Therefore, first read the next pointer, and only then process the task.
|
||||
let next = task.header().run_queue_item.next.load(Ordering::Relaxed);
|
||||
// safety: there are no concurrent accesses to `next`
|
||||
next = unsafe { task.header().run_queue_item.next.get() };
|
||||
|
||||
on_task(task);
|
||||
|
||||
ptr = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#[macro_export]
|
||||
macro_rules! peripherals {
|
||||
macro_rules! peripherals_definition {
|
||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||
/// Types for the peripheral singletons.
|
||||
pub mod peripherals {
|
||||
@ -26,7 +26,12 @@ macro_rules! peripherals {
|
||||
$crate::impl_peripheral!($name);
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! peripherals_struct {
|
||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||
/// Struct containing all the peripheral singletons.
|
||||
///
|
||||
/// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`].
|
||||
@ -76,6 +81,24 @@ macro_rules! peripherals {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! peripherals {
|
||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||
$crate::peripherals_definition!(
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name,
|
||||
)*
|
||||
);
|
||||
$crate::peripherals_struct!(
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name,
|
||||
)*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! into_ref {
|
||||
($($name:ident),*) => {
|
||||
|
@ -22,6 +22,7 @@ sx127x = []
|
||||
stm32wl = ["dep:embassy-stm32"]
|
||||
time = []
|
||||
defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"]
|
||||
external-lora-phy = ["dep:lora-phy"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -38,5 +39,6 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw
|
||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||
bit_field = { version = "0.10" }
|
||||
|
||||
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] }
|
||||
lorawan = { version = "0.7.1", default-features = false }
|
||||
lora-phy = { version = "1", optional = true }
|
||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
|
||||
lorawan = { version = "0.7.3", default-features = false }
|
||||
|
325
embassy-lora/src/iv.rs
Normal file
325
embassy-lora/src/iv.rs
Normal file
@ -0,0 +1,325 @@
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_stm32::interrupt::*;
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_stm32::{pac, PeripheralRef};
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
#[cfg(feature = "stm32wl")]
|
||||
use embassy_sync::signal::Signal;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::delay::DelayUs;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use lora_phy::mod_params::RadioError::*;
|
||||
use lora_phy::mod_params::{BoardType, RadioError};
|
||||
use lora_phy::mod_traits::InterfaceVariant;
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination
|
||||
pub struct Stm32wlInterfaceVariant<'a, CTRL> {
|
||||
board_type: BoardType,
|
||||
irq: PeripheralRef<'a, SUBGHZ_RADIO>,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
impl<'a, CTRL> Stm32wlInterfaceVariant<'a, CTRL>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
{
|
||||
/// Create an InterfaceVariant instance for an stm32wl/sx1262 combination
|
||||
pub fn new(
|
||||
irq: PeripheralRef<'a, SUBGHZ_RADIO>,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
) -> Result<Self, RadioError> {
|
||||
irq.disable();
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
Ok(Self {
|
||||
board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board
|
||||
irq,
|
||||
rf_switch_rx,
|
||||
rf_switch_tx,
|
||||
})
|
||||
}
|
||||
|
||||
fn on_interrupt(_: *mut ()) {
|
||||
unsafe { SUBGHZ_RADIO::steal() }.disable();
|
||||
IRQ_SIGNAL.signal(());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
impl<CTRL> InterfaceVariant for Stm32wlInterfaceVariant<'_, CTRL>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
{
|
||||
fn set_board_type(&mut self, board_type: BoardType) {
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
let pwr = pac::PWR;
|
||||
unsafe {
|
||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
let pwr = pac::PWR;
|
||||
unsafe {
|
||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
let rcc = pac::RCC;
|
||||
unsafe {
|
||||
rcc.csr().modify(|w| w.set_rfrst(true));
|
||||
rcc.csr().modify(|w| w.set_rfrst(false));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
let pwr = pac::PWR;
|
||||
while unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||
self.irq.enable();
|
||||
IRQ_SIGNAL.wait().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination
|
||||
pub struct Stm32l0InterfaceVariant<CTRL, WAIT> {
|
||||
board_type: BoardType,
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
irq: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> Stm32l0InterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
/// Create an InterfaceVariant instance for an stm32l0/sx1276 combination
|
||||
pub fn new(
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
irq: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
) -> Result<Self, RadioError> {
|
||||
Ok(Self {
|
||||
board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board
|
||||
nss,
|
||||
reset,
|
||||
irq,
|
||||
rf_switch_rx,
|
||||
rf_switch_tx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> InterfaceVariant for Stm32l0InterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
fn set_board_type(&mut self, board_type: BoardType) {
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_low().map_err(|_| NSS)
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_high().map_err(|_| NSS)
|
||||
}
|
||||
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
delay.delay_ms(10).await;
|
||||
self.reset.set_low().map_err(|_| Reset)?;
|
||||
delay.delay_ms(10).await;
|
||||
self.reset.set_high().map_err(|_| Reset)?;
|
||||
delay.delay_ms(10).await;
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
Ok(())
|
||||
}
|
||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||
self.irq.wait_for_high().await.map_err(|_| Irq)
|
||||
}
|
||||
|
||||
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board
|
||||
pub struct GenericSx126xInterfaceVariant<CTRL, WAIT> {
|
||||
board_type: BoardType,
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
dio1: WAIT,
|
||||
busy: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> GenericSx126xInterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
/// Create an InterfaceVariant instance for an nrf52840/sx1262 combination
|
||||
pub fn new(
|
||||
nss: CTRL,
|
||||
reset: CTRL,
|
||||
dio1: WAIT,
|
||||
busy: WAIT,
|
||||
rf_switch_rx: Option<CTRL>,
|
||||
rf_switch_tx: Option<CTRL>,
|
||||
) -> Result<Self, RadioError> {
|
||||
Ok(Self {
|
||||
board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board
|
||||
nss,
|
||||
reset,
|
||||
dio1,
|
||||
busy,
|
||||
rf_switch_rx,
|
||||
rf_switch_tx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTRL, WAIT> InterfaceVariant for GenericSx126xInterfaceVariant<CTRL, WAIT>
|
||||
where
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
fn set_board_type(&mut self, board_type: BoardType) {
|
||||
self.board_type = board_type;
|
||||
}
|
||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_low().map_err(|_| NSS)
|
||||
}
|
||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||
self.nss.set_high().map_err(|_| NSS)
|
||||
}
|
||||
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||
delay.delay_ms(10).await;
|
||||
self.reset.set_low().map_err(|_| Reset)?;
|
||||
delay.delay_ms(20).await;
|
||||
self.reset.set_high().map_err(|_| Reset)?;
|
||||
delay.delay_ms(10).await;
|
||||
Ok(())
|
||||
}
|
||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||
self.busy.wait_for_low().await.map_err(|_| Busy)
|
||||
}
|
||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||
if self.board_type != BoardType::RpPicoWaveshareSx1262 {
|
||||
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
||||
} else {
|
||||
self.dio1.wait_for_rising_edge().await.map_err(|_| DIO1)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||
match &mut self.rf_switch_rx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||
None => (),
|
||||
};
|
||||
match &mut self.rf_switch_tx {
|
||||
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(async_fn_in_trait, impl_trait_projections)]
|
||||
#![allow(incomplete_features)]
|
||||
//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
|
||||
//! crate's async LoRaWAN MAC implementation.
|
||||
|
||||
pub(crate) mod fmt;
|
||||
#[cfg(feature = "external-lora-phy")]
|
||||
/// interface variants required by the external lora crate
|
||||
pub mod iv;
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
|
||||
pub mod stm32wl;
|
||||
#[cfg(feature = "sx126x")]
|
||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
|
||||
pub mod sx126x;
|
||||
#[cfg(feature = "sx127x")]
|
||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
|
||||
pub mod sx127x;
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
@ -34,13 +41,11 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
||||
self.start = Instant::now();
|
||||
}
|
||||
|
||||
type AtFuture<'m> = impl core::future::Future<Output = ()> + 'm;
|
||||
fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> {
|
||||
Timer::at(self.start + Duration::from_millis(millis))
|
||||
async fn at(&mut self, millis: u64) {
|
||||
Timer::at(self.start + Duration::from_millis(millis)).await
|
||||
}
|
||||
|
||||
type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm;
|
||||
fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> {
|
||||
Timer::after(Duration::from_millis(millis))
|
||||
async fn delay_ms(&mut self, millis: u64) {
|
||||
Timer::after(Duration::from_millis(millis)).await
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! A radio driver integration for the radio found on STM32WL family devices.
|
||||
use core::future::{poll_fn, Future};
|
||||
#![allow(deprecated)]
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
@ -241,14 +242,12 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf
|
||||
impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
|
||||
type PhyError = RadioError;
|
||||
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where Self: 'm;
|
||||
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
|
||||
async move { self.do_tx(config, buf).await }
|
||||
async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
|
||||
self.do_tx(config, buf).await
|
||||
}
|
||||
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where Self: 'm;
|
||||
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
|
||||
async move { self.do_rx(config, buf).await }
|
||||
async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
|
||||
self.do_rx(config, buf).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
use core::future::Future;
|
||||
|
||||
use defmt::Format;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
@ -71,83 +69,69 @@ where
|
||||
{
|
||||
type PhyError = RadioError<BUS>;
|
||||
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
|
||||
where
|
||||
SPI: 'm,
|
||||
CTRL: 'm,
|
||||
WAIT: 'm,
|
||||
BUS: 'm;
|
||||
|
||||
fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
|
||||
async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result<u32, Self::PhyError> {
|
||||
trace!("TX START");
|
||||
async move {
|
||||
self.lora
|
||||
.set_tx_config(
|
||||
config.pw,
|
||||
config.rf.spreading_factor.into(),
|
||||
config.rf.bandwidth.into(),
|
||||
config.rf.coding_rate.into(),
|
||||
8,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
self.lora.set_max_payload_length(buffer.len() as u8).await?;
|
||||
self.lora.set_channel(config.rf.frequency).await?;
|
||||
self.lora.send(buffer, 0xffffff).await?;
|
||||
self.lora.process_irq(None, None, None).await?;
|
||||
trace!("TX DONE");
|
||||
return Ok(0);
|
||||
}
|
||||
self.lora
|
||||
.set_tx_config(
|
||||
config.pw,
|
||||
config.rf.spreading_factor.into(),
|
||||
config.rf.bandwidth.into(),
|
||||
config.rf.coding_rate.into(),
|
||||
8,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
self.lora.set_max_payload_length(buffer.len() as u8).await?;
|
||||
self.lora.set_channel(config.rf.frequency).await?;
|
||||
self.lora.send(buffer, 0xffffff).await?;
|
||||
self.lora.process_irq(None, None, None).await?;
|
||||
trace!("TX DONE");
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
|
||||
where
|
||||
SPI: 'm,
|
||||
CTRL: 'm,
|
||||
WAIT: 'm,
|
||||
BUS: 'm;
|
||||
|
||||
fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
|
||||
async fn rx(
|
||||
&mut self,
|
||||
config: RfConfig,
|
||||
receiving_buffer: &mut [u8],
|
||||
) -> Result<(usize, RxQuality), Self::PhyError> {
|
||||
trace!("RX START");
|
||||
async move {
|
||||
self.lora
|
||||
.set_rx_config(
|
||||
config.spreading_factor.into(),
|
||||
config.bandwidth.into(),
|
||||
config.coding_rate.into(),
|
||||
8,
|
||||
4,
|
||||
false,
|
||||
0u8,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
|
||||
self.lora.set_channel(config.frequency).await?;
|
||||
self.lora.rx(90 * 1000).await?;
|
||||
let mut received_len = 0u8;
|
||||
self.lora
|
||||
.process_irq(Some(receiving_buffer), Some(&mut received_len), None)
|
||||
.await?;
|
||||
trace!("RX DONE");
|
||||
self.lora
|
||||
.set_rx_config(
|
||||
config.spreading_factor.into(),
|
||||
config.bandwidth.into(),
|
||||
config.coding_rate.into(),
|
||||
8,
|
||||
4,
|
||||
false,
|
||||
0u8,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
|
||||
self.lora.set_channel(config.frequency).await?;
|
||||
self.lora.rx(90 * 1000).await?;
|
||||
let mut received_len = 0u8;
|
||||
self.lora
|
||||
.process_irq(Some(receiving_buffer), Some(&mut received_len), None)
|
||||
.await?;
|
||||
trace!("RX DONE");
|
||||
|
||||
let packet_status = self.lora.get_latest_packet_status();
|
||||
let mut rssi = 0i16;
|
||||
let mut snr = 0i8;
|
||||
if packet_status.is_some() {
|
||||
rssi = packet_status.unwrap().rssi as i16;
|
||||
snr = packet_status.unwrap().snr;
|
||||
}
|
||||
|
||||
Ok((received_len as usize, RxQuality::new(rssi, snr)))
|
||||
let packet_status = self.lora.get_latest_packet_status();
|
||||
let mut rssi = 0i16;
|
||||
let mut snr = 0i8;
|
||||
if packet_status.is_some() {
|
||||
rssi = packet_status.unwrap().rssi as i16;
|
||||
snr = packet_status.unwrap().snr;
|
||||
}
|
||||
|
||||
Ok((received_len as usize, RxQuality::new(rssi, snr)))
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
use core::future::Future;
|
||||
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::*;
|
||||
@ -88,101 +86,78 @@ where
|
||||
{
|
||||
type PhyError = Sx127xError;
|
||||
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
|
||||
where
|
||||
SPI: 'm,
|
||||
CS: 'm,
|
||||
RESET: 'm,
|
||||
E: 'm,
|
||||
I: 'm,
|
||||
RFS: 'm;
|
||||
|
||||
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
|
||||
async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
|
||||
trace!("TX START");
|
||||
async move {
|
||||
self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
|
||||
self.rfs.set_tx();
|
||||
self.radio.set_tx_power(14, 0).await?;
|
||||
self.radio.set_frequency(config.rf.frequency).await?;
|
||||
// TODO: Modify radio to support other coding rates
|
||||
self.radio.set_coding_rate_4(5).await?;
|
||||
self.radio
|
||||
.set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))
|
||||
.await?;
|
||||
self.radio
|
||||
.set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))
|
||||
.await?;
|
||||
|
||||
self.radio.set_preamble_length(8).await?;
|
||||
self.radio.set_lora_pa_ramp().await?;
|
||||
self.radio.set_lora_sync_word().await?;
|
||||
self.radio.set_invert_iq(false).await?;
|
||||
self.radio.set_crc(true).await?;
|
||||
|
||||
self.radio.set_dio0_tx_done().await?;
|
||||
|
||||
self.radio.transmit_start(buf).await?;
|
||||
|
||||
loop {
|
||||
self.irq.wait_for_rising_edge().await.unwrap();
|
||||
self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
|
||||
self.rfs.set_tx();
|
||||
self.radio.set_tx_power(14, 0).await?;
|
||||
self.radio.set_frequency(config.rf.frequency).await?;
|
||||
// TODO: Modify radio to support other coding rates
|
||||
self.radio.set_coding_rate_4(5).await?;
|
||||
self.radio
|
||||
.set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))
|
||||
.await?;
|
||||
self.radio
|
||||
.set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))
|
||||
.await?;
|
||||
|
||||
self.radio.set_preamble_length(8).await?;
|
||||
self.radio.set_lora_pa_ramp().await?;
|
||||
self.radio.set_lora_sync_word().await?;
|
||||
self.radio.set_invert_iq(false).await?;
|
||||
self.radio.set_crc(true).await?;
|
||||
|
||||
self.radio.set_dio0_tx_done().await?;
|
||||
|
||||
self.radio.transmit_start(buf).await?;
|
||||
|
||||
loop {
|
||||
self.irq.wait_for_rising_edge().await.unwrap();
|
||||
self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
|
||||
let irq = self.radio.clear_irq().await.ok().unwrap();
|
||||
if (irq & IRQ::IrqTxDoneMask.addr()) != 0 {
|
||||
trace!("TX DONE");
|
||||
return Ok(0);
|
||||
}
|
||||
let irq = self.radio.clear_irq().await.ok().unwrap();
|
||||
if (irq & IRQ::IrqTxDoneMask.addr()) != 0 {
|
||||
trace!("TX DONE");
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
|
||||
where
|
||||
SPI: 'm,
|
||||
CS: 'm,
|
||||
RESET: 'm,
|
||||
E: 'm,
|
||||
I: 'm,
|
||||
RFS: 'm;
|
||||
async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
|
||||
self.rfs.set_rx();
|
||||
self.radio.reset_payload_length().await?;
|
||||
self.radio.set_frequency(config.frequency).await?;
|
||||
// TODO: Modify radio to support other coding rates
|
||||
self.radio.set_coding_rate_4(5).await?;
|
||||
self.radio
|
||||
.set_signal_bandwidth(bandwidth_to_i64(config.bandwidth))
|
||||
.await?;
|
||||
self.radio
|
||||
.set_spreading_factor(spreading_factor_to_u8(config.spreading_factor))
|
||||
.await?;
|
||||
|
||||
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
|
||||
trace!("RX START");
|
||||
async move {
|
||||
self.rfs.set_rx();
|
||||
self.radio.reset_payload_length().await?;
|
||||
self.radio.set_frequency(config.frequency).await?;
|
||||
// TODO: Modify radio to support other coding rates
|
||||
self.radio.set_coding_rate_4(5).await?;
|
||||
self.radio
|
||||
.set_signal_bandwidth(bandwidth_to_i64(config.bandwidth))
|
||||
.await?;
|
||||
self.radio
|
||||
.set_spreading_factor(spreading_factor_to_u8(config.spreading_factor))
|
||||
.await?;
|
||||
self.radio.set_preamble_length(8).await?;
|
||||
self.radio.set_lora_sync_word().await?;
|
||||
self.radio.set_invert_iq(true).await?;
|
||||
self.radio.set_crc(true).await?;
|
||||
|
||||
self.radio.set_preamble_length(8).await?;
|
||||
self.radio.set_lora_sync_word().await?;
|
||||
self.radio.set_invert_iq(true).await?;
|
||||
self.radio.set_crc(true).await?;
|
||||
self.radio.set_dio0_rx_done().await?;
|
||||
self.radio.set_mode(RadioMode::RxContinuous).await?;
|
||||
|
||||
self.radio.set_dio0_rx_done().await?;
|
||||
self.radio.set_mode(RadioMode::RxContinuous).await?;
|
||||
|
||||
loop {
|
||||
self.irq.wait_for_rising_edge().await.unwrap();
|
||||
self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
|
||||
let irq = self.radio.clear_irq().await.ok().unwrap();
|
||||
if (irq & IRQ::IrqRxDoneMask.addr()) != 0 {
|
||||
let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16;
|
||||
let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8;
|
||||
let response = if let Ok(size) = self.radio.read_packet_size().await {
|
||||
self.radio.read_packet(buf).await?;
|
||||
Ok((size, RxQuality::new(rssi, snr)))
|
||||
} else {
|
||||
Ok((0, RxQuality::new(rssi, snr)))
|
||||
};
|
||||
trace!("RX DONE");
|
||||
return response;
|
||||
}
|
||||
loop {
|
||||
self.irq.wait_for_rising_edge().await.unwrap();
|
||||
self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
|
||||
let irq = self.radio.clear_irq().await.ok().unwrap();
|
||||
if (irq & IRQ::IrqRxDoneMask.addr()) != 0 {
|
||||
let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16;
|
||||
let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8;
|
||||
let response = if let Ok(size) = self.radio.read_packet_size().await {
|
||||
self.radio.read_packet(buf).await?;
|
||||
Ok((size, RxQuality::new(rssi, snr)))
|
||||
} else {
|
||||
Ok((0, RxQuality::new(rssi, snr)))
|
||||
};
|
||||
trace!("RX DONE");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "embassy-macros"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "macros for creating the entry point and tasks for embassy-executor"
|
||||
|
@ -27,12 +27,10 @@ use embassy_sync::waitqueue::WakerRegistration;
|
||||
use embassy_time::{Instant, Timer};
|
||||
use futures::pin_mut;
|
||||
use heapless::Vec;
|
||||
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::iface::{Interface, SocketSet, SocketStorage};
|
||||
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
use smoltcp::socket::dhcpv4;
|
||||
use smoltcp::socket::dhcpv4::RetryConfig;
|
||||
use smoltcp::time::Duration;
|
||||
// smoltcp reexports
|
||||
pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant};
|
||||
@ -76,6 +74,7 @@ pub struct StaticConfig {
|
||||
pub dns_servers: Vec<Ipv4Address, 3>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DhcpConfig {
|
||||
pub max_lease_duration: Option<Duration>,
|
||||
@ -88,6 +87,7 @@ pub struct DhcpConfig {
|
||||
pub client_port: u16,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
impl Default for DhcpConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@ -384,6 +384,7 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
self.config = Some(config)
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) {
|
||||
socket.set_ignore_naks(config.ignore_naks);
|
||||
socket.set_max_lease_duration(config.max_lease_duration);
|
||||
|
@ -78,7 +78,7 @@ _dppi = []
|
||||
_gpio-p1 = []
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor", optional = true }
|
||||
embassy-executor = { version = "0.2.0", path = "../embassy-executor", optional = true }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]}
|
||||
|
@ -184,6 +184,34 @@ pub mod config {
|
||||
NotConfigured,
|
||||
}
|
||||
|
||||
/// Settings for enabling the built in DCDC converters.
|
||||
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
||||
pub struct DcdcConfig {
|
||||
/// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used.
|
||||
#[cfg(feature = "nrf52840")]
|
||||
pub reg0: bool,
|
||||
/// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used.
|
||||
pub reg1: bool,
|
||||
}
|
||||
|
||||
/// Settings for enabling the built in DCDC converters.
|
||||
#[cfg(feature = "_nrf5340-app")]
|
||||
pub struct DcdcConfig {
|
||||
/// Config for the high voltage stage, if disabled LDO will be used.
|
||||
pub regh: bool,
|
||||
/// Config for the main rail, if disabled LDO will be used.
|
||||
pub regmain: bool,
|
||||
/// Config for the radio rail, if disabled LDO will be used.
|
||||
pub regradio: bool,
|
||||
}
|
||||
|
||||
/// Settings for enabling the built in DCDC converter.
|
||||
#[cfg(feature = "_nrf9160")]
|
||||
pub struct DcdcConfig {
|
||||
/// Config for the main rail, if disabled LDO will be used.
|
||||
pub regmain: bool,
|
||||
}
|
||||
|
||||
/// Configuration for peripherals. Default configuration should work on any nRF chip.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
@ -191,6 +219,9 @@ pub mod config {
|
||||
pub hfclk_source: HfclkSource,
|
||||
/// Low frequency clock source.
|
||||
pub lfclk_source: LfclkSource,
|
||||
#[cfg(not(feature = "_nrf5340-net"))]
|
||||
/// DCDC configuration.
|
||||
pub dcdc: DcdcConfig,
|
||||
/// GPIOTE interrupt priority. Should be lower priority than softdevice if used.
|
||||
#[cfg(feature = "gpiote")]
|
||||
pub gpiote_interrupt_priority: crate::interrupt::Priority,
|
||||
@ -209,6 +240,20 @@ pub mod config {
|
||||
// xtals if they know they have them.
|
||||
hfclk_source: HfclkSource::Internal,
|
||||
lfclk_source: LfclkSource::InternalRC,
|
||||
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
||||
dcdc: DcdcConfig {
|
||||
#[cfg(feature = "nrf52840")]
|
||||
reg0: false,
|
||||
reg1: false,
|
||||
},
|
||||
#[cfg(feature = "_nrf5340-app")]
|
||||
dcdc: DcdcConfig {
|
||||
regh: false,
|
||||
regmain: false,
|
||||
regradio: false,
|
||||
},
|
||||
#[cfg(feature = "_nrf9160")]
|
||||
dcdc: DcdcConfig { regmain: false },
|
||||
#[cfg(feature = "gpiote")]
|
||||
gpiote_interrupt_priority: crate::interrupt::Priority::P0,
|
||||
#[cfg(feature = "_time-driver")]
|
||||
@ -454,6 +499,41 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
r.tasks_lfclkstart.write(|w| unsafe { w.bits(1) });
|
||||
while r.events_lfclkstarted.read().bits() == 0 {}
|
||||
|
||||
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
||||
{
|
||||
// Setup DCDCs.
|
||||
let pwr = unsafe { &*pac::POWER::ptr() };
|
||||
#[cfg(feature = "nrf52840")]
|
||||
if config.dcdc.reg0 {
|
||||
pwr.dcdcen0.write(|w| w.dcdcen().set_bit());
|
||||
}
|
||||
if config.dcdc.reg1 {
|
||||
pwr.dcdcen.write(|w| w.dcdcen().set_bit());
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "_nrf9160")]
|
||||
{
|
||||
// Setup DCDC.
|
||||
let reg = unsafe { &*pac::REGULATORS::ptr() };
|
||||
if config.dcdc.regmain {
|
||||
reg.dcdcen.write(|w| w.dcdcen().set_bit());
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "_nrf5340-app")]
|
||||
{
|
||||
// Setup DCDC.
|
||||
let reg = unsafe { &*pac::REGULATORS::ptr() };
|
||||
if config.dcdc.regh {
|
||||
reg.vregh.dcdcen.write(|w| w.dcdcen().set_bit());
|
||||
}
|
||||
if config.dcdc.regmain {
|
||||
reg.vregmain.dcdcen.write(|w| w.dcdcen().set_bit());
|
||||
}
|
||||
if config.dcdc.regradio {
|
||||
reg.vregradio.dcdcen.write(|w| w.dcdcen().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
// Init GPIOTE
|
||||
#[cfg(feature = "gpiote")]
|
||||
gpiote::init(config.gpiote_interrupt_priority);
|
||||
|
@ -992,80 +992,3 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
feature = "_todo_embedded_hal_serial"
|
||||
))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ time-driver = []
|
||||
rom-func-cache = []
|
||||
intrinsics = []
|
||||
rom-v2-intrinsics = []
|
||||
pio = ["dep:pio", "dep:pio-proc"]
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"]
|
||||
@ -40,7 +39,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
|
||||
embassy-executor = { version = "0.2.0", path = "../embassy-executor" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]}
|
||||
@ -60,9 +59,9 @@ chrono = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
|
||||
embedded-storage = { version = "0.3" }
|
||||
rand_core = "0.6.4"
|
||||
fixed = "1.23.1"
|
||||
|
||||
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
|
||||
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
|
||||
rp-pac = { version = "2", features = ["rt"] }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
|
||||
@ -70,5 +69,5 @@ embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
|
||||
|
||||
paste = "1.0"
|
||||
pio-proc = {version= "0.2", optional = true}
|
||||
pio = {version= "0.2.1", optional = true}
|
||||
pio-proc = {version= "0.2" }
|
||||
pio = {version= "0.2.1" }
|
||||
|
@ -155,8 +155,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1:
|
||||
let cs = p.cs().read();
|
||||
let prim = p.prim().read();
|
||||
if cs.lock()
|
||||
&& cs.refdiv() == refdiv as _
|
||||
&& p.fbdiv_int().read().fbdiv_int() == fbdiv as _
|
||||
&& cs.refdiv() == refdiv as u8
|
||||
&& p.fbdiv_int().read().fbdiv_int() == fbdiv as u16
|
||||
&& prim.postdiv1() == post_div1
|
||||
&& prim.postdiv2() == post_div2
|
||||
{
|
||||
|
@ -162,8 +162,6 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
|
||||
/// - interrupts must be disabled
|
||||
/// - DMA must not access flash memory
|
||||
unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> {
|
||||
let dma_status = &mut [false; crate::dma::CHANNEL_COUNT];
|
||||
|
||||
// Make sure we're running on CORE0
|
||||
let core_id: u32 = unsafe { pac::SIO.cpuid().read() };
|
||||
if core_id != 0 {
|
||||
@ -174,25 +172,15 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
|
||||
crate::multicore::pause_core1();
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Pause all DMA channels for the duration of the ram operation
|
||||
for (number, status) in dma_status.iter_mut().enumerate() {
|
||||
let ch = crate::pac::DMA.ch(number as _);
|
||||
*status = ch.ctrl_trig().read().en();
|
||||
if *status {
|
||||
ch.ctrl_trig().modify(|w| w.set_en(false));
|
||||
}
|
||||
// Wait for all DMA channels in flash to finish before ram operation
|
||||
const SRAM_LOWER: u32 = 0x2000_0000;
|
||||
for n in 0..crate::dma::CHANNEL_COUNT {
|
||||
let ch = crate::pac::DMA.ch(n);
|
||||
while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}
|
||||
}
|
||||
|
||||
// Run our flash operation in RAM
|
||||
operation();
|
||||
|
||||
// Re-enable previously enabled DMA channels
|
||||
for (number, status) in dma_status.iter().enumerate() {
|
||||
let ch = crate::pac::DMA.ch(number as _);
|
||||
if *status {
|
||||
ch.ctrl_trig().modify(|w| w.set_en(true));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Resume CORE1 execution
|
||||
|
92
embassy-rp/src/float/add_sub.rs
Normal file
92
embassy-rp/src/float/add_sub.rs
Normal file
@ -0,0 +1,92 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs
|
||||
|
||||
use super::{Float, Int};
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMAdd {
|
||||
fn rom_add(self, b: Self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMAdd for f32 {
|
||||
fn rom_add(self, b: Self) -> Self {
|
||||
rom_data::float_funcs::fadd(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMAdd for f64 {
|
||||
fn rom_add(self, b: Self) -> Self {
|
||||
rom_data::double_funcs::dadd(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn add<F: Float + ROMAdd>(a: F, b: F) -> F {
|
||||
if a.is_not_finite() {
|
||||
if b.is_not_finite() {
|
||||
let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
|
||||
let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
|
||||
|
||||
if class_a == F::Int::ZERO && class_b == F::Int::ZERO {
|
||||
// inf + inf = inf
|
||||
return a;
|
||||
}
|
||||
if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK {
|
||||
// -inf + (-inf) = -inf
|
||||
return a;
|
||||
}
|
||||
|
||||
// Sign mismatch, or either is NaN already
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
// [-]inf/NaN + X = [-]inf/NaN
|
||||
return a;
|
||||
}
|
||||
|
||||
if b.is_not_finite() {
|
||||
// X + [-]inf/NaN = [-]inf/NaN
|
||||
return b;
|
||||
}
|
||||
|
||||
a.rom_add(b)
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[alias = __addsf3vfp]
|
||||
#[aeabi = __aeabi_fadd]
|
||||
extern "C" fn __addsf3(a: f32, b: f32) -> f32 {
|
||||
add(a, b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __adddf3vfp]
|
||||
#[aeabi = __aeabi_dadd]
|
||||
extern "C" fn __adddf3(a: f64, b: f64) -> f64 {
|
||||
add(a, b)
|
||||
}
|
||||
|
||||
// The ROM just implements subtraction the same way, so just do it here
|
||||
// and save the work of implementing more complicated NaN/inf handling.
|
||||
|
||||
#[alias = __subsf3vfp]
|
||||
#[aeabi = __aeabi_fsub]
|
||||
extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
|
||||
add(a, -b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __subdf3vfp]
|
||||
#[aeabi = __aeabi_dsub]
|
||||
extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
|
||||
add(a, -b)
|
||||
}
|
||||
|
||||
extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 {
|
||||
add(b, -a)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 {
|
||||
add(b, -a)
|
||||
}
|
||||
}
|
201
embassy-rp/src/float/cmp.rs
Normal file
201
embassy-rp/src/float/cmp.rs
Normal file
@ -0,0 +1,201 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs
|
||||
|
||||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMCmp {
|
||||
fn rom_cmp(self, b: Self) -> i32;
|
||||
}
|
||||
|
||||
impl ROMCmp for f32 {
|
||||
fn rom_cmp(self, b: Self) -> i32 {
|
||||
rom_data::float_funcs::fcmp(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMCmp for f64 {
|
||||
fn rom_cmp(self, b: Self) -> i32 {
|
||||
rom_data::double_funcs::dcmp(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn le_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
|
||||
if a.is_nan() || b.is_nan() {
|
||||
1
|
||||
} else {
|
||||
a.rom_cmp(b)
|
||||
}
|
||||
}
|
||||
|
||||
fn ge_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
|
||||
if a.is_nan() || b.is_nan() {
|
||||
-1
|
||||
} else {
|
||||
a.rom_cmp(b)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __eqsf2, __ltsf2, __nesf2]
|
||||
extern "C" fn __lesf2(a: f32, b: f32) -> i32 {
|
||||
le_abi(a, b)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __eqdf2, __ltdf2, __nedf2]
|
||||
extern "C" fn __ledf2(a: f64, b: f64) -> i32 {
|
||||
le_abi(a, b)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __gtsf2]
|
||||
extern "C" fn __gesf2(a: f32, b: f32) -> i32 {
|
||||
ge_abi(a, b)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[alias = __gtdf2]
|
||||
extern "C" fn __gedf2(a: f64, b: f64) -> i32 {
|
||||
ge_abi(a, b)
|
||||
}
|
||||
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) >= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 {
|
||||
(ge_abi(a, b) > 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) < 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) <= 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) != 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) != 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 {
|
||||
(le_abi(a, b) == 0) as i32
|
||||
}
|
||||
}
|
157
embassy-rp/src/float/conv.rs
Normal file
157
embassy-rp/src/float/conv.rs
Normal file
@ -0,0 +1,157 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs
|
||||
|
||||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
// Some of these are also not connected in the Pico SDK. This is probably
|
||||
// because the ROM version actually does a fixed point conversion, just with
|
||||
// the fractional width set to zero.
|
||||
|
||||
intrinsics! {
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_i2f]
|
||||
extern "C" fn __floatsisf(i: i32) -> f32 {
|
||||
rom_data::float_funcs::int_to_float(i)
|
||||
}
|
||||
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_i2d]
|
||||
extern "C" fn __floatsidf(i: i32) -> f64 {
|
||||
rom_data::double_funcs::int_to_double(i)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[aeabi = __aeabi_l2f]
|
||||
extern "C" fn __floatdisf(i: i64) -> f32 {
|
||||
rom_data::float_funcs::int64_to_float(i)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_l2d]
|
||||
extern "C" fn __floatdidf(i: i64) -> f64 {
|
||||
rom_data::double_funcs::int64_to_double(i)
|
||||
}
|
||||
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_ui2f]
|
||||
extern "C" fn __floatunsisf(i: u32) -> f32 {
|
||||
rom_data::float_funcs::uint_to_float(i)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_ui2d]
|
||||
extern "C" fn __floatunsidf(i: u32) -> f64 {
|
||||
rom_data::double_funcs::uint_to_double(i)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_ul2f]
|
||||
extern "C" fn __floatundisf(i: u64) -> f32 {
|
||||
rom_data::float_funcs::uint64_to_float(i)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_ul2d]
|
||||
extern "C" fn __floatundidf(i: u64) -> f64 {
|
||||
rom_data::double_funcs::uint64_to_double(i)
|
||||
}
|
||||
|
||||
|
||||
// The Pico SDK does some optimization here (e.x. fast paths for zero and
|
||||
// one), but we can just directly connect it.
|
||||
#[aeabi = __aeabi_f2iz]
|
||||
extern "C" fn __fixsfsi(f: f32) -> i32 {
|
||||
rom_data::float_funcs::float_to_int(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_f2lz]
|
||||
extern "C" fn __fixsfdi(f: f32) -> i64 {
|
||||
rom_data::float_funcs::float_to_int64(f)
|
||||
}
|
||||
|
||||
// Not connected in the Pico SDK
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2iz]
|
||||
extern "C" fn __fixdfsi(f: f64) -> i32 {
|
||||
rom_data::double_funcs::double_to_int(f)
|
||||
}
|
||||
|
||||
// Like with the 32 bit version, there's optimization that we just
|
||||
// skip.
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2lz]
|
||||
extern "C" fn __fixdfdi(f: f64) -> i64 {
|
||||
rom_data::double_funcs::double_to_int64(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[aeabi = __aeabi_f2uiz]
|
||||
extern "C" fn __fixunssfsi(f: f32) -> u32 {
|
||||
rom_data::float_funcs::float_to_uint(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_f2ulz]
|
||||
extern "C" fn __fixunssfdi(f: f32) -> u64 {
|
||||
rom_data::float_funcs::float_to_uint64(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2uiz]
|
||||
extern "C" fn __fixunsdfsi(f: f64) -> u32 {
|
||||
rom_data::double_funcs::double_to_uint(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
#[aeabi = __aeabi_d2ulz]
|
||||
extern "C" fn __fixunsdfdi(f: f64) -> u64 {
|
||||
rom_data::double_funcs::double_to_uint64(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __extendsfdf2vfp]
|
||||
#[aeabi = __aeabi_f2d]
|
||||
extern "C" fn __extendsfdf2(f: f32) -> f64 {
|
||||
if f.is_not_finite() {
|
||||
return f64::from_repr(
|
||||
// Not finite
|
||||
f64::EXPONENT_MASK |
|
||||
// Preserve NaN or inf
|
||||
((f.repr() & f32::SIGNIFICAND_MASK) as u64) |
|
||||
// Preserve sign
|
||||
((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS)
|
||||
);
|
||||
}
|
||||
rom_data::float_funcs::float_to_double(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __truncdfsf2vfp]
|
||||
#[aeabi = __aeabi_d2f]
|
||||
extern "C" fn __truncdfsf2(f: f64) -> f32 {
|
||||
if f.is_not_finite() {
|
||||
let mut repr: u32 =
|
||||
// Not finite
|
||||
f32::EXPONENT_MASK |
|
||||
// Preserve sign
|
||||
((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32;
|
||||
// Set NaN
|
||||
if (f.repr() & f64::SIGNIFICAND_MASK) != 0 {
|
||||
repr |= 1;
|
||||
}
|
||||
return f32::from_repr(repr);
|
||||
}
|
||||
rom_data::double_funcs::double_to_float(f)
|
||||
}
|
||||
}
|
141
embassy-rp/src/float/div.rs
Normal file
141
embassy-rp/src/float/div.rs
Normal file
@ -0,0 +1,141 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs
|
||||
|
||||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
// Make sure this stays as a separate call, because when it's inlined the
|
||||
// compiler will move the save of the registers used to contain the divider
|
||||
// state into the function prologue. That save and restore (push/pop) takes
|
||||
// longer than the actual division, so doing it in the common case where
|
||||
// they are not required wastes a lot of time.
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn save_divider_and_call<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let sio = rp_pac::SIO;
|
||||
|
||||
unsafe {
|
||||
// Since we can't save the signed-ness of the calculation, we have to make
|
||||
// sure that there's at least an 8 cycle delay before we read the result.
|
||||
// The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads.
|
||||
// Since we can't be sure the Rust implementation will optimize to the same,
|
||||
// just use an explicit wait.
|
||||
while !sio.div().csr().read().ready() {}
|
||||
|
||||
// Read the quotient last, since that's what clears the dirty flag
|
||||
let dividend = sio.div().udividend().read();
|
||||
let divisor = sio.div().udivisor().read();
|
||||
let remainder = sio.div().remainder().read();
|
||||
let quotient = sio.div().quotient().read();
|
||||
|
||||
// If we get interrupted here (before a write sets the DIRTY flag) its fine, since
|
||||
// we have the full state, so the interruptor doesn't have to restore it. Once the
|
||||
// write happens and the DIRTY flag is set, the interruptor becomes responsible for
|
||||
// restoring our state.
|
||||
let result = f();
|
||||
|
||||
// If we are interrupted here, then the interruptor will start an incorrect calculation
|
||||
// using a wrong divisor, but we'll restore the divisor and result ourselves correctly.
|
||||
// This sets DIRTY, so any interruptor will save the state.
|
||||
sio.div().udividend().write_value(dividend);
|
||||
// If we are interrupted here, the the interruptor may start the calculation using
|
||||
// incorrectly signed inputs, but we'll restore the result ourselves.
|
||||
// This sets DIRTY, so any interruptor will save the state.
|
||||
sio.div().udivisor().write_value(divisor);
|
||||
// If we are interrupted here, the interruptor will have restored everything but the
|
||||
// quotient may be wrongly signed. If the calculation started by the above writes is
|
||||
// still ongoing it is stopped, so it won't replace the result we're restoring.
|
||||
// DIRTY and READY set, but only DIRTY matters to make the interruptor save the state.
|
||||
sio.div().remainder().write_value(remainder);
|
||||
// State fully restored after the quotient write. This sets both DIRTY and READY, so
|
||||
// whatever we may have interrupted can read the result.
|
||||
sio.div().quotient().write_value(quotient);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn save_divider<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let sio = rp_pac::SIO;
|
||||
if unsafe { !sio.div().csr().read().dirty() } {
|
||||
// Not dirty, so nothing is waiting for the calculation. So we can just
|
||||
// issue it directly without a save/restore.
|
||||
f()
|
||||
} else {
|
||||
save_divider_and_call(f)
|
||||
}
|
||||
}
|
||||
|
||||
trait ROMDiv {
|
||||
fn rom_div(self, b: Self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMDiv for f32 {
|
||||
fn rom_div(self, b: Self) -> Self {
|
||||
// ROM implementation uses the hardware divider, so we have to save it
|
||||
save_divider(|| rom_data::float_funcs::fdiv(self, b))
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMDiv for f64 {
|
||||
fn rom_div(self, b: Self) -> Self {
|
||||
// ROM implementation uses the hardware divider, so we have to save it
|
||||
save_divider(|| rom_data::double_funcs::ddiv(self, b))
|
||||
}
|
||||
}
|
||||
|
||||
fn div<F: Float + ROMDiv>(a: F, b: F) -> F {
|
||||
if a.is_not_finite() {
|
||||
if b.is_not_finite() {
|
||||
// inf/NaN / inf/NaN = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
if b.is_zero() {
|
||||
// inf/NaN / 0 = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
return if b.is_sign_negative() {
|
||||
// [+/-]inf/NaN / (-X) = [-/+]inf/NaN
|
||||
a.negate()
|
||||
} else {
|
||||
// [-]inf/NaN / X = [-]inf/NaN
|
||||
a
|
||||
};
|
||||
}
|
||||
|
||||
if b.is_nan() {
|
||||
// X / NaN = NaN
|
||||
return b;
|
||||
}
|
||||
|
||||
// ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only
|
||||
// need to catch 0 / 0
|
||||
if b.is_zero() && a.is_zero() {
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
a.rom_div(b)
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[alias = __divsf3vfp]
|
||||
#[aeabi = __aeabi_fdiv]
|
||||
extern "C" fn __divsf3(a: f32, b: f32) -> f32 {
|
||||
div(a, b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __divdf3vfp]
|
||||
#[aeabi = __aeabi_ddiv]
|
||||
extern "C" fn __divdf3(a: f64, b: f64) -> f64 {
|
||||
div(a, b)
|
||||
}
|
||||
}
|
239
embassy-rp/src/float/functions.rs
Normal file
239
embassy-rp/src/float/functions.rs
Normal file
@ -0,0 +1,239 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs
|
||||
|
||||
use crate::float::{Float, Int};
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMFunctions {
|
||||
fn sqrt(self) -> Self;
|
||||
fn ln(self) -> Self;
|
||||
fn exp(self) -> Self;
|
||||
fn sin(self) -> Self;
|
||||
fn cos(self) -> Self;
|
||||
fn tan(self) -> Self;
|
||||
fn atan2(self, y: Self) -> Self;
|
||||
|
||||
fn to_trig_range(self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMFunctions for f32 {
|
||||
fn sqrt(self) -> Self {
|
||||
rom_data::float_funcs::fsqrt(self)
|
||||
}
|
||||
|
||||
fn ln(self) -> Self {
|
||||
rom_data::float_funcs::fln(self)
|
||||
}
|
||||
|
||||
fn exp(self) -> Self {
|
||||
rom_data::float_funcs::fexp(self)
|
||||
}
|
||||
|
||||
fn sin(self) -> Self {
|
||||
rom_data::float_funcs::fsin(self)
|
||||
}
|
||||
|
||||
fn cos(self) -> Self {
|
||||
rom_data::float_funcs::fcos(self)
|
||||
}
|
||||
|
||||
fn tan(self) -> Self {
|
||||
rom_data::float_funcs::ftan(self)
|
||||
}
|
||||
|
||||
fn atan2(self, y: Self) -> Self {
|
||||
rom_data::float_funcs::fatan2(self, y)
|
||||
}
|
||||
|
||||
fn to_trig_range(self) -> Self {
|
||||
// -128 < X < 128, logic from the Pico SDK
|
||||
let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
|
||||
if exponent < 134 {
|
||||
self
|
||||
} else {
|
||||
self % (core::f32::consts::PI * 2.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMFunctions for f64 {
|
||||
fn sqrt(self) -> Self {
|
||||
rom_data::double_funcs::dsqrt(self)
|
||||
}
|
||||
|
||||
fn ln(self) -> Self {
|
||||
rom_data::double_funcs::dln(self)
|
||||
}
|
||||
|
||||
fn exp(self) -> Self {
|
||||
rom_data::double_funcs::dexp(self)
|
||||
}
|
||||
|
||||
fn sin(self) -> Self {
|
||||
rom_data::double_funcs::dsin(self)
|
||||
}
|
||||
|
||||
fn cos(self) -> Self {
|
||||
rom_data::double_funcs::dcos(self)
|
||||
}
|
||||
fn tan(self) -> Self {
|
||||
rom_data::double_funcs::dtan(self)
|
||||
}
|
||||
|
||||
fn atan2(self, y: Self) -> Self {
|
||||
rom_data::double_funcs::datan2(self, y)
|
||||
}
|
||||
|
||||
fn to_trig_range(self) -> Self {
|
||||
// -1024 < X < 1024, logic from the Pico SDK
|
||||
let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
|
||||
if exponent < 1033 {
|
||||
self
|
||||
} else {
|
||||
self % (core::f64::consts::PI * 2.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative_nonzero_or_nan<F: Float>(f: F) -> bool {
|
||||
let repr = f.repr();
|
||||
if (repr & F::SIGN_MASK) != F::Int::ZERO {
|
||||
// Negative, so anything other than exactly zero
|
||||
return (repr & (!F::SIGN_MASK)) != F::Int::ZERO;
|
||||
}
|
||||
// NaN
|
||||
(repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK
|
||||
}
|
||||
|
||||
fn sqrt<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if is_negative_nonzero_or_nan(f) {
|
||||
F::NAN
|
||||
} else {
|
||||
f.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
fn ln<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if is_negative_nonzero_or_nan(f) {
|
||||
F::NAN
|
||||
} else {
|
||||
f.ln()
|
||||
}
|
||||
}
|
||||
|
||||
fn exp<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_nan() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.exp()
|
||||
}
|
||||
}
|
||||
|
||||
fn sin<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_not_finite() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.to_trig_range().sin()
|
||||
}
|
||||
}
|
||||
|
||||
fn cos<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_not_finite() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.to_trig_range().cos()
|
||||
}
|
||||
}
|
||||
|
||||
fn tan<F: Float + ROMFunctions>(f: F) -> F {
|
||||
if f.is_not_finite() {
|
||||
F::NAN
|
||||
} else {
|
||||
f.to_trig_range().tan()
|
||||
}
|
||||
}
|
||||
|
||||
fn atan2<F: Float + ROMFunctions>(x: F, y: F) -> F {
|
||||
if x.is_nan() || y.is_nan() {
|
||||
F::NAN
|
||||
} else {
|
||||
x.to_trig_range().atan2(y)
|
||||
}
|
||||
}
|
||||
|
||||
// Name collisions
|
||||
mod intrinsics {
|
||||
intrinsics! {
|
||||
extern "C" fn sqrtf(f: f32) -> f32 {
|
||||
super::sqrt(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "C" fn sqrt(f: f64) -> f64 {
|
||||
super::sqrt(f)
|
||||
}
|
||||
|
||||
extern "C" fn logf(f: f32) -> f32 {
|
||||
super::ln(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "C" fn log(f: f64) -> f64 {
|
||||
super::ln(f)
|
||||
}
|
||||
|
||||
extern "C" fn expf(f: f32) -> f32 {
|
||||
super::exp(f)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
extern "C" fn exp(f: f64) -> f64 {
|
||||
super::exp(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
extern "C" fn sinf(f: f32) -> f32 {
|
||||
super::sin(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn sin(f: f64) -> f64 {
|
||||
super::sin(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
extern "C" fn cosf(f: f32) -> f32 {
|
||||
super::cos(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn cos(f: f64) -> f64 {
|
||||
super::cos(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
extern "C" fn tanf(f: f32) -> f32 {
|
||||
super::tan(f)
|
||||
}
|
||||
|
||||
#[slower_than_default]
|
||||
#[bootrom_v2]
|
||||
extern "C" fn tan(f: f64) -> f64 {
|
||||
super::tan(f)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
extern "C" fn atan2f(a: f32, b: f32) -> f32 {
|
||||
super::atan2(a, b)
|
||||
}
|
||||
|
||||
// Questionable gain
|
||||
#[bootrom_v2]
|
||||
extern "C" fn atan2(a: f64, b: f64) -> f64 {
|
||||
super::atan2(a, b)
|
||||
}
|
||||
}
|
||||
}
|
149
embassy-rp/src/float/mod.rs
Normal file
149
embassy-rp/src/float/mod.rs
Normal file
@ -0,0 +1,149 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs
|
||||
|
||||
use core::ops;
|
||||
|
||||
// Borrowed and simplified from compiler-builtins so we can use bit ops
|
||||
// on floating point without macro soup.
|
||||
pub(crate) trait Int:
|
||||
Copy
|
||||
+ core::fmt::Debug
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ ops::AddAssign
|
||||
+ ops::SubAssign
|
||||
+ ops::BitAndAssign
|
||||
+ ops::BitOrAssign
|
||||
+ ops::BitXorAssign
|
||||
+ ops::ShlAssign<i32>
|
||||
+ ops::ShrAssign<u32>
|
||||
+ ops::Add<Output = Self>
|
||||
+ ops::Sub<Output = Self>
|
||||
+ ops::Div<Output = Self>
|
||||
+ ops::Shl<u32, Output = Self>
|
||||
+ ops::Shr<u32, Output = Self>
|
||||
+ ops::BitOr<Output = Self>
|
||||
+ ops::BitXor<Output = Self>
|
||||
+ ops::BitAnd<Output = Self>
|
||||
+ ops::Not<Output = Self>
|
||||
{
|
||||
const ZERO: Self;
|
||||
}
|
||||
|
||||
macro_rules! int_impl {
|
||||
($ty:ty) => {
|
||||
impl Int for $ty {
|
||||
const ZERO: Self = 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_impl!(u32);
|
||||
int_impl!(u64);
|
||||
|
||||
pub(crate) trait Float:
|
||||
Copy
|
||||
+ core::fmt::Debug
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ ops::AddAssign
|
||||
+ ops::MulAssign
|
||||
+ ops::Add<Output = Self>
|
||||
+ ops::Sub<Output = Self>
|
||||
+ ops::Div<Output = Self>
|
||||
+ ops::Rem<Output = Self>
|
||||
{
|
||||
/// A uint of the same with as the float
|
||||
type Int: Int;
|
||||
|
||||
/// NaN representation for the float
|
||||
const NAN: Self;
|
||||
|
||||
/// The bitwidth of the float type
|
||||
const BITS: u32;
|
||||
|
||||
/// The bitwidth of the significand
|
||||
const SIGNIFICAND_BITS: u32;
|
||||
|
||||
/// A mask for the sign bit
|
||||
const SIGN_MASK: Self::Int;
|
||||
|
||||
/// A mask for the significand
|
||||
const SIGNIFICAND_MASK: Self::Int;
|
||||
|
||||
/// A mask for the exponent
|
||||
const EXPONENT_MASK: Self::Int;
|
||||
|
||||
/// Returns `self` transmuted to `Self::Int`
|
||||
fn repr(self) -> Self::Int;
|
||||
|
||||
/// Returns a `Self::Int` transmuted back to `Self`
|
||||
fn from_repr(a: Self::Int) -> Self;
|
||||
|
||||
/// Return a sign swapped `self`
|
||||
fn negate(self) -> Self;
|
||||
|
||||
/// Returns true if `self` is either NaN or infinity
|
||||
fn is_not_finite(self) -> bool {
|
||||
(self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK
|
||||
}
|
||||
|
||||
/// Returns true if `self` is infinity
|
||||
fn is_infinity(self) -> bool {
|
||||
(self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK
|
||||
}
|
||||
|
||||
/// Returns true if `self is NaN
|
||||
fn is_nan(self) -> bool {
|
||||
(self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK
|
||||
}
|
||||
|
||||
/// Returns true if `self` is negative
|
||||
fn is_sign_negative(self) -> bool {
|
||||
(self.repr() & Self::SIGN_MASK) != Self::Int::ZERO
|
||||
}
|
||||
|
||||
/// Returns true if `self` is zero (either sign)
|
||||
fn is_zero(self) -> bool {
|
||||
(self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! float_impl {
|
||||
($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {
|
||||
impl Float for $ty {
|
||||
type Int = $ity;
|
||||
|
||||
const NAN: Self = <$ty>::NAN;
|
||||
|
||||
const BITS: u32 = $bits;
|
||||
const SIGNIFICAND_BITS: u32 = $significand_bits;
|
||||
|
||||
const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1);
|
||||
const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1;
|
||||
const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK);
|
||||
|
||||
fn repr(self) -> Self::Int {
|
||||
self.to_bits()
|
||||
}
|
||||
|
||||
fn from_repr(a: Self::Int) -> Self {
|
||||
Self::from_bits(a)
|
||||
}
|
||||
|
||||
fn negate(self) -> Self {
|
||||
-self
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float_impl!(f32, u32, 32, 23);
|
||||
float_impl!(f64, u64, 64, 52);
|
||||
|
||||
mod add_sub;
|
||||
mod cmp;
|
||||
mod conv;
|
||||
mod div;
|
||||
mod functions;
|
||||
mod mul;
|
70
embassy-rp/src/float/mul.rs
Normal file
70
embassy-rp/src/float/mul.rs
Normal file
@ -0,0 +1,70 @@
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs
|
||||
|
||||
use super::Float;
|
||||
use crate::rom_data;
|
||||
|
||||
trait ROMMul {
|
||||
fn rom_mul(self, b: Self) -> Self;
|
||||
}
|
||||
|
||||
impl ROMMul for f32 {
|
||||
fn rom_mul(self, b: Self) -> Self {
|
||||
rom_data::float_funcs::fmul(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl ROMMul for f64 {
|
||||
fn rom_mul(self, b: Self) -> Self {
|
||||
rom_data::double_funcs::dmul(self, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn mul<F: Float + ROMMul>(a: F, b: F) -> F {
|
||||
if a.is_not_finite() {
|
||||
if b.is_zero() {
|
||||
// [-]inf/NaN * 0 = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
return if b.is_sign_negative() {
|
||||
// [+/-]inf/NaN * (-X) = [-/+]inf/NaN
|
||||
a.negate()
|
||||
} else {
|
||||
// [-]inf/NaN * X = [-]inf/NaN
|
||||
a
|
||||
};
|
||||
}
|
||||
|
||||
if b.is_not_finite() {
|
||||
if a.is_zero() {
|
||||
// 0 * [-]inf/NaN = NaN
|
||||
return F::NAN;
|
||||
}
|
||||
|
||||
return if b.is_sign_negative() {
|
||||
// (-X) * [+/-]inf/NaN = [-/+]inf/NaN
|
||||
b.negate()
|
||||
} else {
|
||||
// X * [-]inf/NaN = [-]inf/NaN
|
||||
b
|
||||
};
|
||||
}
|
||||
|
||||
a.rom_mul(b)
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
#[alias = __mulsf3vfp]
|
||||
#[aeabi = __aeabi_fmul]
|
||||
extern "C" fn __mulsf3(a: f32, b: f32) -> f32 {
|
||||
mul(a, b)
|
||||
}
|
||||
|
||||
#[bootrom_v2]
|
||||
#[alias = __muldf3vfp]
|
||||
#[aeabi = __aeabi_dmul]
|
||||
extern "C" fn __muldf3(a: f64, b: f64) -> f64 {
|
||||
mul(a, b)
|
||||
}
|
||||
}
|
@ -274,3 +274,203 @@ macro_rules! intrinsics {
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/sio.rs
|
||||
|
||||
// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers
|
||||
// by packing it into r0[0:31] and r1[32:63]. So all we need to do is put
|
||||
// the remainder in the high order 32 bits of a 64 bit result. We can also
|
||||
// alias the division operators to these for a similar reason r0 is the
|
||||
// result either way and r1 a scratch register, so the caller can't assume it
|
||||
// retains the argument value.
|
||||
#[cfg(target_arch = "arm")]
|
||||
core::arch::global_asm!(
|
||||
".macro hwdivider_head",
|
||||
"ldr r2, =(0xd0000000)", // SIO_BASE
|
||||
// Check the DIRTY state of the divider by shifting it into the C
|
||||
// status bit.
|
||||
"ldr r3, [r2, #0x078]", // DIV_CSR
|
||||
"lsrs r3, #2", // DIRTY = 1, so shift 2 down
|
||||
// We only need to save the state when DIRTY, otherwise we can just do the
|
||||
// division directly.
|
||||
"bcs 2f",
|
||||
"1:",
|
||||
// Do the actual division now, we're either not DIRTY, or we've saved the
|
||||
// state and branched back here so it's safe now.
|
||||
".endm",
|
||||
".macro hwdivider_tail",
|
||||
// 8 cycle delay to wait for the result. Each branch takes two cycles
|
||||
// and fits into a 2-byte Thumb instruction, so this is smaller than
|
||||
// 8 NOPs.
|
||||
"b 3f",
|
||||
"3: b 3f",
|
||||
"3: b 3f",
|
||||
"3: b 3f",
|
||||
"3:",
|
||||
// Read the quotient last, since that's what clears the dirty flag.
|
||||
"ldr r1, [r2, #0x074]", // DIV_REMAINDER
|
||||
"ldr r0, [r2, #0x070]", // DIV_QUOTIENT
|
||||
// Either return to the caller or back to the state restore.
|
||||
"bx lr",
|
||||
"2:",
|
||||
// Since we can't save the signed-ness of the calculation, we have to make
|
||||
// sure that there's at least an 8 cycle delay before we read the result.
|
||||
// The push takes 5 cycles, and we've already spent at least 7 checking
|
||||
// the DIRTY state to get here.
|
||||
"push {{r4-r6, lr}}",
|
||||
// Read the quotient last, since that's what clears the dirty flag.
|
||||
"ldr r3, [r2, #0x060]", // DIV_UDIVIDEND
|
||||
"ldr r4, [r2, #0x064]", // DIV_UDIVISOR
|
||||
"ldr r5, [r2, #0x074]", // DIV_REMAINDER
|
||||
"ldr r6, [r2, #0x070]", // DIV_QUOTIENT
|
||||
// If we get interrupted here (before a write sets the DIRTY flag) it's
|
||||
// fine, since we have the full state, so the interruptor doesn't have to
|
||||
// restore it. Once the write happens and the DIRTY flag is set, the
|
||||
// interruptor becomes responsible for restoring our state.
|
||||
"bl 1b",
|
||||
// If we are interrupted here, then the interruptor will start an incorrect
|
||||
// calculation using a wrong divisor, but we'll restore the divisor and
|
||||
// result ourselves correctly. This sets DIRTY, so any interruptor will
|
||||
// save the state.
|
||||
"str r3, [r2, #0x060]", // DIV_UDIVIDEND
|
||||
// If we are interrupted here, the the interruptor may start the
|
||||
// calculation using incorrectly signed inputs, but we'll restore the
|
||||
// result ourselves. This sets DIRTY, so any interruptor will save the
|
||||
// state.
|
||||
"str r4, [r2, #0x064]", // DIV_UDIVISOR
|
||||
// If we are interrupted here, the interruptor will have restored
|
||||
// everything but the quotient may be wrongly signed. If the calculation
|
||||
// started by the above writes is still ongoing it is stopped, so it won't
|
||||
// replace the result we're restoring. DIRTY and READY set, but only
|
||||
// DIRTY matters to make the interruptor save the state.
|
||||
"str r5, [r2, #0x074]", // DIV_REMAINDER
|
||||
// State fully restored after the quotient write. This sets both DIRTY
|
||||
// and READY, so whatever we may have interrupted can read the result.
|
||||
"str r6, [r2, #0x070]", // DIV_QUOTIENT
|
||||
"pop {{r4-r6, pc}}",
|
||||
".endm",
|
||||
);
|
||||
|
||||
macro_rules! division_function {
|
||||
(
|
||||
$name:ident $($intrinsic:ident)* ( $argty:ty ) {
|
||||
$($begin:literal),+
|
||||
}
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
core::arch::global_asm!(
|
||||
// Mangle the name slightly, since this is a global symbol.
|
||||
concat!(".section .text._erphal_", stringify!($name)),
|
||||
concat!(".global _erphal_", stringify!($name)),
|
||||
concat!(".type _erphal_", stringify!($name), ", %function"),
|
||||
".align 2",
|
||||
concat!("_erphal_", stringify!($name), ":"),
|
||||
$(
|
||||
concat!(".global ", stringify!($intrinsic)),
|
||||
concat!(".type ", stringify!($intrinsic), ", %function"),
|
||||
concat!(stringify!($intrinsic), ":"),
|
||||
)*
|
||||
|
||||
"hwdivider_head",
|
||||
$($begin),+ ,
|
||||
"hwdivider_tail",
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "arm", not(feature = "intrinsics")))]
|
||||
core::arch::global_asm!(
|
||||
// Mangle the name slightly, since this is a global symbol.
|
||||
concat!(".section .text._erphal_", stringify!($name)),
|
||||
concat!(".global _erphal_", stringify!($name)),
|
||||
concat!(".type _erphal_", stringify!($name), ", %function"),
|
||||
".align 2",
|
||||
concat!("_erphal_", stringify!($name), ":"),
|
||||
|
||||
"hwdivider_head",
|
||||
$($begin),+ ,
|
||||
"hwdivider_tail",
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
extern "aapcs" {
|
||||
// Connect a local name to global symbol above through FFI.
|
||||
#[link_name = concat!("_erphal_", stringify!($name)) ]
|
||||
fn $name(n: $argty, d: $argty) -> u64;
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "arm"))]
|
||||
#[allow(unused_variables)]
|
||||
unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }
|
||||
};
|
||||
}
|
||||
|
||||
division_function! {
|
||||
unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {
|
||||
"str r0, [r2, #0x060]", // DIV_UDIVIDEND
|
||||
"str r1, [r2, #0x064]" // DIV_UDIVISOR
|
||||
}
|
||||
}
|
||||
|
||||
division_function! {
|
||||
signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {
|
||||
"str r0, [r2, #0x068]", // DIV_SDIVIDEND
|
||||
"str r1, [r2, #0x06c]" // DIV_SDIVISOR
|
||||
}
|
||||
}
|
||||
|
||||
fn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {
|
||||
let packed = unsafe { unsigned_divmod(n, d) };
|
||||
DivResult {
|
||||
quotient: packed as u32,
|
||||
remainder: (packed >> 32) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
fn divider_signed(n: i32, d: i32) -> DivResult<i32> {
|
||||
let packed = unsafe { signed_divmod(n, d) };
|
||||
// Double casts to avoid sign extension
|
||||
DivResult {
|
||||
quotient: packed as u32 as i32,
|
||||
remainder: (packed >> 32) as u32 as i32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of divide/modulo operation
|
||||
struct DivResult<T> {
|
||||
/// The quotient of divide/modulo operation
|
||||
pub quotient: T,
|
||||
/// The remainder of divide/modulo operation
|
||||
pub remainder: T,
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
|
||||
divider_unsigned(n, d).quotient
|
||||
}
|
||||
|
||||
extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
|
||||
divider_unsigned(n, d).remainder
|
||||
}
|
||||
|
||||
extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
|
||||
let quo_rem = divider_unsigned(n, d);
|
||||
if let Some(rem) = rem {
|
||||
*rem = quo_rem.remainder;
|
||||
}
|
||||
quo_rem.quotient
|
||||
}
|
||||
|
||||
extern "C" fn __divsi3(n: i32, d: i32) -> i32 {
|
||||
divider_signed(n, d).quotient
|
||||
}
|
||||
|
||||
extern "C" fn __modsi3(n: i32, d: i32) -> i32 {
|
||||
divider_signed(n, d).remainder
|
||||
}
|
||||
|
||||
extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {
|
||||
let quo_rem = divider_signed(n, d);
|
||||
*rem = quo_rem.remainder;
|
||||
quo_rem.quotient
|
||||
}
|
||||
}
|
||||
|
@ -11,18 +11,16 @@ mod critical_section_impl;
|
||||
mod intrinsics;
|
||||
|
||||
pub mod adc;
|
||||
pub mod clocks;
|
||||
pub mod dma;
|
||||
pub mod flash;
|
||||
mod float;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod interrupt;
|
||||
|
||||
#[cfg(feature = "pio")]
|
||||
pub mod pio;
|
||||
#[cfg(feature = "pio")]
|
||||
pub mod pio_instr_util;
|
||||
#[cfg(feature = "pio")]
|
||||
pub mod relocate;
|
||||
|
||||
pub mod multicore;
|
||||
pub mod pwm;
|
||||
mod reset;
|
||||
pub mod rom_data;
|
||||
pub mod rtc;
|
||||
pub mod spi;
|
||||
@ -31,22 +29,22 @@ pub mod timer;
|
||||
pub mod uart;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub mod usb;
|
||||
|
||||
pub mod clocks;
|
||||
pub mod flash;
|
||||
pub mod multicore;
|
||||
mod reset;
|
||||
pub mod watchdog;
|
||||
|
||||
// Reexports
|
||||
// PIO
|
||||
// TODO: move `pio_instr_util` and `relocate` to inside `pio`
|
||||
pub mod pio;
|
||||
pub mod pio_instr_util;
|
||||
pub mod relocate;
|
||||
|
||||
// Reexports
|
||||
pub use embassy_cortex_m::executor;
|
||||
pub use embassy_cortex_m::interrupt::_export::interrupt;
|
||||
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
#[cfg(feature = "unstable-pac")]
|
||||
pub use rp2040_pac2 as pac;
|
||||
pub use rp_pac as pac;
|
||||
#[cfg(not(feature = "unstable-pac"))]
|
||||
pub(crate) use rp2040_pac2 as pac;
|
||||
pub(crate) use rp_pac as pac;
|
||||
|
||||
embassy_hal_common::peripherals! {
|
||||
PIN_0,
|
||||
@ -108,6 +106,15 @@ embassy_hal_common::peripherals! {
|
||||
DMA_CH10,
|
||||
DMA_CH11,
|
||||
|
||||
PWM_CH0,
|
||||
PWM_CH1,
|
||||
PWM_CH2,
|
||||
PWM_CH3,
|
||||
PWM_CH4,
|
||||
PWM_CH5,
|
||||
PWM_CH6,
|
||||
PWM_CH7,
|
||||
|
||||
USB,
|
||||
|
||||
RTC,
|
||||
|
338
embassy-rp/src/pwm.rs
Normal file
338
embassy-rp/src/pwm.rs
Normal file
@ -0,0 +1,338 @@
|
||||
//! Pulse Width Modulation (PWM)
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
use fixed::traits::ToFixed;
|
||||
use fixed::FixedU16;
|
||||
use pac::pwm::regs::{ChDiv, Intr};
|
||||
use pac::pwm::vals::Divmode;
|
||||
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
use crate::{pac, peripherals, RegExt};
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone)]
|
||||
pub struct Config {
|
||||
pub invert_a: bool,
|
||||
pub invert_b: bool,
|
||||
pub phase_correct: bool,
|
||||
pub enable: bool,
|
||||
pub divider: fixed::FixedU16<fixed::types::extra::U4>,
|
||||
pub compare_a: u16,
|
||||
pub compare_b: u16,
|
||||
pub top: u16,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
invert_a: false,
|
||||
invert_b: false,
|
||||
phase_correct: false,
|
||||
enable: true, // differs from reset value
|
||||
divider: 1.to_fixed(),
|
||||
compare_a: 0,
|
||||
compare_b: 0,
|
||||
top: 0xffff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InputMode {
|
||||
Level,
|
||||
RisingEdge,
|
||||
FallingEdge,
|
||||
}
|
||||
|
||||
impl From<InputMode> for Divmode {
|
||||
fn from(value: InputMode) -> Self {
|
||||
match value {
|
||||
InputMode::Level => Divmode::LEVEL,
|
||||
InputMode::RisingEdge => Divmode::RISE,
|
||||
InputMode::FallingEdge => Divmode::FALL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pwm<'d, T: Channel> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
pin_a: Option<PeripheralRef<'d, AnyPin>>,
|
||||
pin_b: Option<PeripheralRef<'d, AnyPin>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Channel> Pwm<'d, T> {
|
||||
fn new_inner(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: Option<PeripheralRef<'d, AnyPin>>,
|
||||
b: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
divmode: Divmode,
|
||||
) -> Self {
|
||||
into_ref!(inner);
|
||||
|
||||
let p = inner.regs();
|
||||
unsafe {
|
||||
p.csr().modify(|w| {
|
||||
w.set_divmode(divmode);
|
||||
w.set_en(false);
|
||||
});
|
||||
p.ctr().write(|w| w.0 = 0);
|
||||
Self::configure(p, &config);
|
||||
|
||||
if let Some(pin) = &a {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(4));
|
||||
}
|
||||
if let Some(pin) = &b {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(4));
|
||||
}
|
||||
}
|
||||
Self {
|
||||
inner,
|
||||
pin_a: a.into(),
|
||||
pin_b: b.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_free(inner: impl Peripheral<P = T> + 'd, config: Config) -> Self {
|
||||
Self::new_inner(inner, None, None, config, Divmode::DIV)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_output_a(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(a);
|
||||
Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_output_b(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(b);
|
||||
Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_output_ab(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(a, b);
|
||||
Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_input(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
mode: InputMode,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(b);
|
||||
Self::new_inner(inner, None, Some(b.map_into()), config, mode.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_output_input(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
mode: InputMode,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(a, b);
|
||||
Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into())
|
||||
}
|
||||
|
||||
fn configure(p: pac::pwm::Channel, config: &Config) {
|
||||
if config.divider > FixedU16::<fixed::types::extra::U4>::from_bits(0xFF_F) {
|
||||
panic!("Requested divider is too large");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
p.div().write_value(ChDiv(config.divider.to_bits() as u32));
|
||||
p.cc().write(|w| {
|
||||
w.set_a(config.compare_a);
|
||||
w.set_b(config.compare_b);
|
||||
});
|
||||
p.top().write(|w| w.set_top(config.top));
|
||||
p.csr().modify(|w| {
|
||||
w.set_a_inv(config.invert_a);
|
||||
w.set_b_inv(config.invert_b);
|
||||
w.set_ph_correct(config.phase_correct);
|
||||
w.set_en(config.enable);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn phase_advance(&mut self) {
|
||||
let p = self.inner.regs();
|
||||
p.csr().write_set(|w| w.set_ph_adv(true));
|
||||
while p.csr().read().ph_adv() {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn phase_retard(&mut self) {
|
||||
let p = self.inner.regs();
|
||||
p.csr().write_set(|w| w.set_ph_ret(true));
|
||||
while p.csr().read().ph_ret() {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn counter(&self) -> u16 {
|
||||
unsafe { self.inner.regs().ctr().read().ctr() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_counter(&self, ctr: u16) {
|
||||
unsafe { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wait_for_wrap(&mut self) {
|
||||
while !self.wrapped() {}
|
||||
self.clear_wrapped();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wrapped(&mut self) -> bool {
|
||||
unsafe { pac::PWM.intr().read().0 & self.bit() != 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_wrapped(&mut self) {
|
||||
unsafe {
|
||||
pac::PWM.intr().write_value(Intr(self.bit() as _));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bit(&self) -> u32 {
|
||||
1 << self.inner.number() as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PwmBatch(u32);
|
||||
|
||||
impl PwmBatch {
|
||||
#[inline]
|
||||
pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) {
|
||||
self.0 |= pwm.bit();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) {
|
||||
let mut en = PwmBatch(0);
|
||||
batch(&mut en);
|
||||
unsafe {
|
||||
if enabled {
|
||||
pac::PWM.en().write_set(|w| w.0 = en.0);
|
||||
} else {
|
||||
pac::PWM.en().write_clear(|w| w.0 = en.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Channel> Drop for Pwm<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.inner.regs().csr().write_clear(|w| w.set_en(false));
|
||||
if let Some(pin) = &self.pin_a {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(31));
|
||||
}
|
||||
if let Some(pin) = &self.pin_b {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(31));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Channel {}
|
||||
}
|
||||
|
||||
pub trait Channel: Peripheral<P = Self> + sealed::Channel + Sized + 'static {
|
||||
fn number(&self) -> u8;
|
||||
|
||||
fn regs(&self) -> pac::pwm::Channel {
|
||||
pac::PWM.ch(self.number() as _)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! channel {
|
||||
($name:ident, $num:expr) => {
|
||||
impl sealed::Channel for peripherals::$name {}
|
||||
impl Channel for peripherals::$name {
|
||||
fn number(&self) -> u8 {
|
||||
$num
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
channel!(PWM_CH0, 0);
|
||||
channel!(PWM_CH1, 1);
|
||||
channel!(PWM_CH2, 2);
|
||||
channel!(PWM_CH3, 3);
|
||||
channel!(PWM_CH4, 4);
|
||||
channel!(PWM_CH5, 5);
|
||||
channel!(PWM_CH6, 6);
|
||||
channel!(PWM_CH7, 7);
|
||||
|
||||
pub trait PwmPinA<T: Channel>: GpioPin {}
|
||||
pub trait PwmPinB<T: Channel>: GpioPin {}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($pin:ident, $channel:ident, $kind:ident) => {
|
||||
impl $kind<peripherals::$channel> for peripherals::$pin {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pin!(PIN_0, PWM_CH0, PwmPinA);
|
||||
impl_pin!(PIN_1, PWM_CH0, PwmPinB);
|
||||
impl_pin!(PIN_2, PWM_CH1, PwmPinA);
|
||||
impl_pin!(PIN_3, PWM_CH1, PwmPinB);
|
||||
impl_pin!(PIN_4, PWM_CH2, PwmPinA);
|
||||
impl_pin!(PIN_5, PWM_CH2, PwmPinB);
|
||||
impl_pin!(PIN_6, PWM_CH3, PwmPinA);
|
||||
impl_pin!(PIN_7, PWM_CH3, PwmPinB);
|
||||
impl_pin!(PIN_8, PWM_CH4, PwmPinA);
|
||||
impl_pin!(PIN_9, PWM_CH4, PwmPinB);
|
||||
impl_pin!(PIN_10, PWM_CH5, PwmPinA);
|
||||
impl_pin!(PIN_11, PWM_CH5, PwmPinB);
|
||||
impl_pin!(PIN_12, PWM_CH6, PwmPinA);
|
||||
impl_pin!(PIN_13, PWM_CH6, PwmPinB);
|
||||
impl_pin!(PIN_14, PWM_CH7, PwmPinA);
|
||||
impl_pin!(PIN_15, PWM_CH7, PwmPinB);
|
||||
impl_pin!(PIN_16, PWM_CH0, PwmPinA);
|
||||
impl_pin!(PIN_17, PWM_CH0, PwmPinB);
|
||||
impl_pin!(PIN_18, PWM_CH1, PwmPinA);
|
||||
impl_pin!(PIN_19, PWM_CH1, PwmPinB);
|
||||
impl_pin!(PIN_20, PWM_CH2, PwmPinA);
|
||||
impl_pin!(PIN_21, PWM_CH2, PwmPinB);
|
||||
impl_pin!(PIN_22, PWM_CH3, PwmPinA);
|
||||
impl_pin!(PIN_23, PWM_CH3, PwmPinB);
|
||||
impl_pin!(PIN_24, PWM_CH4, PwmPinA);
|
||||
impl_pin!(PIN_25, PWM_CH4, PwmPinB);
|
||||
impl_pin!(PIN_26, PWM_CH5, PwmPinA);
|
||||
impl_pin!(PIN_27, PWM_CH5, PwmPinB);
|
||||
impl_pin!(PIN_28, PWM_CH6, PwmPinA);
|
||||
impl_pin!(PIN_29, PWM_CH6, PwmPinB);
|
||||
|
||||
impl<'d, T: Channel> SetConfig for Pwm<'d, T> {
|
||||
type Config = Config;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
Self::configure(self.inner.regs(), config);
|
||||
}
|
||||
}
|
@ -56,50 +56,11 @@ macro_rules! declare_rom_function {
|
||||
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
declare_rom_function!{
|
||||
__internal ,
|
||||
$(#[$outer])*
|
||||
fn $name( $($argname: $ty),* ) -> $ret
|
||||
$lookup
|
||||
}
|
||||
};
|
||||
|
||||
@ -107,6 +68,21 @@ macro_rules! declare_rom_function {
|
||||
$(#[$outer:meta])*
|
||||
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
declare_rom_function!{
|
||||
__internal unsafe ,
|
||||
$(#[$outer])*
|
||||
fn $name( $($argname: $ty),* ) -> $ret
|
||||
$lookup
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
__internal
|
||||
$( $maybe_unsafe:ident )? ,
|
||||
$(#[$outer:meta])*
|
||||
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
@ -114,43 +90,58 @@ macro_rules! declare_rom_function {
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret
|
||||
= core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
// unlike rp2040-hal we store a full word, containing the full function pointer.
|
||||
// rp2040-hal saves two bytes by storing only the rom offset, at the cost of
|
||||
// having to do an indirection and an atomic operation on every rom call.
|
||||
static mut CACHE: $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret
|
||||
= trampoline;
|
||||
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
$( $maybe_unsafe )? extern "C" fn trampoline( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
let p: *const u32 = $lookup;
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
CACHE = core::mem::transmute(p);
|
||||
compiler_fence(Ordering::Release);
|
||||
CACHE($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
//
|
||||
// We easily get away with using only compiler fences here
|
||||
// because RP2040 SRAM is not cached. If it were we'd need
|
||||
// to make sure updates propagate quickly, or just take the
|
||||
// hit and let each core resolve every function once.
|
||||
compiler_fence(Ordering::Acquire);
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
CACHE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
@ -369,6 +360,7 @@ pub fn fplib_start() -> *const u8 {
|
||||
}
|
||||
|
||||
/// See Table 180 in the RP2040 datasheet for the contents of this table.
|
||||
#[cfg_attr(feature = "rom-func-cache", inline(never))]
|
||||
pub fn soft_float_table() -> *const usize {
|
||||
rom_table_lookup(DATA_TABLE, *b"SF")
|
||||
}
|
||||
@ -379,6 +371,7 @@ pub fn fplib_end() -> *const u8 {
|
||||
}
|
||||
|
||||
/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
|
||||
#[cfg_attr(feature = "rom-func-cache", inline(never))]
|
||||
pub fn soft_double_table() -> *const usize {
|
||||
if rom_version_number() < 2 {
|
||||
panic!(
|
||||
|
@ -726,58 +726,3 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
feature = "_todo_embedded_hal_serial"
|
||||
))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
Self::write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
Self::flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
Self::read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUart<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
BufferedUartTx::<'d, T>::write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
BufferedUartTx::<'d, T>::flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUart<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
BufferedUartRx::<'d, T>::read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use crate::dma::{AnyChannel, Channel};
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::pac::io::vals::{Inover, Outover};
|
||||
use crate::{pac, peripherals, Peripheral};
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
@ -53,6 +54,14 @@ pub struct Config {
|
||||
pub data_bits: DataBits,
|
||||
pub stop_bits: StopBits,
|
||||
pub parity: Parity,
|
||||
/// Invert the tx pin output
|
||||
pub invert_tx: bool,
|
||||
/// Invert the rx pin input
|
||||
pub invert_rx: bool,
|
||||
// Invert the rts pin
|
||||
pub invert_rts: bool,
|
||||
// Invert the cts pin
|
||||
pub invert_cts: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -62,6 +71,10 @@ impl Default for Config {
|
||||
data_bits: DataBits::DataBits8,
|
||||
stop_bits: StopBits::STOP1,
|
||||
parity: Parity::ParityNone,
|
||||
invert_rx: false,
|
||||
invert_tx: false,
|
||||
invert_rts: false,
|
||||
invert_cts: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,7 +180,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
|
||||
/// Create a new DMA-enabled UART which can only send data
|
||||
/// Create a new DMA-enabled UART which can only recieve data
|
||||
pub fn new(
|
||||
_uart: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
@ -175,7 +188,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rx, rx_dma);
|
||||
Uart::<T, M>::init(Some(rx.map_into()), None, None, None, config);
|
||||
Uart::<T, M>::init(None, Some(rx.map_into()), None, None, config);
|
||||
Self::new_inner(Some(rx_dma.map_into()))
|
||||
}
|
||||
|
||||
@ -381,19 +394,47 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
if let Some(pin) = &tx {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(2));
|
||||
pin.io().ctrl().write(|w| {
|
||||
w.set_funcsel(2);
|
||||
w.set_outover(if config.invert_tx {
|
||||
Outover::INVERT
|
||||
} else {
|
||||
Outover::NORMAL
|
||||
});
|
||||
});
|
||||
pin.pad_ctrl().write(|w| w.set_ie(true));
|
||||
}
|
||||
if let Some(pin) = &rx {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(2));
|
||||
pin.io().ctrl().write(|w| {
|
||||
w.set_funcsel(2);
|
||||
w.set_inover(if config.invert_rx {
|
||||
Inover::INVERT
|
||||
} else {
|
||||
Inover::NORMAL
|
||||
});
|
||||
});
|
||||
pin.pad_ctrl().write(|w| w.set_ie(true));
|
||||
}
|
||||
if let Some(pin) = &cts {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(2));
|
||||
pin.io().ctrl().write(|w| {
|
||||
w.set_funcsel(2);
|
||||
w.set_inover(if config.invert_cts {
|
||||
Inover::INVERT
|
||||
} else {
|
||||
Inover::NORMAL
|
||||
});
|
||||
});
|
||||
pin.pad_ctrl().write(|w| w.set_ie(true));
|
||||
}
|
||||
if let Some(pin) = &rts {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(2));
|
||||
pin.io().ctrl().write(|w| {
|
||||
w.set_funcsel(2);
|
||||
w.set_outover(if config.invert_rts {
|
||||
Outover::INVERT
|
||||
} else {
|
||||
Outover::NORMAL
|
||||
});
|
||||
});
|
||||
pin.pad_ctrl().write(|w| w.set_ie(true));
|
||||
}
|
||||
|
||||
@ -651,61 +692,6 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
feature = "_todo_embedded_hal_serial"
|
||||
))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
|
@ -231,7 +231,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
let len = (max_packet_size + 63) / 64 * 64;
|
||||
|
||||
let addr = self.ep_mem_free;
|
||||
if addr + len > EP_MEMORY_SIZE as _ {
|
||||
if addr + len > EP_MEMORY_SIZE as u16 {
|
||||
warn!("Endpoint memory full");
|
||||
return Err(EndpointAllocError);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ flavors = [
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
|
||||
embassy-executor = { version = "0.2.0", path = "../embassy-executor" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]}
|
||||
@ -55,7 +55,7 @@ cortex-m = "0.7.6"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true }
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||
critical-section = "1.1"
|
||||
atomic-polyfill = "1.0.1"
|
||||
stm32-metapac = "6"
|
||||
@ -66,6 +66,7 @@ stm32-fmc = "0.2.4"
|
||||
seq-macro = "0.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
|
||||
chrono = { version = "^0.4", default-features = false, optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
critical-section = { version = "1.1", features = ["std"] }
|
||||
|
@ -81,11 +81,74 @@ fn main() {
|
||||
singletons.push(c.name.to_string());
|
||||
}
|
||||
|
||||
// ========
|
||||
// Handle time-driver-XXXX features.
|
||||
|
||||
let time_driver = match env::vars()
|
||||
.map(|(a, _)| a)
|
||||
.filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
|
||||
.get_one()
|
||||
{
|
||||
Ok(x) => Some(
|
||||
x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
|
||||
.unwrap()
|
||||
.to_ascii_lowercase(),
|
||||
),
|
||||
Err(GetOneError::None) => None,
|
||||
Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
|
||||
};
|
||||
|
||||
let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
|
||||
None => "",
|
||||
Some("tim2") => "TIM2",
|
||||
Some("tim3") => "TIM3",
|
||||
Some("tim4") => "TIM4",
|
||||
Some("tim5") => "TIM5",
|
||||
Some("tim12") => "TIM12",
|
||||
Some("tim15") => "TIM15",
|
||||
Some("any") => {
|
||||
if singletons.contains(&"TIM2".to_string()) {
|
||||
"TIM2"
|
||||
} else if singletons.contains(&"TIM3".to_string()) {
|
||||
"TIM3"
|
||||
} else if singletons.contains(&"TIM4".to_string()) {
|
||||
"TIM4"
|
||||
} else if singletons.contains(&"TIM5".to_string()) {
|
||||
"TIM5"
|
||||
} else if singletons.contains(&"TIM12".to_string()) {
|
||||
"TIM12"
|
||||
} else if singletons.contains(&"TIM15".to_string()) {
|
||||
"TIM15"
|
||||
} else {
|
||||
panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
|
||||
}
|
||||
}
|
||||
_ => panic!("unknown time_driver {:?}", time_driver),
|
||||
};
|
||||
|
||||
if time_driver_singleton != "" {
|
||||
println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase());
|
||||
}
|
||||
|
||||
// ========
|
||||
// Write singletons
|
||||
|
||||
let mut g = TokenStream::new();
|
||||
|
||||
let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
|
||||
|
||||
g.extend(quote! {
|
||||
embassy_hal_common::peripherals!(#(#singleton_tokens),*);
|
||||
embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*);
|
||||
});
|
||||
|
||||
let singleton_tokens: Vec<_> = singletons
|
||||
.iter()
|
||||
.filter(|s| *s != &time_driver_singleton.to_string())
|
||||
.map(|s| format_ident!("{}", s))
|
||||
.collect();
|
||||
|
||||
g.extend(quote! {
|
||||
embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*);
|
||||
});
|
||||
|
||||
// ========
|
||||
@ -192,12 +255,20 @@ fn main() {
|
||||
];
|
||||
});
|
||||
|
||||
let max_erase_size = flash_memory_regions
|
||||
.iter()
|
||||
.map(|region| region.settings.as_ref().unwrap().erase_size)
|
||||
.max()
|
||||
.unwrap();
|
||||
|
||||
g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; });
|
||||
|
||||
g.extend(quote! { pub mod flash_regions { #flash_regions } });
|
||||
|
||||
// ========
|
||||
// Generate DMA IRQs.
|
||||
|
||||
let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new();
|
||||
let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new();
|
||||
|
||||
for p in METADATA.peripherals {
|
||||
if let Some(r) = &p.registers {
|
||||
@ -207,7 +278,10 @@ fn main() {
|
||||
continue;
|
||||
}
|
||||
for irq in p.interrupts {
|
||||
dma_irqs.entry(irq.interrupt).or_default().push((p.name, irq.signal));
|
||||
dma_irqs
|
||||
.entry(irq.interrupt)
|
||||
.or_default()
|
||||
.push((r.kind, p.name, irq.signal));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,13 +290,14 @@ fn main() {
|
||||
for (irq, channels) in dma_irqs {
|
||||
let irq = format_ident!("{}", irq);
|
||||
|
||||
let channels = channels.iter().map(|(dma, ch)| format_ident!("{}_{}", dma, ch));
|
||||
let xdma = format_ident!("{}", channels[0].0);
|
||||
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
|
||||
|
||||
g.extend(quote! {
|
||||
#[crate::interrupt]
|
||||
unsafe fn #irq () {
|
||||
#(
|
||||
<crate::peripherals::#channels as crate::dma::sealed::Channel>::on_irq();
|
||||
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
|
||||
)*
|
||||
}
|
||||
});
|
||||
@ -838,51 +913,6 @@ fn main() {
|
||||
println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
|
||||
println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
|
||||
|
||||
// ========
|
||||
// Handle time-driver-XXXX features.
|
||||
|
||||
let time_driver = match env::vars()
|
||||
.map(|(a, _)| a)
|
||||
.filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
|
||||
.get_one()
|
||||
{
|
||||
Ok(x) => Some(
|
||||
x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
|
||||
.unwrap()
|
||||
.to_ascii_lowercase(),
|
||||
),
|
||||
Err(GetOneError::None) => None,
|
||||
Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
|
||||
};
|
||||
|
||||
match time_driver.as_ref().map(|x| x.as_ref()) {
|
||||
None => {}
|
||||
Some("tim2") => println!("cargo:rustc-cfg=time_driver_tim2"),
|
||||
Some("tim3") => println!("cargo:rustc-cfg=time_driver_tim3"),
|
||||
Some("tim4") => println!("cargo:rustc-cfg=time_driver_tim4"),
|
||||
Some("tim5") => println!("cargo:rustc-cfg=time_driver_tim5"),
|
||||
Some("tim12") => println!("cargo:rustc-cfg=time_driver_tim12"),
|
||||
Some("tim15") => println!("cargo:rustc-cfg=time_driver_tim15"),
|
||||
Some("any") => {
|
||||
if singletons.contains(&"TIM2".to_string()) {
|
||||
println!("cargo:rustc-cfg=time_driver_tim2");
|
||||
} else if singletons.contains(&"TIM3".to_string()) {
|
||||
println!("cargo:rustc-cfg=time_driver_tim3");
|
||||
} else if singletons.contains(&"TIM4".to_string()) {
|
||||
println!("cargo:rustc-cfg=time_driver_tim4");
|
||||
} else if singletons.contains(&"TIM5".to_string()) {
|
||||
println!("cargo:rustc-cfg=time_driver_tim5");
|
||||
} else if singletons.contains(&"TIM12".to_string()) {
|
||||
println!("cargo:rustc-cfg=time_driver_tim12");
|
||||
} else if singletons.contains(&"TIM15".to_string()) {
|
||||
println!("cargo:rustc-cfg=time_driver_tim15");
|
||||
} else {
|
||||
panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
|
||||
}
|
||||
}
|
||||
_ => panic!("unknown time_driver {:?}", time_driver),
|
||||
}
|
||||
|
||||
// Handle time-driver-XXXX features.
|
||||
if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
|
||||
println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);
|
||||
|
@ -4,6 +4,7 @@ use core::task::Poll;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
@ -385,14 +386,11 @@ where
|
||||
return self.capture_giant(buffer).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
|
||||
let channel = &mut self.dma;
|
||||
let request = channel.request();
|
||||
|
||||
let r = self.inner.regs();
|
||||
let src = r.dr().ptr() as *mut u32;
|
||||
let dma_read = crate::dma::read(channel, request, src, buffer);
|
||||
let request = self.dma.request();
|
||||
let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) };
|
||||
|
||||
Self::clear_interrupt_flags();
|
||||
Self::enable_irqs();
|
||||
@ -436,6 +434,12 @@ where
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(not(dma))]
|
||||
async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> {
|
||||
panic!("capturing to buffers larger than 0xffff is only supported on DMA for now, not on BDMA or GPDMA.");
|
||||
}
|
||||
|
||||
#[cfg(dma)]
|
||||
async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
|
||||
use crate::dma::TransferOptions;
|
||||
|
||||
@ -460,16 +464,24 @@ where
|
||||
let r = self.inner.regs();
|
||||
let src = r.dr().ptr() as *mut u32;
|
||||
|
||||
unsafe {
|
||||
channel.start_double_buffered_read(request, src, m0ar, m1ar, chunk_size, TransferOptions::default());
|
||||
}
|
||||
let mut transfer = unsafe {
|
||||
crate::dma::DoubleBuffered::new_read(
|
||||
&mut self.dma,
|
||||
request,
|
||||
src,
|
||||
m0ar,
|
||||
m1ar,
|
||||
chunk_size,
|
||||
TransferOptions::default(),
|
||||
)
|
||||
};
|
||||
|
||||
let mut last_chunk_set_for_transfer = false;
|
||||
let mut buffer0_last_accessible = false;
|
||||
let dma_result = poll_fn(|cx| {
|
||||
channel.set_waker(cx.waker());
|
||||
transfer.set_waker(cx.waker());
|
||||
|
||||
let buffer0_currently_accessible = unsafe { channel.is_buffer0_accessible() };
|
||||
let buffer0_currently_accessible = transfer.is_buffer0_accessible();
|
||||
|
||||
// check if the accessible buffer changed since last poll
|
||||
if buffer0_last_accessible == buffer0_currently_accessible {
|
||||
@ -480,21 +492,21 @@ where
|
||||
if remaining_chunks != 0 {
|
||||
if remaining_chunks % 2 == 0 && buffer0_currently_accessible {
|
||||
m0ar = unsafe { m0ar.add(2 * chunk_size) };
|
||||
unsafe { channel.set_buffer0(m0ar) }
|
||||
unsafe { transfer.set_buffer0(m0ar) }
|
||||
remaining_chunks -= 1;
|
||||
} else if !buffer0_currently_accessible {
|
||||
m1ar = unsafe { m1ar.add(2 * chunk_size) };
|
||||
unsafe { channel.set_buffer1(m1ar) };
|
||||
unsafe { transfer.set_buffer1(m1ar) };
|
||||
remaining_chunks -= 1;
|
||||
}
|
||||
} else {
|
||||
if buffer0_currently_accessible {
|
||||
unsafe { channel.set_buffer0(buffer.as_mut_ptr()) }
|
||||
unsafe { transfer.set_buffer0(buffer.as_mut_ptr()) }
|
||||
} else {
|
||||
unsafe { channel.set_buffer1(buffer.as_mut_ptr()) }
|
||||
unsafe { transfer.set_buffer1(buffer.as_mut_ptr()) }
|
||||
}
|
||||
if last_chunk_set_for_transfer {
|
||||
channel.request_stop();
|
||||
transfer.request_stop();
|
||||
return Poll::Ready(());
|
||||
}
|
||||
last_chunk_set_for_transfer = true;
|
||||
|
@ -1,18 +1,32 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::{TransferOptions, Word, WordSize};
|
||||
use super::word::{Word, WordSize};
|
||||
use super::Dir;
|
||||
use crate::_generated::BDMA_CHANNEL_COUNT;
|
||||
use crate::dma::Request;
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::pac;
|
||||
use crate::pac::bdma::vals;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub struct TransferOptions {}
|
||||
|
||||
impl Default for TransferOptions {
|
||||
fn default() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WordSize> for vals::Size {
|
||||
fn from(raw: WordSize) -> Self {
|
||||
match raw {
|
||||
@ -23,6 +37,15 @@ impl From<WordSize> for vals::Size {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Dir> for vals::Dir {
|
||||
fn from(raw: Dir) -> Self {
|
||||
match raw {
|
||||
Dir::MemoryToPeripheral => Self::FROMMEMORY,
|
||||
Dir::PeripheralToMemory => Self::FROMPERIPHERAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT],
|
||||
}
|
||||
@ -55,228 +78,267 @@ foreach_dma_channel! {
|
||||
// BDMA1 in H7 doesn't use DMAMUX, which breaks
|
||||
};
|
||||
($channel_peri:ident, $dma_peri:ident, bdma, $channel_num:expr, $index:expr, $dmamux:tt) => {
|
||||
impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {
|
||||
|
||||
unsafe fn start_write<W: Word>(&mut self, _request: Request, buf: *const[W], reg_addr: *mut W, options: TransferOptions) {
|
||||
let (ptr, len) = super::slice_ptr_parts(buf);
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
#[cfg(any(bdma_v2, dmamux))]
|
||||
_request,
|
||||
vals::Dir::FROMMEMORY,
|
||||
reg_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
);
|
||||
impl sealed::Channel for crate::peripherals::$channel_peri {
|
||||
fn regs(&self) -> pac::bdma::Dma {
|
||||
pac::$dma_peri
|
||||
}
|
||||
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
#[cfg(any(bdma_v2, dmamux))]
|
||||
_request,
|
||||
vals::Dir::FROMMEMORY,
|
||||
reg_addr as *const u32,
|
||||
repeated as *mut u32,
|
||||
count,
|
||||
false,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
)
|
||||
fn num(&self) -> usize {
|
||||
$channel_num
|
||||
}
|
||||
|
||||
unsafe fn start_read<W: Word>(&mut self, _request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) {
|
||||
let (ptr, len) = super::slice_ptr_parts_mut(buf);
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
#[cfg(any(bdma_v2, dmamux))]
|
||||
_request,
|
||||
vals::Dir::FROMPERIPHERAL,
|
||||
reg_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
);
|
||||
fn index(&self) -> usize {
|
||||
$index
|
||||
}
|
||||
|
||||
unsafe fn start_double_buffered_read<W: super::Word>(
|
||||
&mut self,
|
||||
_request: Request,
|
||||
_reg_addr: *const W,
|
||||
_buffer0: *mut W,
|
||||
_buffer1: *mut W,
|
||||
_buffer_len: usize,
|
||||
_options: TransferOptions,
|
||||
) {
|
||||
panic!("Unsafe double buffered mode is unavailable on BDMA");
|
||||
}
|
||||
|
||||
unsafe fn set_buffer0<W: super::Word>(&mut self, _buffer: *mut W) {
|
||||
panic!("Unsafe double buffered mode is unavailable on BDMA");
|
||||
}
|
||||
|
||||
unsafe fn set_buffer1<W: super::Word>(&mut self, _buffer: *mut W) {
|
||||
panic!("Unsafe double buffered mode is unavailable on BDMA");
|
||||
}
|
||||
|
||||
unsafe fn is_buffer0_accessible(&mut self) -> bool {
|
||||
panic!("Unsafe double buffered mode is unavailable on BDMA");
|
||||
}
|
||||
|
||||
fn request_stop(&mut self){
|
||||
unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);}
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)}
|
||||
}
|
||||
fn remaining_transfers(&mut self) -> u16 {
|
||||
unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)}
|
||||
}
|
||||
|
||||
fn set_waker(&mut self, waker: &Waker) {
|
||||
unsafe { low_level_api::set_waker($index, waker) }
|
||||
}
|
||||
|
||||
fn on_irq() {
|
||||
unsafe {
|
||||
low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index);
|
||||
}
|
||||
unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::dma::Channel for crate::peripherals::$channel_peri {}
|
||||
impl Channel for crate::peripherals::$channel_peri {}
|
||||
};
|
||||
}
|
||||
|
||||
mod low_level_api {
|
||||
/// Safety: Must be called with a matching set of parameters for a valid dma channel
|
||||
pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index: usize) {
|
||||
let isr = dma.isr().read();
|
||||
let cr = dma.ch(channel_num).cr();
|
||||
|
||||
if isr.teif(channel_num) {
|
||||
panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num);
|
||||
}
|
||||
if isr.tcif(channel_num) && cr.read().tcie() {
|
||||
cr.write(|_| ()); // Disable channel interrupts with the default value.
|
||||
STATE.ch_wakers[index].wake();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(bdma_v2, dmamux))]
|
||||
pub type Request = u8;
|
||||
#[cfg(not(any(bdma_v2, dmamux)))]
|
||||
pub type Request = ();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
||||
#[cfg(not(dmamux))]
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub unsafe fn start_transfer(
|
||||
dma: pac::bdma::Dma,
|
||||
channel_number: u8,
|
||||
#[cfg(any(bdma_v2, dmamux))] request: Request,
|
||||
dir: vals::Dir,
|
||||
pub trait Channel {
|
||||
fn regs(&self) -> pac::bdma::Dma;
|
||||
fn num(&self) -> usize;
|
||||
fn index(&self) -> usize;
|
||||
fn on_irq();
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Transfer<'a, C: Channel> {
|
||||
channel: PeripheralRef<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub unsafe fn new_read<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
peri_addr: *mut W,
|
||||
buf: &'a mut [W],
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
||||
}
|
||||
|
||||
pub unsafe fn new_read_raw<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
peri_addr: *mut W,
|
||||
buf: *mut [W],
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
let (ptr, len) = super::slice_ptr_parts_mut(buf);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::PeripheralToMemory,
|
||||
peri_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
buf: &'a [W],
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write_raw<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
buf: *const [W],
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
let (ptr, len) = super::slice_ptr_parts(buf);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::MemoryToPeripheral,
|
||||
peri_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write_repeated<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
repeated: &'a W,
|
||||
count: usize,
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::MemoryToPeripheral,
|
||||
peri_addr as *const u32,
|
||||
repeated as *const W as *mut u32,
|
||||
count,
|
||||
false,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn new_inner(
|
||||
channel: PeripheralRef<'a, C>,
|
||||
_request: Request,
|
||||
dir: Dir,
|
||||
peri_addr: *const u32,
|
||||
mem_addr: *mut u32,
|
||||
mem_len: usize,
|
||||
incr_mem: bool,
|
||||
data_size: vals::Size,
|
||||
options: TransferOptions,
|
||||
#[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux,
|
||||
#[cfg(dmamux)] dmamux_ch_num: u8,
|
||||
) {
|
||||
assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported");
|
||||
assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported");
|
||||
assert!(
|
||||
options.flow_ctrl == crate::dma::FlowControl::Dma,
|
||||
"Peripheral flow control not supported"
|
||||
);
|
||||
assert!(options.fifo_threshold.is_none(), "FIFO mode not supported");
|
||||
|
||||
let ch = dma.ch(channel_number as _);
|
||||
|
||||
reset_status(dma, channel_number);
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
|
||||
|
||||
#[cfg(bdma_v2)]
|
||||
critical_section::with(|_| dma.cselr().modify(|w| w.set_cs(channel_number as _, request)));
|
||||
data_size: WordSize,
|
||||
_options: TransferOptions,
|
||||
) -> Self {
|
||||
let ch = channel.regs().ch(channel.num());
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
#[cfg(bdma_v2)]
|
||||
critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request)));
|
||||
|
||||
let mut this = Self { channel };
|
||||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
ch.mar().write_value(mem_addr as u32);
|
||||
ch.ndtr().write(|w| w.set_ndt(mem_len as u16));
|
||||
ch.cr().write(|w| {
|
||||
w.set_psize(data_size);
|
||||
w.set_msize(data_size);
|
||||
w.set_psize(data_size.into());
|
||||
w.set_msize(data_size.into());
|
||||
if incr_mem {
|
||||
w.set_minc(vals::Inc::ENABLED);
|
||||
} else {
|
||||
w.set_minc(vals::Inc::DISABLED);
|
||||
}
|
||||
w.set_dir(dir);
|
||||
w.set_dir(dir.into());
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub unsafe fn request_stop(dma: pac::bdma::Dma, channel_number: u8) {
|
||||
reset_status(dma, channel_number);
|
||||
|
||||
let ch = dma.ch(channel_number as _);
|
||||
|
||||
// Disable the channel and interrupts with the default value.
|
||||
ch.cr().write(|_| ());
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
fn clear_irqs(&mut self) {
|
||||
unsafe {
|
||||
self.channel.regs().ifcr().write(|w| {
|
||||
w.set_tcif(self.channel.num(), true);
|
||||
w.set_teif(self.channel.num(), true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn is_running(dma: pac::bdma::Dma, ch: u8) -> bool {
|
||||
let ch = dma.ch(ch as _);
|
||||
ch.cr().read().en()
|
||||
pub fn request_stop(&mut self) {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
|
||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
||||
unsafe {
|
||||
ch.cr().write(|w| {
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
unsafe { ch.cr().read() }.en()
|
||||
}
|
||||
|
||||
/// Gets the total remaining transfers for the channel
|
||||
/// Note: this will be zero for transfers that completed without cancellation.
|
||||
pub unsafe fn get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.ch(ch as _);
|
||||
// read the remaining transfer count. If this is zero, the transfer completed fully.
|
||||
ch.ndtr().read().ndt() as u16
|
||||
pub fn get_remaining_transfers(&self) -> u16 {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
unsafe { ch.ndtr().read() }.ndt()
|
||||
}
|
||||
|
||||
/// Sets the waker for the specified DMA channel
|
||||
pub unsafe fn set_waker(state_number: usize, waker: &Waker) {
|
||||
STATE.ch_wakers[state_number].register(waker);
|
||||
pub fn blocking_wait(mut self) {
|
||||
while self.is_running() {}
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
core::mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) {
|
||||
dma.ifcr().write(|w| {
|
||||
w.set_tcif(channel_number as _, true);
|
||||
w.set_teif(channel_number as _, true);
|
||||
});
|
||||
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||
fn drop(&mut self) {
|
||||
self.request_stop();
|
||||
while self.is_running() {}
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety: Must be called with a matching set of parameters for a valid dma channel
|
||||
pub unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: u8, index: u8) {
|
||||
let channel_num = channel_num as usize;
|
||||
let index = index as usize;
|
||||
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
STATE.ch_wakers[self.channel.index()].register(cx.waker());
|
||||
|
||||
let isr = dma.isr().read();
|
||||
let cr = dma.ch(channel_num).cr();
|
||||
|
||||
if isr.teif(channel_num) {
|
||||
panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num);
|
||||
}
|
||||
if isr.tcif(channel_num) && cr.read().tcie() {
|
||||
cr.write(|_| ()); // Disable channel interrupts with the default value.
|
||||
STATE.ch_wakers[index].wake();
|
||||
if self.is_running() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,46 @@
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomData;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use pac::dma::regs;
|
||||
|
||||
use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize};
|
||||
use super::word::{Word, WordSize};
|
||||
use super::Dir;
|
||||
use crate::_generated::DMA_CHANNEL_COUNT;
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::pac::dma::{regs, vals};
|
||||
use crate::pac::dma::vals;
|
||||
use crate::{interrupt, pac};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub struct TransferOptions {
|
||||
/// Peripheral burst transfer configuration
|
||||
pub pburst: Burst,
|
||||
/// Memory burst transfer configuration
|
||||
pub mburst: Burst,
|
||||
/// Flow control configuration
|
||||
pub flow_ctrl: FlowControl,
|
||||
/// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
|
||||
pub fifo_threshold: Option<FifoThreshold>,
|
||||
}
|
||||
|
||||
impl Default for TransferOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pburst: Burst::Single,
|
||||
mburst: Burst::Single,
|
||||
flow_ctrl: FlowControl::Dma,
|
||||
fifo_threshold: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WordSize> for vals::Size {
|
||||
fn from(raw: WordSize) -> Self {
|
||||
match raw {
|
||||
@ -20,6 +51,28 @@ impl From<WordSize> for vals::Size {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Dir> for vals::Dir {
|
||||
fn from(raw: Dir) -> Self {
|
||||
match raw {
|
||||
Dir::MemoryToPeripheral => Self::MEMORYTOPERIPHERAL,
|
||||
Dir::PeripheralToMemory => Self::PERIPHERALTOMEMORY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Burst {
|
||||
/// Single transfer
|
||||
Single,
|
||||
/// Incremental burst of 4 beats
|
||||
Incr4,
|
||||
/// Incremental burst of 8 beats
|
||||
Incr8,
|
||||
/// Incremental burst of 16 beats
|
||||
Incr16,
|
||||
}
|
||||
|
||||
impl From<Burst> for vals::Burst {
|
||||
fn from(burst: Burst) -> Self {
|
||||
match burst {
|
||||
@ -31,6 +84,15 @@ impl From<Burst> for vals::Burst {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FlowControl {
|
||||
/// Flow control by DMA
|
||||
Dma,
|
||||
/// Flow control by peripheral
|
||||
Peripheral,
|
||||
}
|
||||
|
||||
impl From<FlowControl> for vals::Pfctrl {
|
||||
fn from(flow: FlowControl) -> Self {
|
||||
match flow {
|
||||
@ -40,6 +102,19 @@ impl From<FlowControl> for vals::Pfctrl {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FifoThreshold {
|
||||
/// 1/4 full FIFO
|
||||
Quarter,
|
||||
/// 1/2 full FIFO
|
||||
Half,
|
||||
/// 3/4 full FIFO
|
||||
ThreeQuarters,
|
||||
/// Full FIFO
|
||||
Full,
|
||||
}
|
||||
|
||||
impl From<FifoThreshold> for vals::Fth {
|
||||
fn from(value: FifoThreshold) -> Self {
|
||||
match value {
|
||||
@ -51,27 +126,15 @@ impl From<FifoThreshold> for vals::Fth {
|
||||
}
|
||||
}
|
||||
|
||||
struct ChannelState {
|
||||
waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl ChannelState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
channels: [ChannelState; DMA_CHANNEL_COUNT],
|
||||
ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT],
|
||||
}
|
||||
|
||||
impl State {
|
||||
const fn new() -> Self {
|
||||
const CH: ChannelState = ChannelState::new();
|
||||
const AW: AtomicWaker = AtomicWaker::new();
|
||||
Self {
|
||||
channels: [CH; DMA_CHANNEL_COUNT],
|
||||
ch_wakers: [AW; DMA_CHANNEL_COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,158 +155,183 @@ pub(crate) unsafe fn init(irq_priority: Priority) {
|
||||
|
||||
foreach_dma_channel! {
|
||||
($channel_peri:ident, $dma_peri:ident, dma, $channel_num:expr, $index:expr, $dmamux:tt) => {
|
||||
impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {
|
||||
unsafe fn start_write<W: Word>(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) {
|
||||
let (ptr, len) = super::slice_ptr_parts(buf);
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
vals::Dir::MEMORYTOPERIPHERAL,
|
||||
reg_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
)
|
||||
impl sealed::Channel for crate::peripherals::$channel_peri {
|
||||
fn regs(&self) -> pac::dma::Dma {
|
||||
pac::$dma_peri
|
||||
}
|
||||
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
vals::Dir::MEMORYTOPERIPHERAL,
|
||||
reg_addr as *const u32,
|
||||
repeated as *mut u32,
|
||||
count,
|
||||
false,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
)
|
||||
fn num(&self) -> usize {
|
||||
$channel_num
|
||||
}
|
||||
|
||||
unsafe fn start_read<W: Word>(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) {
|
||||
let (ptr, len) = super::slice_ptr_parts_mut(buf);
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
vals::Dir::PERIPHERALTOMEMORY,
|
||||
reg_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
);
|
||||
fn index(&self) -> usize {
|
||||
$index
|
||||
}
|
||||
|
||||
unsafe fn start_double_buffered_read<W: Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
reg_addr: *const W,
|
||||
buffer0: *mut W,
|
||||
buffer1: *mut W,
|
||||
buffer_len: usize,
|
||||
options: TransferOptions,
|
||||
) {
|
||||
low_level_api::start_dbm_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
vals::Dir::PERIPHERALTOMEMORY,
|
||||
reg_addr as *const u32,
|
||||
buffer0 as *mut u32,
|
||||
buffer1 as *mut u32,
|
||||
buffer_len,
|
||||
true,
|
||||
vals::Size::from(W::bits()),
|
||||
options,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
|
||||
#[cfg(dmamux)]
|
||||
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn set_buffer0<W: Word>(&mut self, buffer: *mut W) {
|
||||
low_level_api::set_dbm_buffer0(pac::$dma_peri, $channel_num, buffer as *mut u32);
|
||||
}
|
||||
|
||||
unsafe fn set_buffer1<W: Word>(&mut self, buffer: *mut W) {
|
||||
low_level_api::set_dbm_buffer1(pac::$dma_peri, $channel_num, buffer as *mut u32);
|
||||
}
|
||||
|
||||
unsafe fn is_buffer0_accessible(&mut self) -> bool {
|
||||
low_level_api::is_buffer0_accessible(pac::$dma_peri, $channel_num)
|
||||
}
|
||||
|
||||
fn request_stop(&mut self) {
|
||||
unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);}
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)}
|
||||
}
|
||||
|
||||
fn remaining_transfers(&mut self) -> u16 {
|
||||
unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)}
|
||||
}
|
||||
|
||||
fn set_waker(&mut self, waker: &Waker) {
|
||||
unsafe {low_level_api::set_waker($index, waker )}
|
||||
}
|
||||
|
||||
fn on_irq() {
|
||||
unsafe {
|
||||
low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index);
|
||||
}
|
||||
unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
|
||||
}
|
||||
}
|
||||
impl crate::dma::Channel for crate::peripherals::$channel_peri { }
|
||||
|
||||
impl Channel for crate::peripherals::$channel_peri {}
|
||||
};
|
||||
}
|
||||
|
||||
mod low_level_api {
|
||||
/// Safety: Must be called with a matching set of parameters for a valid dma channel
|
||||
pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: usize) {
|
||||
let cr = dma.st(channel_num).cr();
|
||||
let isr = dma.isr(channel_num / 4).read();
|
||||
|
||||
if isr.teif(channel_num % 4) {
|
||||
panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
|
||||
}
|
||||
|
||||
if isr.tcif(channel_num % 4) && cr.read().tcie() {
|
||||
/* acknowledge transfer complete interrupt */
|
||||
dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
|
||||
STATE.ch_wakers[index].wake();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(dma_v2, dmamux))]
|
||||
pub type Request = u8;
|
||||
#[cfg(not(any(dma_v2, dmamux)))]
|
||||
pub type Request = ();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
||||
#[cfg(not(dmamux))]
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub unsafe fn start_transfer(
|
||||
dma: pac::dma::Dma,
|
||||
channel_number: u8,
|
||||
pub trait Channel {
|
||||
fn regs(&self) -> pac::dma::Dma;
|
||||
fn num(&self) -> usize;
|
||||
fn index(&self) -> usize;
|
||||
fn on_irq();
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Transfer<'a, C: Channel> {
|
||||
channel: PeripheralRef<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub unsafe fn new_read<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
dir: vals::Dir,
|
||||
peri_addr: *mut W,
|
||||
buf: &'a mut [W],
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
||||
}
|
||||
|
||||
pub unsafe fn new_read_raw<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
peri_addr: *mut W,
|
||||
buf: *mut [W],
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
let (ptr, len) = super::slice_ptr_parts_mut(buf);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::PeripheralToMemory,
|
||||
peri_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
buf: &'a [W],
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write_raw<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
buf: *const [W],
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
let (ptr, len) = super::slice_ptr_parts(buf);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::MemoryToPeripheral,
|
||||
peri_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write_repeated<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
repeated: &'a W,
|
||||
count: usize,
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::MemoryToPeripheral,
|
||||
peri_addr as *const u32,
|
||||
repeated as *const W as *mut u32,
|
||||
count,
|
||||
false,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn new_inner(
|
||||
channel: PeripheralRef<'a, C>,
|
||||
_request: Request,
|
||||
dir: Dir,
|
||||
peri_addr: *const u32,
|
||||
mem_addr: *mut u32,
|
||||
mem_len: usize,
|
||||
incr_mem: bool,
|
||||
data_size: vals::Size,
|
||||
data_size: WordSize,
|
||||
options: TransferOptions,
|
||||
#[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux,
|
||||
#[cfg(dmamux)] dmamux_ch_num: u8,
|
||||
) {
|
||||
#[cfg(dmamux)]
|
||||
super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
|
||||
) -> Self {
|
||||
let ch = channel.regs().st(channel.num());
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
reset_status(dma, channel_number);
|
||||
let mut this = Self { channel };
|
||||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
|
||||
let ch = dma.st(channel_number as _);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
ch.m0ar().write_value(mem_addr as u32);
|
||||
ch.ndtr().write_value(regs::Ndtr(mem_len as _));
|
||||
@ -258,15 +346,14 @@ mod low_level_api {
|
||||
}
|
||||
});
|
||||
ch.cr().write(|w| {
|
||||
w.set_dir(dir);
|
||||
w.set_msize(data_size);
|
||||
w.set_psize(data_size);
|
||||
w.set_dir(dir.into());
|
||||
w.set_msize(data_size.into());
|
||||
w.set_psize(data_size.into());
|
||||
w.set_pl(vals::Pl::VERYHIGH);
|
||||
if incr_mem {
|
||||
w.set_minc(vals::Inc::INCREMENTED);
|
||||
} else {
|
||||
w.set_minc(vals::Inc::FIXED);
|
||||
}
|
||||
w.set_minc(match incr_mem {
|
||||
true => vals::Inc::INCREMENTED,
|
||||
false => vals::Inc::FIXED,
|
||||
});
|
||||
w.set_pinc(vals::Inc::FIXED);
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
@ -274,7 +361,7 @@ mod low_level_api {
|
||||
w.set_trbuff(true);
|
||||
|
||||
#[cfg(dma_v2)]
|
||||
w.set_chsel(request);
|
||||
w.set_chsel(_request);
|
||||
|
||||
w.set_pburst(options.pburst.into());
|
||||
w.set_mburst(options.mburst.into());
|
||||
@ -282,159 +369,232 @@ mod low_level_api {
|
||||
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub unsafe fn start_dbm_transfer(
|
||||
dma: pac::dma::Dma,
|
||||
channel_number: u8,
|
||||
request: Request,
|
||||
dir: vals::Dir,
|
||||
peri_addr: *const u32,
|
||||
mem0_addr: *mut u32,
|
||||
mem1_addr: *mut u32,
|
||||
mem_len: usize,
|
||||
incr_mem: bool,
|
||||
data_size: vals::Size,
|
||||
options: TransferOptions,
|
||||
#[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux,
|
||||
#[cfg(dmamux)] dmamux_ch_num: u8,
|
||||
) {
|
||||
#[cfg(dmamux)]
|
||||
super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request);
|
||||
fn clear_irqs(&mut self) {
|
||||
let isrn = self.channel.num() / 4;
|
||||
let isrbit = self.channel.num() % 4;
|
||||
|
||||
trace!(
|
||||
"Starting DBM transfer with 0: 0x{:x}, 1: 0x{:x}, len: 0x{:x}",
|
||||
mem0_addr as u32,
|
||||
mem1_addr as u32,
|
||||
mem_len
|
||||
);
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
reset_status(dma, channel_number);
|
||||
|
||||
let ch = dma.st(channel_number as _);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
ch.m0ar().write_value(mem0_addr as u32);
|
||||
// configures the second buffer for DBM
|
||||
ch.m1ar().write_value(mem1_addr as u32);
|
||||
ch.ndtr().write_value(regs::Ndtr(mem_len as _));
|
||||
ch.cr().write(|w| {
|
||||
w.set_dir(dir);
|
||||
w.set_msize(data_size);
|
||||
w.set_psize(data_size);
|
||||
w.set_pl(vals::Pl::VERYHIGH);
|
||||
if incr_mem {
|
||||
w.set_minc(vals::Inc::INCREMENTED);
|
||||
} else {
|
||||
w.set_minc(vals::Inc::FIXED);
|
||||
}
|
||||
w.set_pinc(vals::Inc::FIXED);
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
|
||||
#[cfg(dma_v1)]
|
||||
w.set_trbuff(true);
|
||||
|
||||
#[cfg(dma_v2)]
|
||||
w.set_chsel(request);
|
||||
|
||||
// enable double buffered mode
|
||||
w.set_dbm(vals::Dbm::ENABLED);
|
||||
|
||||
w.set_pburst(options.pburst.into());
|
||||
w.set_mburst(options.mburst.into());
|
||||
w.set_pfctrl(options.flow_ctrl.into());
|
||||
|
||||
w.set_en(true);
|
||||
});
|
||||
unsafe {
|
||||
self.channel.regs().ifcr(isrn).write(|w| {
|
||||
w.set_tcif(isrbit, true);
|
||||
w.set_teif(isrbit, true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_dbm_buffer0(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.st(channel_number as _);
|
||||
// change M0AR to the new address
|
||||
ch.m0ar().write_value(mem_addr as _);
|
||||
}
|
||||
|
||||
pub unsafe fn set_dbm_buffer1(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.st(channel_number as _);
|
||||
// change M1AR to the new address
|
||||
ch.m1ar().write_value(mem_addr as _);
|
||||
}
|
||||
|
||||
pub unsafe fn is_buffer0_accessible(dma: pac::dma::Dma, channel_number: u8) -> bool {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.st(channel_number as _);
|
||||
// check the current target register value
|
||||
ch.cr().read().ct() == vals::Ct::MEMORY1
|
||||
}
|
||||
|
||||
/// Stops the DMA channel.
|
||||
pub unsafe fn request_stop(dma: pac::dma::Dma, channel_number: u8) {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.st(channel_number as _);
|
||||
pub fn request_stop(&mut self) {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
|
||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
||||
ch.cr().write(|w| {
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
});
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
unsafe {
|
||||
ch.cr().write(|w| {
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the running status of the channel
|
||||
pub unsafe fn is_running(dma: pac::dma::Dma, ch: u8) -> bool {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.st(ch as _);
|
||||
// Get whether it's enabled (running)
|
||||
ch.cr().read().en()
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
unsafe { ch.cr().read() }.en()
|
||||
}
|
||||
|
||||
/// Gets the total remaining transfers for the channel
|
||||
/// Note: this will be zero for transfers that completed without cancellation.
|
||||
pub unsafe fn get_remaining_transfers(dma: pac::dma::Dma, ch: u8) -> u16 {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.st(ch as _);
|
||||
// read the remaining transfer count. If this is zero, the transfer completed fully.
|
||||
ch.ndtr().read().ndt()
|
||||
pub fn get_remaining_transfers(&self) -> u16 {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
unsafe { ch.ndtr().read() }.ndt()
|
||||
}
|
||||
|
||||
/// Sets the waker for the specified DMA channel
|
||||
pub unsafe fn set_waker(state_number: usize, waker: &Waker) {
|
||||
STATE.channels[state_number].waker.register(waker);
|
||||
pub fn blocking_wait(mut self) {
|
||||
while self.is_running() {}
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
core::mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn reset_status(dma: pac::dma::Dma, channel_number: u8) {
|
||||
let isrn = channel_number as usize / 4;
|
||||
let isrbit = channel_number as usize % 4;
|
||||
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||
fn drop(&mut self) {
|
||||
self.request_stop();
|
||||
while self.is_running() {}
|
||||
|
||||
dma.ifcr(isrn).write(|w| {
|
||||
w.set_tcif(isrbit, true);
|
||||
w.set_teif(isrbit, true);
|
||||
});
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety: Must be called with a matching set of parameters for a valid dma channel
|
||||
pub unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: u8, state_index: u8) {
|
||||
let channel_num = channel_num as usize;
|
||||
let state_index = state_index as usize;
|
||||
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
STATE.ch_wakers[self.channel.index()].register(cx.waker());
|
||||
|
||||
let cr = dma.st(channel_num).cr();
|
||||
let isr = dma.isr(channel_num / 4).read();
|
||||
|
||||
if isr.teif(channel_num % 4) {
|
||||
panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
|
||||
}
|
||||
|
||||
if isr.tcif(channel_num % 4) && cr.read().tcie() {
|
||||
/* acknowledge transfer complete interrupt */
|
||||
dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
|
||||
STATE.channels[state_index].waker.wake();
|
||||
if self.is_running() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================================
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct DoubleBuffered<'a, C: Channel, W: Word> {
|
||||
channel: PeripheralRef<'a, C>,
|
||||
_phantom: PhantomData<W>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
||||
pub unsafe fn new_read(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
_request: Request,
|
||||
peri_addr: *mut W,
|
||||
buf0: *mut W,
|
||||
buf1: *mut W,
|
||||
len: usize,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
let dir = Dir::PeripheralToMemory;
|
||||
let data_size = W::size();
|
||||
|
||||
let channel_number = channel.num();
|
||||
let dma = channel.regs();
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let mut this = Self {
|
||||
channel,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
|
||||
let ch = dma.st(channel_number);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
ch.m0ar().write_value(buf0 as u32);
|
||||
ch.m1ar().write_value(buf1 as u32);
|
||||
ch.ndtr().write_value(regs::Ndtr(len as _));
|
||||
ch.fcr().write(|w| {
|
||||
if let Some(fth) = options.fifo_threshold {
|
||||
// FIFO mode
|
||||
w.set_dmdis(vals::Dmdis::DISABLED);
|
||||
w.set_fth(fth.into());
|
||||
} else {
|
||||
// Direct mode
|
||||
w.set_dmdis(vals::Dmdis::ENABLED);
|
||||
}
|
||||
});
|
||||
ch.cr().write(|w| {
|
||||
w.set_dir(dir.into());
|
||||
w.set_msize(data_size.into());
|
||||
w.set_psize(data_size.into());
|
||||
w.set_pl(vals::Pl::VERYHIGH);
|
||||
w.set_minc(vals::Inc::INCREMENTED);
|
||||
w.set_pinc(vals::Inc::FIXED);
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
#[cfg(dma_v1)]
|
||||
w.set_trbuff(true);
|
||||
|
||||
#[cfg(dma_v2)]
|
||||
w.set_chsel(_request);
|
||||
|
||||
w.set_pburst(options.pburst.into());
|
||||
w.set_mburst(options.mburst.into());
|
||||
w.set_pfctrl(options.flow_ctrl.into());
|
||||
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn clear_irqs(&mut self) {
|
||||
let channel_number = self.channel.num();
|
||||
let dma = self.channel.regs();
|
||||
let isrn = channel_number / 4;
|
||||
let isrbit = channel_number % 4;
|
||||
|
||||
unsafe {
|
||||
dma.ifcr(isrn).write(|w| {
|
||||
w.set_tcif(isrbit, true);
|
||||
w.set_teif(isrbit, true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
ch.m0ar().write_value(buffer as _);
|
||||
}
|
||||
|
||||
pub unsafe fn set_buffer1(&mut self, buffer: *mut W) {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
ch.m1ar().write_value(buffer as _);
|
||||
}
|
||||
|
||||
pub fn is_buffer0_accessible(&mut self) -> bool {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1
|
||||
}
|
||||
|
||||
pub fn set_waker(&mut self, waker: &Waker) {
|
||||
STATE.ch_wakers[self.channel.index()].register(waker);
|
||||
}
|
||||
|
||||
pub fn request_stop(&mut self) {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
|
||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
||||
unsafe {
|
||||
ch.cr().write(|w| {
|
||||
w.set_teie(true);
|
||||
w.set_tcie(true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
unsafe { ch.cr().read() }.en()
|
||||
}
|
||||
|
||||
/// Gets the total remaining transfers for the channel
|
||||
/// Note: this will be zero for transfers that completed without cancellation.
|
||||
pub fn get_remaining_transfers(&self) -> u16 {
|
||||
let ch = self.channel.regs().st(self.channel.num());
|
||||
unsafe { ch.ndtr().read() }.ndt()
|
||||
}
|
||||
|
||||
pub fn blocking_wait(mut self) {
|
||||
while self.is_running() {}
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
core::mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> {
|
||||
fn drop(&mut self) {
|
||||
self.request_stop();
|
||||
while self.is_running() {}
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::{pac, peripherals};
|
||||
|
||||
pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_ch_num: u8, request: u8) {
|
||||
let ch_mux_regs = dmamux_regs.ccr(dmamux_ch_num as _);
|
||||
pub(crate) unsafe fn configure_dmamux<M: MuxChannel>(channel: &mut M, request: u8) {
|
||||
let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num());
|
||||
ch_mux_regs.write(|reg| {
|
||||
reg.set_nbreq(0);
|
||||
reg.set_dmareq_id(request);
|
||||
@ -14,11 +14,11 @@ pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_c
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub(crate) mod dmamux_sealed {
|
||||
use super::*;
|
||||
pub trait MuxChannel {
|
||||
const DMAMUX_CH_NUM: u8;
|
||||
const DMAMUX_REGS: pac::dmamux::Dmamux;
|
||||
fn mux_regs(&self) -> pac::dmamux::Dmamux;
|
||||
fn mux_num(&self) -> usize;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,15 +26,19 @@ pub struct DMAMUX1;
|
||||
#[cfg(stm32h7)]
|
||||
pub struct DMAMUX2;
|
||||
|
||||
pub trait MuxChannel: sealed::MuxChannel + super::Channel {
|
||||
pub trait MuxChannel: dmamux_sealed::MuxChannel {
|
||||
type Mux;
|
||||
}
|
||||
|
||||
foreach_dma_channel! {
|
||||
($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $index:expr, {dmamux: $dmamux:ident, dmamux_channel: $dmamux_channel:expr}) => {
|
||||
impl sealed::MuxChannel for peripherals::$channel_peri {
|
||||
const DMAMUX_CH_NUM: u8 = $dmamux_channel;
|
||||
const DMAMUX_REGS: pac::dmamux::Dmamux = pac::$dmamux;
|
||||
impl dmamux_sealed::MuxChannel for peripherals::$channel_peri {
|
||||
fn mux_regs(&self) -> pac::dmamux::Dmamux {
|
||||
pac::$dmamux
|
||||
}
|
||||
fn mux_num(&self) -> usize {
|
||||
$dmamux_channel
|
||||
}
|
||||
}
|
||||
impl MuxChannel for peripherals::$channel_peri {
|
||||
type Mux = $dmamux;
|
||||
|
@ -1,13 +1,31 @@
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::Waker;
|
||||
#![macro_use]
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::{Request, TransferOptions, Word, WordSize};
|
||||
use super::word::{Word, WordSize};
|
||||
use super::Dir;
|
||||
use crate::_generated::GPDMA_CHANNEL_COUNT;
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::pac::gpdma::{vals, Gpdma};
|
||||
use crate::{interrupt, pac};
|
||||
use crate::pac;
|
||||
use crate::pac::gpdma::vals;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub struct TransferOptions {}
|
||||
|
||||
impl Default for TransferOptions {
|
||||
fn default() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WordSize> for vals::ChTr1Dw {
|
||||
fn from(raw: WordSize) -> Self {
|
||||
@ -19,27 +37,15 @@ impl From<WordSize> for vals::ChTr1Dw {
|
||||
}
|
||||
}
|
||||
|
||||
struct ChannelState {
|
||||
waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl ChannelState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
channels: [ChannelState; GPDMA_CHANNEL_COUNT],
|
||||
ch_wakers: [AtomicWaker; GPDMA_CHANNEL_COUNT],
|
||||
}
|
||||
|
||||
impl State {
|
||||
const fn new() -> Self {
|
||||
const CH: ChannelState = ChannelState::new();
|
||||
const AW: AtomicWaker = AtomicWaker::new();
|
||||
Self {
|
||||
channels: [CH; GPDMA_CHANNEL_COUNT],
|
||||
ch_wakers: [AW; GPDMA_CHANNEL_COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,10 +53,12 @@ impl State {
|
||||
static STATE: State = State::new();
|
||||
|
||||
/// safety: must be called only once
|
||||
pub(crate) unsafe fn init() {
|
||||
pub(crate) unsafe fn init(irq_priority: Priority) {
|
||||
foreach_interrupt! {
|
||||
($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
|
||||
interrupt::$irq::steal().enable();
|
||||
let irq = crate::interrupt::$irq::steal();
|
||||
irq.set_priority(irq_priority);
|
||||
irq.enable();
|
||||
};
|
||||
}
|
||||
crate::_generated::init_gpdma();
|
||||
@ -58,117 +66,171 @@ pub(crate) unsafe fn init() {
|
||||
|
||||
foreach_dma_channel! {
|
||||
($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => {
|
||||
impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {
|
||||
unsafe fn start_write<W: Word>(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) {
|
||||
let (ptr, len) = super::slice_ptr_parts(buf);
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
low_level_api::Dir::MemoryToPeripheral,
|
||||
reg_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::bits(),
|
||||
options,
|
||||
)
|
||||
impl sealed::Channel for crate::peripherals::$channel_peri {
|
||||
fn regs(&self) -> pac::gpdma::Gpdma {
|
||||
pac::$dma_peri
|
||||
}
|
||||
|
||||
unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) {
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
low_level_api::Dir::MemoryToPeripheral,
|
||||
reg_addr as *const u32,
|
||||
repeated as *mut u32,
|
||||
count,
|
||||
false,
|
||||
W::bits(),
|
||||
options,
|
||||
)
|
||||
fn num(&self) -> usize {
|
||||
$channel_num
|
||||
}
|
||||
|
||||
unsafe fn start_read<W: Word>(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) {
|
||||
let (ptr, len) = super::slice_ptr_parts_mut(buf);
|
||||
low_level_api::start_transfer(
|
||||
pac::$dma_peri,
|
||||
$channel_num,
|
||||
request,
|
||||
low_level_api::Dir::PeripheralToMemory,
|
||||
reg_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::bits(),
|
||||
options,
|
||||
);
|
||||
fn index(&self) -> usize {
|
||||
$index
|
||||
}
|
||||
|
||||
unsafe fn start_double_buffered_read<W: Word>(
|
||||
&mut self,
|
||||
_request: Request,
|
||||
_reg_addr: *const W,
|
||||
_buffer0: *mut W,
|
||||
_buffer1: *mut W,
|
||||
_buffer_len: usize,
|
||||
_options: TransferOptions,
|
||||
) {
|
||||
panic!("Unsafe double buffered mode is unavailable on GPBDMA");
|
||||
}
|
||||
|
||||
unsafe fn set_buffer0<W: Word>(&mut self, _buffer: *mut W) {
|
||||
panic!("Unsafe double buffered mode is unavailable on GPBDMA");
|
||||
}
|
||||
|
||||
unsafe fn set_buffer1<W: Word>(&mut self, _buffer: *mut W) {
|
||||
panic!("Unsafe double buffered mode is unavailable on GPBDMA");
|
||||
}
|
||||
|
||||
unsafe fn is_buffer0_accessible(&mut self) -> bool {
|
||||
panic!("Unsafe double buffered mode is unavailable on GPBDMA");
|
||||
}
|
||||
|
||||
fn request_stop(&mut self) {
|
||||
unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);}
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)}
|
||||
}
|
||||
|
||||
fn remaining_transfers(&mut self) -> u16 {
|
||||
unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)}
|
||||
}
|
||||
|
||||
fn set_waker(&mut self, waker: &Waker) {
|
||||
unsafe {low_level_api::set_waker($index, waker )}
|
||||
}
|
||||
|
||||
fn on_irq() {
|
||||
unsafe {
|
||||
low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index);
|
||||
}
|
||||
unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) }
|
||||
}
|
||||
}
|
||||
impl crate::dma::Channel for crate::peripherals::$channel_peri { }
|
||||
|
||||
impl Channel for crate::peripherals::$channel_peri {}
|
||||
};
|
||||
}
|
||||
|
||||
mod low_level_api {
|
||||
use super::*;
|
||||
/// Safety: Must be called with a matching set of parameters for a valid dma channel
|
||||
pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, index: usize) {
|
||||
let ch = dma.ch(channel_num);
|
||||
let sr = ch.sr().read();
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Dir {
|
||||
MemoryToPeripheral,
|
||||
PeripheralToMemory,
|
||||
if sr.dtef() {
|
||||
panic!(
|
||||
"DMA: data transfer error on DMA@{:08x} channel {}",
|
||||
dma.0 as u32, channel_num
|
||||
);
|
||||
}
|
||||
if sr.usef() {
|
||||
panic!(
|
||||
"DMA: user settings error on DMA@{:08x} channel {}",
|
||||
dma.0 as u32, channel_num
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn start_transfer(
|
||||
dma: Gpdma,
|
||||
channel_number: u8,
|
||||
if sr.suspf() || sr.tcf() {
|
||||
// disable all xxIEs to prevent the irq from firing again.
|
||||
ch.cr().write(|_| {});
|
||||
|
||||
// Wake the future. It'll look at tcf and see it's set.
|
||||
STATE.ch_wakers[index].wake();
|
||||
}
|
||||
}
|
||||
|
||||
pub type Request = u8;
|
||||
|
||||
#[cfg(dmamux)]
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
||||
#[cfg(not(dmamux))]
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Channel {
|
||||
fn regs(&self) -> pac::gpdma::Gpdma;
|
||||
fn num(&self) -> usize;
|
||||
fn index(&self) -> usize;
|
||||
fn on_irq();
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Transfer<'a, C: Channel> {
|
||||
channel: PeripheralRef<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub unsafe fn new_read<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
peri_addr: *mut W,
|
||||
buf: &'a mut [W],
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
||||
}
|
||||
|
||||
pub unsafe fn new_read_raw<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
peri_addr: *mut W,
|
||||
buf: *mut [W],
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
let (ptr, len) = super::slice_ptr_parts_mut(buf);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::PeripheralToMemory,
|
||||
peri_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
buf: &'a [W],
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write_raw<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
buf: *const [W],
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
let (ptr, len) = super::slice_ptr_parts(buf);
|
||||
assert!(len > 0 && len <= 0xFFFF);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::MemoryToPeripheral,
|
||||
peri_addr as *const u32,
|
||||
ptr as *mut u32,
|
||||
len,
|
||||
true,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn new_write_repeated<W: Word>(
|
||||
channel: impl Peripheral<P = C> + 'a,
|
||||
request: Request,
|
||||
repeated: &'a W,
|
||||
count: usize,
|
||||
peri_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
) -> Self {
|
||||
into_ref!(channel);
|
||||
|
||||
Self::new_inner(
|
||||
channel,
|
||||
request,
|
||||
Dir::MemoryToPeripheral,
|
||||
peri_addr as *const u32,
|
||||
repeated as *const W as *mut u32,
|
||||
count,
|
||||
false,
|
||||
W::size(),
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn new_inner(
|
||||
channel: PeripheralRef<'a, C>,
|
||||
request: Request,
|
||||
dir: Dir,
|
||||
peri_addr: *const u32,
|
||||
@ -176,24 +238,19 @@ mod low_level_api {
|
||||
mem_len: usize,
|
||||
incr_mem: bool,
|
||||
data_size: WordSize,
|
||||
options: TransferOptions,
|
||||
) {
|
||||
assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported");
|
||||
assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported");
|
||||
assert!(
|
||||
options.flow_ctrl == crate::dma::FlowControl::Dma,
|
||||
"Peripheral flow control not supported"
|
||||
);
|
||||
assert!(options.fifo_threshold.is_none(), "FIFO mode not supported");
|
||||
_options: TransferOptions,
|
||||
) -> Self {
|
||||
let ch = channel.regs().ch(channel.num());
|
||||
|
||||
// "Preceding reads and writes cannot be moved past subsequent writes."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let ch = dma.ch(channel_number as _);
|
||||
let this = Self { channel };
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, request);
|
||||
|
||||
// Reset ch
|
||||
ch.cr().write(|w| w.set_reset(true));
|
||||
|
||||
ch.llr().write(|_| {}); // no linked list
|
||||
ch.tr1().write(|w| {
|
||||
w.set_sdw(data_size.into());
|
||||
@ -234,72 +291,66 @@ mod low_level_api {
|
||||
// Start it
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Stops the DMA channel.
|
||||
pub unsafe fn request_stop(dma: Gpdma, channel_number: u8) {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.ch(channel_number as _);
|
||||
pub fn request_stop(&mut self) {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
|
||||
// Disable the channel. Keep the IEs enabled so the irqs still fire.
|
||||
ch.cr().write(|w| {
|
||||
w.set_tcie(true);
|
||||
w.set_useie(true);
|
||||
w.set_dteie(true);
|
||||
w.set_suspie(true);
|
||||
});
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
unsafe {
|
||||
ch.cr().write(|w| {
|
||||
w.set_tcie(true);
|
||||
w.set_useie(true);
|
||||
w.set_dteie(true);
|
||||
w.set_suspie(true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the running status of the channel
|
||||
pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool {
|
||||
let ch = dma.ch(ch as _);
|
||||
!ch.sr().read().tcf()
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
!unsafe { ch.sr().read() }.tcf()
|
||||
}
|
||||
|
||||
/// Gets the total remaining transfers for the channel
|
||||
/// Note: this will be zero for transfers that completed without cancellation.
|
||||
pub unsafe fn get_remaining_transfers(dma: Gpdma, ch: u8) -> u16 {
|
||||
// get a handle on the channel itself
|
||||
let ch = dma.ch(ch as _);
|
||||
// read the remaining transfer count. If this is zero, the transfer completed fully.
|
||||
ch.br1().read().bndt()
|
||||
pub fn get_remaining_transfers(&self) -> u16 {
|
||||
let ch = self.channel.regs().ch(self.channel.num());
|
||||
unsafe { ch.br1().read() }.bndt()
|
||||
}
|
||||
|
||||
/// Sets the waker for the specified DMA channel
|
||||
pub unsafe fn set_waker(state_number: usize, waker: &Waker) {
|
||||
STATE.channels[state_number].waker.register(waker);
|
||||
pub fn blocking_wait(mut self) {
|
||||
while self.is_running() {}
|
||||
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
core::mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety: Must be called with a matching set of parameters for a valid dma channel
|
||||
pub unsafe fn on_irq_inner(dma: Gpdma, channel_num: u8, state_index: u8) {
|
||||
let channel_num = channel_num as usize;
|
||||
let state_index = state_index as usize;
|
||||
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||
fn drop(&mut self) {
|
||||
self.request_stop();
|
||||
while self.is_running() {}
|
||||
|
||||
let ch = dma.ch(channel_num);
|
||||
let sr = ch.sr().read();
|
||||
// "Subsequent reads and writes cannot be moved ahead of preceding reads."
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
if sr.dtef() {
|
||||
panic!(
|
||||
"DMA: data transfer error on DMA@{:08x} channel {}",
|
||||
dma.0 as u32, channel_num
|
||||
);
|
||||
}
|
||||
if sr.usef() {
|
||||
panic!(
|
||||
"DMA: user settings error on DMA@{:08x} channel {}",
|
||||
dma.0 as u32, channel_num
|
||||
);
|
||||
}
|
||||
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
STATE.ch_wakers[self.channel.index()].register(cx.waker());
|
||||
|
||||
if sr.suspf() || sr.tcf() {
|
||||
// disable all xxIEs to prevent the irq from firing again.
|
||||
ch.cr().write(|_| {});
|
||||
|
||||
// Wake the future. It'll look at tcf and see it's set.
|
||||
STATE.channels[state_index].waker.wake();
|
||||
if self.is_running() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,329 +1,47 @@
|
||||
#[cfg(bdma)]
|
||||
pub(crate) mod bdma;
|
||||
#[cfg(dma)]
|
||||
pub(crate) mod dma;
|
||||
#[cfg(dma)]
|
||||
pub use dma::*;
|
||||
|
||||
// stm32h7 has both dma and bdma. In that case, we export dma as "main" dma,
|
||||
// and bdma as "secondary", under `embassy_stm32::dma::bdma`.
|
||||
#[cfg(all(bdma, dma))]
|
||||
pub mod bdma;
|
||||
|
||||
#[cfg(all(bdma, not(dma)))]
|
||||
pub(crate) mod bdma;
|
||||
#[cfg(all(bdma, not(dma)))]
|
||||
pub use bdma::*;
|
||||
|
||||
#[cfg(gpdma)]
|
||||
pub(crate) mod gpdma;
|
||||
#[cfg(gpdma)]
|
||||
pub use gpdma::*;
|
||||
|
||||
#[cfg(dmamux)]
|
||||
mod dmamux;
|
||||
#[cfg(gpdma)]
|
||||
mod gpdma;
|
||||
|
||||
use core::future::Future;
|
||||
pub mod word;
|
||||
|
||||
use core::mem;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
#[cfg(any(dma, bdma))]
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
use embassy_hal_common::{impl_peripheral, into_ref};
|
||||
use embassy_hal_common::impl_peripheral;
|
||||
|
||||
#[cfg(dmamux)]
|
||||
pub use self::dmamux::*;
|
||||
use crate::Peripheral;
|
||||
|
||||
#[cfg(feature = "unstable-pac")]
|
||||
pub mod low_level {
|
||||
pub use super::transfers::*;
|
||||
}
|
||||
|
||||
pub(crate) use transfers::*;
|
||||
|
||||
#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))]
|
||||
pub type Request = u8;
|
||||
#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))]
|
||||
pub type Request = ();
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Word {}
|
||||
|
||||
pub trait Channel {
|
||||
/// Starts this channel for writing a stream of words.
|
||||
///
|
||||
/// Safety:
|
||||
/// - `buf` must point to a valid buffer for DMA reading.
|
||||
/// - `buf` must be alive for the entire duration of the DMA transfer.
|
||||
/// - `reg_addr` must be a valid peripheral register address to write to.
|
||||
unsafe fn start_write<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
buf: *const [W],
|
||||
reg_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
/// Starts this channel for writing a word repeatedly.
|
||||
///
|
||||
/// Safety:
|
||||
/// - `reg_addr` must be a valid peripheral register address to write to.
|
||||
unsafe fn start_write_repeated<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
repeated: *const W,
|
||||
count: usize,
|
||||
reg_addr: *mut W,
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
/// Starts this channel for reading a stream of words.
|
||||
///
|
||||
/// Safety:
|
||||
/// - `buf` must point to a valid buffer for DMA writing.
|
||||
/// - `buf` must be alive for the entire duration of the DMA transfer.
|
||||
/// - `reg_addr` must be a valid peripheral register address to read from.
|
||||
unsafe fn start_read<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
reg_addr: *const W,
|
||||
buf: *mut [W],
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
/// DMA double-buffered mode is unsafe as UB can happen when the hardware writes to a buffer currently owned by the software
|
||||
/// more information can be found here: https://github.com/embassy-rs/embassy/issues/702
|
||||
/// This feature is now used solely for the purposes of implementing giant DMA transfers required for DCMI
|
||||
unsafe fn start_double_buffered_read<W: super::Word>(
|
||||
&mut self,
|
||||
request: Request,
|
||||
reg_addr: *const W,
|
||||
buffer0: *mut W,
|
||||
buffer1: *mut W,
|
||||
buffer_len: usize,
|
||||
options: TransferOptions,
|
||||
);
|
||||
|
||||
unsafe fn set_buffer0<W: super::Word>(&mut self, buffer: *mut W);
|
||||
|
||||
unsafe fn set_buffer1<W: super::Word>(&mut self, buffer: *mut W);
|
||||
|
||||
unsafe fn is_buffer0_accessible(&mut self) -> bool;
|
||||
|
||||
/// Requests the channel to stop.
|
||||
/// NOTE: The channel does not immediately stop, you have to wait
|
||||
/// for `is_running() = false`.
|
||||
fn request_stop(&mut self);
|
||||
|
||||
/// Returns whether this channel is running or stopped.
|
||||
///
|
||||
/// The channel stops running when it either completes or is manually stopped.
|
||||
fn is_running(&self) -> bool;
|
||||
|
||||
/// Returns the total number of remaining transfers.
|
||||
fn remaining_transfers(&mut self) -> u16;
|
||||
|
||||
/// Sets the waker that is called when this channel stops (either completed or manually stopped)
|
||||
fn set_waker(&mut self, waker: &Waker);
|
||||
|
||||
/// This is called when this channel triggers an interrupt.
|
||||
/// Note: Because some channels share an interrupt, this function might be
|
||||
/// called for a channel that didn't trigger an interrupt.
|
||||
fn on_irq();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum WordSize {
|
||||
OneByte,
|
||||
TwoBytes,
|
||||
FourBytes,
|
||||
enum Dir {
|
||||
MemoryToPeripheral,
|
||||
PeripheralToMemory,
|
||||
}
|
||||
|
||||
impl WordSize {
|
||||
pub fn bytes(&self) -> usize {
|
||||
match self {
|
||||
Self::OneByte => 1,
|
||||
Self::TwoBytes => 2,
|
||||
Self::FourBytes => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Word: sealed::Word {
|
||||
fn bits() -> WordSize;
|
||||
}
|
||||
|
||||
impl sealed::Word for u8 {}
|
||||
impl Word for u8 {
|
||||
fn bits() -> WordSize {
|
||||
WordSize::OneByte
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::Word for u16 {}
|
||||
impl Word for u16 {
|
||||
fn bits() -> WordSize {
|
||||
WordSize::TwoBytes
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::Word for u32 {}
|
||||
impl Word for u32 {
|
||||
fn bits() -> WordSize {
|
||||
WordSize::FourBytes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Burst {
|
||||
/// Single transfer
|
||||
Single,
|
||||
/// Incremental burst of 4 beats
|
||||
Incr4,
|
||||
/// Incremental burst of 8 beats
|
||||
Incr8,
|
||||
/// Incremental burst of 16 beats
|
||||
Incr16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FlowControl {
|
||||
/// Flow control by DMA
|
||||
Dma,
|
||||
/// Flow control by peripheral
|
||||
Peripheral,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum FifoThreshold {
|
||||
/// 1/4 full FIFO
|
||||
Quarter,
|
||||
/// 1/2 full FIFO
|
||||
Half,
|
||||
/// 3/4 full FIFO
|
||||
ThreeQuarters,
|
||||
/// Full FIFO
|
||||
Full,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct TransferOptions {
|
||||
/// Peripheral burst transfer configuration
|
||||
pub pburst: Burst,
|
||||
/// Memory burst transfer configuration
|
||||
pub mburst: Burst,
|
||||
/// Flow control configuration
|
||||
pub flow_ctrl: FlowControl,
|
||||
/// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
|
||||
pub fifo_threshold: Option<FifoThreshold>,
|
||||
}
|
||||
|
||||
impl Default for TransferOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pburst: Burst::Single,
|
||||
mburst: Burst::Single,
|
||||
flow_ctrl: FlowControl::Dma,
|
||||
fifo_threshold: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod transfers {
|
||||
use embassy_hal_common::PeripheralRef;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn read<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
reg_addr: *mut W,
|
||||
buf: &'a mut [W],
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
|
||||
into_ref!(channel);
|
||||
|
||||
unsafe { channel.start_read::<W>(request, reg_addr, buf, Default::default()) };
|
||||
|
||||
Transfer::new(channel)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn write<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
buf: &'a [W],
|
||||
reg_addr: *mut W,
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
assert!(buf.len() > 0 && buf.len() <= 0xFFFF);
|
||||
into_ref!(channel);
|
||||
|
||||
unsafe { channel.start_write::<W>(request, buf, reg_addr, Default::default()) };
|
||||
|
||||
Transfer::new(channel)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn write_repeated<'a, W: Word>(
|
||||
channel: impl Peripheral<P = impl Channel> + 'a,
|
||||
request: Request,
|
||||
repeated: *const W,
|
||||
count: usize,
|
||||
reg_addr: *mut W,
|
||||
) -> impl Future<Output = ()> + 'a {
|
||||
into_ref!(channel);
|
||||
|
||||
unsafe { channel.start_write_repeated::<W>(request, repeated, count, reg_addr, Default::default()) };
|
||||
|
||||
Transfer::new(channel)
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub(crate) struct Transfer<'a, C: Channel> {
|
||||
channel: PeripheralRef<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub(crate) fn new(channel: impl Peripheral<P = C> + 'a) -> Self {
|
||||
into_ref!(channel);
|
||||
Self { channel }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||
fn drop(&mut self) {
|
||||
self.channel.request_stop();
|
||||
while self.channel.is_running() {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.channel.set_waker(cx.waker());
|
||||
if self.channel.is_running() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||
|
||||
pub struct NoDma;
|
||||
|
||||
impl_peripheral!(NoDma);
|
||||
|
||||
// safety: must be called only once at startup
|
||||
pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) {
|
||||
#[cfg(bdma)]
|
||||
bdma::init(bdma_priority);
|
||||
#[cfg(dma)]
|
||||
dma::init(dma_priority);
|
||||
#[cfg(dmamux)]
|
||||
dmamux::init();
|
||||
#[cfg(gpdma)]
|
||||
gpdma::init();
|
||||
}
|
||||
|
||||
// TODO: replace transmutes with core::ptr::metadata once it's stable
|
||||
#[allow(unused)]
|
||||
pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) {
|
||||
@ -334,3 +52,19 @@ pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (usize, usize) {
|
||||
pub(crate) fn slice_ptr_parts_mut<T>(slice: *mut [T]) -> (usize, usize) {
|
||||
unsafe { mem::transmute(slice) }
|
||||
}
|
||||
|
||||
// safety: must be called only once at startup
|
||||
pub(crate) unsafe fn init(
|
||||
#[cfg(bdma)] bdma_priority: Priority,
|
||||
#[cfg(dma)] dma_priority: Priority,
|
||||
#[cfg(gpdma)] gpdma_priority: Priority,
|
||||
) {
|
||||
#[cfg(bdma)]
|
||||
bdma::init(bdma_priority);
|
||||
#[cfg(dma)]
|
||||
dma::init(dma_priority);
|
||||
#[cfg(gpdma)]
|
||||
gpdma::init(gpdma_priority);
|
||||
#[cfg(dmamux)]
|
||||
dmamux::init();
|
||||
}
|
||||
|
79
embassy-stm32/src/dma/word.rs
Normal file
79
embassy-stm32/src/dma/word.rs
Normal file
@ -0,0 +1,79 @@
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum WordSize {
|
||||
OneByte,
|
||||
TwoBytes,
|
||||
FourBytes,
|
||||
}
|
||||
|
||||
impl WordSize {
|
||||
pub fn bytes(&self) -> usize {
|
||||
match self {
|
||||
Self::OneByte => 1,
|
||||
Self::TwoBytes => 2,
|
||||
Self::FourBytes => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Word {}
|
||||
}
|
||||
|
||||
pub trait Word: sealed::Word + Default + Copy + 'static {
|
||||
fn size() -> WordSize;
|
||||
fn bits() -> usize;
|
||||
}
|
||||
|
||||
macro_rules! impl_word {
|
||||
(_, $T:ident, $bits:literal, $size:ident) => {
|
||||
impl sealed::Word for $T {}
|
||||
impl Word for $T {
|
||||
fn bits() -> usize {
|
||||
$bits
|
||||
}
|
||||
fn size() -> WordSize {
|
||||
WordSize::$size
|
||||
}
|
||||
}
|
||||
};
|
||||
($T:ident, $uX:ident, $bits:literal, $size:ident) => {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct $T(pub $uX);
|
||||
impl_word!(_, $T, $bits, $size);
|
||||
};
|
||||
}
|
||||
|
||||
impl_word!(U1, u8, 1, OneByte);
|
||||
impl_word!(U2, u8, 2, OneByte);
|
||||
impl_word!(U3, u8, 3, OneByte);
|
||||
impl_word!(U4, u8, 4, OneByte);
|
||||
impl_word!(U5, u8, 5, OneByte);
|
||||
impl_word!(U6, u8, 6, OneByte);
|
||||
impl_word!(U7, u8, 7, OneByte);
|
||||
impl_word!(_, u8, 8, OneByte);
|
||||
impl_word!(U9, u16, 9, TwoBytes);
|
||||
impl_word!(U10, u16, 10, TwoBytes);
|
||||
impl_word!(U11, u16, 11, TwoBytes);
|
||||
impl_word!(U12, u16, 12, TwoBytes);
|
||||
impl_word!(U13, u16, 13, TwoBytes);
|
||||
impl_word!(U14, u16, 14, TwoBytes);
|
||||
impl_word!(U15, u16, 15, TwoBytes);
|
||||
impl_word!(_, u16, 16, TwoBytes);
|
||||
impl_word!(U17, u32, 17, FourBytes);
|
||||
impl_word!(U18, u32, 18, FourBytes);
|
||||
impl_word!(U19, u32, 19, FourBytes);
|
||||
impl_word!(U20, u32, 20, FourBytes);
|
||||
impl_word!(U21, u32, 21, FourBytes);
|
||||
impl_word!(U22, u32, 22, FourBytes);
|
||||
impl_word!(U23, u32, 23, FourBytes);
|
||||
impl_word!(U24, u32, 24, FourBytes);
|
||||
impl_word!(U25, u32, 25, FourBytes);
|
||||
impl_word!(U26, u32, 26, FourBytes);
|
||||
impl_word!(U27, u32, 27, FourBytes);
|
||||
impl_word!(U28, u32, 28, FourBytes);
|
||||
impl_word!(U29, u32, 29, FourBytes);
|
||||
impl_word!(U30, u32, 30, FourBytes);
|
||||
impl_word!(U31, u32, 31, FourBytes);
|
||||
impl_word!(_, u32, 32, FourBytes);
|
@ -2,7 +2,7 @@ use atomic_polyfill::{fence, Ordering};
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
|
||||
use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
|
||||
use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE};
|
||||
use crate::flash::FlashBank;
|
||||
use crate::Peripheral;
|
||||
|
||||
@ -162,6 +162,35 @@ impl FlashRegion {
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_storage::nor_flash::ErrorType for Flash<'_> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> {
|
||||
const READ_SIZE: usize = 1;
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(offset, bytes)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
FLASH_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_storage::nor_flash::NorFlash for Flash<'_> {
|
||||
const WRITE_SIZE: usize = WRITE_SIZE;
|
||||
const ERASE_SIZE: usize = MAX_ERASE_SIZE;
|
||||
|
||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(offset, bytes)
|
||||
}
|
||||
|
||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||
self.blocking_erase(from, to)
|
||||
}
|
||||
}
|
||||
|
||||
foreach_flash_region! {
|
||||
($type_name:ident, $write_size:literal, $erase_size:literal) => {
|
||||
impl crate::_generated::flash_regions::$type_name<'_> {
|
||||
|
@ -7,6 +7,7 @@ mod common;
|
||||
pub use common::*;
|
||||
|
||||
pub use crate::_generated::flash_regions::*;
|
||||
pub use crate::_generated::MAX_ERASE_SIZE;
|
||||
pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,6 +1,5 @@
|
||||
use core::cmp;
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
@ -8,7 +7,7 @@ use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::dma::NoDma;
|
||||
use crate::dma::{NoDma, Transfer};
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Pull;
|
||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||
@ -35,14 +34,12 @@ impl Default for Config {
|
||||
|
||||
pub struct State {
|
||||
waker: AtomicWaker,
|
||||
chunks_transferred: AtomicUsize,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
chunks_transferred: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
let isr = regs.isr().read();
|
||||
|
||||
if isr.tcr() || isr.tc() {
|
||||
let state = T::state();
|
||||
let transferred = state.chunks_transferred.load(Ordering::Relaxed);
|
||||
state.chunks_transferred.store(transferred + 1, Ordering::Relaxed);
|
||||
state.waker.wake();
|
||||
T::state().waker.wake();
|
||||
}
|
||||
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||
// the interrupt
|
||||
@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
TXDMA: crate::i2c::TxDma<T>,
|
||||
{
|
||||
let total_len = write.len();
|
||||
let completed_chunks = total_len / 255;
|
||||
let total_chunks = if completed_chunks * 255 == total_len {
|
||||
completed_chunks
|
||||
} else {
|
||||
completed_chunks + 1
|
||||
};
|
||||
|
||||
let dma_transfer = unsafe {
|
||||
let regs = T::regs();
|
||||
@ -476,11 +464,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
let ch = &mut self.tx_dma;
|
||||
let request = ch.request();
|
||||
crate::dma::write(ch, request, write, dst)
|
||||
Transfer::new_write(ch, request, write, dst, Default::default())
|
||||
};
|
||||
|
||||
let state = T::state();
|
||||
state.chunks_transferred.store(0, Ordering::Relaxed);
|
||||
let mut remaining_len = total_len;
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
@ -495,33 +482,35 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
});
|
||||
|
||||
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||
if first_slice {
|
||||
unsafe {
|
||||
Self::master_write(
|
||||
address,
|
||||
total_len.min(255),
|
||||
Stop::Software,
|
||||
(total_chunks != 1) || !last_slice,
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?;
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
|
||||
|
||||
if chunks_transferred == total_chunks {
|
||||
let isr = unsafe { T::regs().isr().read() };
|
||||
if remaining_len == total_len {
|
||||
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||
if first_slice {
|
||||
unsafe {
|
||||
Self::master_write(
|
||||
address,
|
||||
total_len.min(255),
|
||||
Stop::Software,
|
||||
(total_len > 255) || !last_slice,
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?;
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
} else if !(isr.tcr() || isr.tc()) {
|
||||
// poll_fn was woken without an interrupt present
|
||||
return Poll::Pending;
|
||||
} else if remaining_len == 0 {
|
||||
return Poll::Ready(Ok(()));
|
||||
} else if chunks_transferred != 0 {
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
|
||||
} else {
|
||||
let last_piece = (remaining_len <= 255) && last_slice;
|
||||
|
||||
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
@ -531,6 +520,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
Poll::Pending
|
||||
})
|
||||
.await?;
|
||||
@ -559,12 +550,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
RXDMA: crate::i2c::RxDma<T>,
|
||||
{
|
||||
let total_len = buffer.len();
|
||||
let completed_chunks = total_len / 255;
|
||||
let total_chunks = if completed_chunks * 255 == total_len {
|
||||
completed_chunks
|
||||
} else {
|
||||
completed_chunks + 1
|
||||
};
|
||||
|
||||
let dma_transfer = unsafe {
|
||||
let regs = T::regs();
|
||||
@ -576,11 +561,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
|
||||
let ch = &mut self.rx_dma;
|
||||
let request = ch.request();
|
||||
crate::dma::read(ch, request, src, buffer)
|
||||
Transfer::new_read(ch, request, src, buffer, Default::default())
|
||||
};
|
||||
|
||||
let state = T::state();
|
||||
state.chunks_transferred.store(0, Ordering::Relaxed);
|
||||
let mut remaining_len = total_len;
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
@ -593,27 +577,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
});
|
||||
|
||||
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
Self::master_read(
|
||||
address,
|
||||
total_len.min(255),
|
||||
Stop::Software,
|
||||
total_chunks != 1,
|
||||
restart,
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
|
||||
|
||||
if chunks_transferred == total_chunks {
|
||||
let isr = unsafe { T::regs().isr().read() };
|
||||
if remaining_len == total_len {
|
||||
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
Self::master_read(
|
||||
address,
|
||||
total_len.min(255),
|
||||
Stop::Software,
|
||||
total_len > 255,
|
||||
restart,
|
||||
&check_timeout,
|
||||
)?;
|
||||
}
|
||||
} else if !(isr.tcr() || isr.tc()) {
|
||||
// poll_fn was woken without an interrupt present
|
||||
return Poll::Pending;
|
||||
} else if remaining_len == 0 {
|
||||
return Poll::Ready(Ok(()));
|
||||
} else if chunks_transferred != 0 {
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
let last_piece = chunks_transferred + 1 == total_chunks;
|
||||
} else {
|
||||
let last_piece = remaining_len <= 255;
|
||||
|
||||
// NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
|
||||
unsafe {
|
||||
@ -623,6 +609,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
}
|
||||
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
Poll::Pending
|
||||
})
|
||||
.await?;
|
||||
|
@ -49,11 +49,14 @@ pub mod pwm;
|
||||
pub mod qspi;
|
||||
#[cfg(rng)]
|
||||
pub mod rng;
|
||||
#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))]
|
||||
pub mod rtc;
|
||||
#[cfg(sdmmc)]
|
||||
pub mod sdmmc;
|
||||
#[cfg(spi)]
|
||||
pub mod spi;
|
||||
#[cfg(stm32wl)]
|
||||
#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
|
||||
pub mod subghz;
|
||||
#[cfg(usart)]
|
||||
pub mod usart;
|
||||
@ -76,7 +79,6 @@ pub(crate) mod _generated {
|
||||
// Reexports
|
||||
pub use _generated::{peripherals, Peripherals};
|
||||
pub use embassy_cortex_m::executor;
|
||||
#[cfg(any(dma, bdma))]
|
||||
use embassy_cortex_m::interrupt::Priority;
|
||||
pub use embassy_cortex_m::interrupt::_export::interrupt;
|
||||
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
@ -94,6 +96,8 @@ pub struct Config {
|
||||
pub bdma_interrupt_priority: Priority,
|
||||
#[cfg(dma)]
|
||||
pub dma_interrupt_priority: Priority,
|
||||
#[cfg(gpdma)]
|
||||
pub gpdma_interrupt_priority: Priority,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -106,6 +110,8 @@ impl Default for Config {
|
||||
bdma_interrupt_priority: Priority::P0,
|
||||
#[cfg(dma)]
|
||||
dma_interrupt_priority: Priority::P0,
|
||||
#[cfg(gpdma)]
|
||||
gpdma_interrupt_priority: Priority::P0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,6 +155,8 @@ pub fn init(config: Config) -> Peripherals {
|
||||
config.bdma_interrupt_priority,
|
||||
#[cfg(dma)]
|
||||
config.dma_interrupt_priority,
|
||||
#[cfg(gpdma)]
|
||||
config.gpdma_interrupt_priority,
|
||||
);
|
||||
#[cfg(feature = "exti")]
|
||||
exti::init();
|
||||
|
@ -5,7 +5,7 @@ pub mod enums;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use enums::*;
|
||||
|
||||
use crate::dma::TransferOptions;
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::pac::quadspi::Quadspi as Regs;
|
||||
@ -230,9 +230,6 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
unsafe {
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
let request = self.dma.request();
|
||||
let options = TransferOptions::default();
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
@ -241,12 +238,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
self.dma
|
||||
.start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options);
|
||||
let request = self.dma.request();
|
||||
let transfer = Transfer::new_read(
|
||||
&mut self.dma,
|
||||
request,
|
||||
T::REGS.dr().ptr() as *mut u8,
|
||||
buf,
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
while self.dma.is_running() {}
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,19 +260,22 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
unsafe {
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
let request = self.dma.request();
|
||||
let options = TransferOptions::default();
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
self.dma
|
||||
.start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options);
|
||||
let request = self.dma.request();
|
||||
let transfer = Transfer::new_write(
|
||||
&mut self.dma,
|
||||
request,
|
||||
buf,
|
||||
T::REGS.dr().ptr() as *mut u8,
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
while self.dma.is_running() {}
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,66 @@ pub struct Config {
|
||||
pub pclk1: Option<Hertz>,
|
||||
pub pclk2: Option<Hertz>,
|
||||
|
||||
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||
pub plli2s: Option<Hertz>,
|
||||
|
||||
pub pll48: bool,
|
||||
}
|
||||
|
||||
unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
|
||||
#[cfg(stm32f410)]
|
||||
unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
// Not currently implemented, but will be in the future
|
||||
#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
|
||||
unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||
unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
|
||||
let min_div = 2;
|
||||
let max_div = 7;
|
||||
let target = match plli2s {
|
||||
Some(target) => target,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// We loop through the possible divider values to find the best configuration. Looping
|
||||
// through all possible "N" values would result in more iterations.
|
||||
let (n, outdiv, output, _error) = (min_div..=max_div)
|
||||
.filter_map(|outdiv| {
|
||||
let target_vco_out = match target.checked_mul(outdiv) {
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
};
|
||||
let n = (target_vco_out + (vco_in >> 1)) / vco_in;
|
||||
let vco_out = vco_in * n;
|
||||
if !(100_000_000..=432_000_000).contains(&vco_out) {
|
||||
return None;
|
||||
}
|
||||
let output = vco_out / outdiv;
|
||||
let error = (output as i32 - target as i32).unsigned_abs();
|
||||
Some((n, outdiv, output, error))
|
||||
})
|
||||
.min_by_key(|(_, _, _, error)| *error)?;
|
||||
|
||||
RCC.plli2scfgr().modify(|w| {
|
||||
w.set_plli2sn(n as u16);
|
||||
w.set_plli2sr(outdiv as u8);
|
||||
});
|
||||
|
||||
Some(output)
|
||||
}
|
||||
|
||||
unsafe fn setup_pll(
|
||||
pllsrcclk: u32,
|
||||
use_hse: bool,
|
||||
pllsysclk: Option<u32>,
|
||||
plli2s: Option<u32>,
|
||||
pll48clk: bool,
|
||||
) -> PllResults {
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
@ -43,6 +99,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
|
||||
use_pll: false,
|
||||
pllsysclk: None,
|
||||
pll48clk: None,
|
||||
plli2sclk: None,
|
||||
};
|
||||
}
|
||||
// Input divisor from PLL source clock, must result to frequency in
|
||||
@ -101,6 +158,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
|
||||
use_pll: true,
|
||||
pllsysclk: Some(real_pllsysclk),
|
||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||
plli2sclk: setup_i2s_pll(vco_in, plli2s),
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,6 +344,10 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
pllsrcclk,
|
||||
config.hse.is_some(),
|
||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
||||
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
|
||||
config.plli2s.map(|i2s| i2s.0),
|
||||
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
|
||||
None,
|
||||
config.pll48,
|
||||
);
|
||||
|
||||
@ -376,6 +438,13 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
#[cfg(not(stm32f410))]
|
||||
if plls.plli2sclk.is_some() {
|
||||
RCC.cr().modify(|w| w.set_plli2son(true));
|
||||
|
||||
while !RCC.cr().read().plli2srdy() {}
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
@ -409,6 +478,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
|
||||
#[cfg(not(stm32f410))]
|
||||
plli2s: plls.plli2sclk.map(Hertz),
|
||||
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||
pllsai: None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -416,6 +491,8 @@ struct PllResults {
|
||||
use_pll: bool,
|
||||
pllsysclk: Option<u32>,
|
||||
pll48clk: Option<u32>,
|
||||
#[allow(dead_code)]
|
||||
plli2sclk: Option<u32>,
|
||||
}
|
||||
|
||||
mod max {
|
||||
|
@ -60,6 +60,12 @@ pub struct Clocks {
|
||||
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
|
||||
pub pll48: Option<Hertz>,
|
||||
|
||||
#[cfg(all(rcc_f4, not(stm32f410)))]
|
||||
pub plli2s: Option<Hertz>,
|
||||
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||
pub pllsai: Option<Hertz>,
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
pub adc: Hertz,
|
||||
|
||||
|
203
embassy-stm32/src/rtc/datetime.rs
Normal file
203
embassy-stm32/src/rtc/datetime.rs
Normal file
@ -0,0 +1,203 @@
|
||||
#[cfg(feature = "chrono")]
|
||||
use core::convert::From;
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
|
||||
|
||||
use super::byte_to_bcd2;
|
||||
use crate::pac::rtc::Rtc;
|
||||
|
||||
/// Errors regarding the [`DateTime`] struct.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
|
||||
InvalidMonth,
|
||||
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
|
||||
InvalidDay,
|
||||
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
|
||||
InvalidDayOfWeek(
|
||||
/// The value of the DayOfWeek that was given.
|
||||
u8,
|
||||
),
|
||||
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
|
||||
InvalidHour,
|
||||
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
|
||||
InvalidMinute,
|
||||
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
|
||||
InvalidSecond,
|
||||
}
|
||||
|
||||
/// Structure containing date and time information
|
||||
pub struct DateTime {
|
||||
/// 0..4095
|
||||
pub year: u16,
|
||||
/// 1..12, 1 is January
|
||||
pub month: u8,
|
||||
/// 1..28,29,30,31 depending on month
|
||||
pub day: u8,
|
||||
///
|
||||
pub day_of_week: DayOfWeek,
|
||||
/// 0..23
|
||||
pub hour: u8,
|
||||
/// 0..59
|
||||
pub minute: u8,
|
||||
/// 0..59
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl From<chrono::NaiveDateTime> for DateTime {
|
||||
fn from(date_time: chrono::NaiveDateTime) -> Self {
|
||||
Self {
|
||||
year: (date_time.year() - 1970) as u16,
|
||||
month: date_time.month() as u8,
|
||||
day: date_time.day() as u8,
|
||||
day_of_week: date_time.weekday().into(),
|
||||
hour: date_time.hour() as u8,
|
||||
minute: date_time.minute() as u8,
|
||||
second: date_time.second() as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl From<DateTime> for chrono::NaiveDateTime {
|
||||
fn from(date_time: DateTime) -> Self {
|
||||
NaiveDate::from_ymd_opt(
|
||||
(date_time.year + 1970) as i32,
|
||||
date_time.month as u32,
|
||||
date_time.day as u32,
|
||||
)
|
||||
.unwrap()
|
||||
.and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A day of the week
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DayOfWeek {
|
||||
Monday = 0,
|
||||
Tuesday = 1,
|
||||
Wednesday = 2,
|
||||
Thursday = 3,
|
||||
Friday = 4,
|
||||
Saturday = 5,
|
||||
Sunday = 6,
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl From<chrono::Weekday> for DayOfWeek {
|
||||
fn from(weekday: Weekday) -> Self {
|
||||
day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl From<DayOfWeek> for chrono::Weekday {
|
||||
fn from(weekday: DayOfWeek) -> Self {
|
||||
match weekday {
|
||||
DayOfWeek::Monday => Weekday::Mon,
|
||||
DayOfWeek::Tuesday => Weekday::Tue,
|
||||
DayOfWeek::Wednesday => Weekday::Wed,
|
||||
DayOfWeek::Thursday => Weekday::Thu,
|
||||
DayOfWeek::Friday => Weekday::Fri,
|
||||
DayOfWeek::Saturday => Weekday::Sat,
|
||||
DayOfWeek::Sunday => Weekday::Sun,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
|
||||
Ok(match v {
|
||||
0 => DayOfWeek::Monday,
|
||||
1 => DayOfWeek::Tuesday,
|
||||
2 => DayOfWeek::Wednesday,
|
||||
3 => DayOfWeek::Thursday,
|
||||
4 => DayOfWeek::Friday,
|
||||
5 => DayOfWeek::Saturday,
|
||||
6 => DayOfWeek::Sunday,
|
||||
x => return Err(Error::InvalidDayOfWeek(x)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw as u8
|
||||
}
|
||||
|
||||
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year > 4095 {
|
||||
Err(Error::InvalidYear)
|
||||
} else if dt.month < 1 || dt.month > 12 {
|
||||
Err(Error::InvalidMonth)
|
||||
} else if dt.day < 1 || dt.day > 31 {
|
||||
Err(Error::InvalidDay)
|
||||
} else if dt.hour > 23 {
|
||||
Err(Error::InvalidHour)
|
||||
} else if dt.minute > 59 {
|
||||
Err(Error::InvalidMinute)
|
||||
} else if dt.second > 59 {
|
||||
Err(Error::InvalidSecond)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
|
||||
let (ht, hu) = byte_to_bcd2(t.hour as u8);
|
||||
let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
|
||||
let (st, su) = byte_to_bcd2(t.second as u8);
|
||||
|
||||
let (dt, du) = byte_to_bcd2(t.day as u8);
|
||||
let (mt, mu) = byte_to_bcd2(t.month as u8);
|
||||
let yr = t.year as u16;
|
||||
let yr_offset = (yr - 1970_u16) as u8;
|
||||
let (yt, yu) = byte_to_bcd2(yr_offset);
|
||||
|
||||
unsafe {
|
||||
rtc.tr().write(|w| {
|
||||
w.set_ht(ht);
|
||||
w.set_hu(hu);
|
||||
w.set_mnt(mnt);
|
||||
w.set_mnu(mnu);
|
||||
w.set_st(st);
|
||||
w.set_su(su);
|
||||
w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
|
||||
});
|
||||
|
||||
rtc.dr().write(|w| {
|
||||
w.set_dt(dt);
|
||||
w.set_du(du);
|
||||
w.set_mt(mt > 0);
|
||||
w.set_mu(mu);
|
||||
w.set_yt(yt);
|
||||
w.set_yu(yu);
|
||||
w.set_wdu(day_of_week_to_u8(t.day_of_week));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn datetime(
|
||||
year: u16,
|
||||
month: u8,
|
||||
day: u8,
|
||||
day_of_week: u8,
|
||||
hour: u8,
|
||||
minute: u8,
|
||||
second: u8,
|
||||
) -> Result<DateTime, Error> {
|
||||
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
||||
Ok(DateTime {
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
day_of_week,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
})
|
||||
}
|
85
embassy-stm32/src/rtc/datetime_chrono.rs
Normal file
85
embassy-stm32/src/rtc/datetime_chrono.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use chrono::{Datelike, Timelike};
|
||||
|
||||
use super::byte_to_bcd2;
|
||||
use crate::pac::rtc::Rtc;
|
||||
|
||||
/// Alias for [`chrono::NaiveDateTime`]
|
||||
pub type DateTime = chrono::NaiveDateTime;
|
||||
/// Alias for [`chrono::Weekday`]
|
||||
pub type DayOfWeek = chrono::Weekday;
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid date.
|
||||
InvalidDate,
|
||||
/// The [DateTime] contains an invalid time.
|
||||
InvalidTime,
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw.num_days_from_monday() as u8
|
||||
}
|
||||
|
||||
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year() < 0 || dt.year() > 4095 {
|
||||
// rp2040 can't hold these years
|
||||
Err(Error::InvalidYear)
|
||||
} else {
|
||||
// The rest of the chrono date is assumed to be valid
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
|
||||
let (ht, hu) = byte_to_bcd2(t.hour() as u8);
|
||||
let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
|
||||
let (st, su) = byte_to_bcd2(t.second() as u8);
|
||||
|
||||
let (dt, du) = byte_to_bcd2(t.day() as u8);
|
||||
let (mt, mu) = byte_to_bcd2(t.month() as u8);
|
||||
let yr = t.year() as u16;
|
||||
let yr_offset = (yr - 1970_u16) as u8;
|
||||
let (yt, yu) = byte_to_bcd2(yr_offset);
|
||||
|
||||
unsafe {
|
||||
rtc.tr().write(|w| {
|
||||
w.set_ht(ht);
|
||||
w.set_hu(hu);
|
||||
w.set_mnt(mnt);
|
||||
w.set_mnu(mnu);
|
||||
w.set_st(st);
|
||||
w.set_su(su);
|
||||
w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
|
||||
});
|
||||
|
||||
rtc.dr().write(|w| {
|
||||
w.set_dt(dt);
|
||||
w.set_du(du);
|
||||
w.set_mt(mt > 0);
|
||||
w.set_mu(mu);
|
||||
w.set_yt(yt);
|
||||
w.set_yu(yu);
|
||||
w.set_wdu(day_of_week_to_u8(t.weekday()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn datetime(
|
||||
year: u16,
|
||||
month: u8,
|
||||
day: u8,
|
||||
_day_of_week: u8,
|
||||
hour: u8,
|
||||
minute: u8,
|
||||
second: u8,
|
||||
) -> Result<DateTime, Error> {
|
||||
let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into())
|
||||
.ok_or(Error::InvalidDate)?;
|
||||
let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?;
|
||||
Ok(DateTime::new(date, time))
|
||||
}
|
235
embassy-stm32/src/rtc/mod.rs
Normal file
235
embassy-stm32/src/rtc/mod.rs
Normal file
@ -0,0 +1,235 @@
|
||||
//! RTC peripheral abstraction
|
||||
use core::marker::PhantomData;
|
||||
mod datetime;
|
||||
|
||||
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||
|
||||
/// refer to AN4759 to compare features of RTC2 and RTC3
|
||||
#[cfg_attr(any(rtc_v1), path = "v1.rs")]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
|
||||
),
|
||||
path = "v2/mod.rs"
|
||||
)]
|
||||
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
|
||||
mod versions;
|
||||
use embassy_hal_common::Peripheral;
|
||||
pub use versions::*;
|
||||
|
||||
/// Errors that can occur on methods on [RtcClock]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RtcError {
|
||||
/// An invalid DateTime was given or stored on the hardware.
|
||||
InvalidDateTime(DateTimeError),
|
||||
|
||||
/// The RTC clock is not running
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
/// RTC Abstraction
|
||||
pub struct Rtc<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
rtc_config: RtcConfig,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum RtcClockSource {
|
||||
/// 00: No clock
|
||||
NoClock = 0b00,
|
||||
/// 01: LSE oscillator clock used as RTC clock
|
||||
LSE = 0b01,
|
||||
/// 10: LSI oscillator clock used as RTC clock
|
||||
LSI = 0b10,
|
||||
/// 11: HSE oscillator clock divided by 32 used as RTC clock
|
||||
HSE = 0b11,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct RtcConfig {
|
||||
/// RTC clock source
|
||||
clock_config: RtcClockSource,
|
||||
/// Asynchronous prescaler factor
|
||||
/// This is the asynchronous division factor:
|
||||
/// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
|
||||
/// ck_apre drives the subsecond register
|
||||
async_prescaler: u8,
|
||||
/// Synchronous prescaler factor
|
||||
/// This is the synchronous division factor:
|
||||
/// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
|
||||
/// ck_spre must be 1Hz
|
||||
sync_prescaler: u16,
|
||||
}
|
||||
|
||||
impl Default for RtcConfig {
|
||||
/// LSI with prescalers assuming 32.768 kHz.
|
||||
/// Raw sub-seconds in 1/256.
|
||||
fn default() -> Self {
|
||||
RtcConfig {
|
||||
clock_config: RtcClockSource::LSI,
|
||||
async_prescaler: 127,
|
||||
sync_prescaler: 255,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RtcConfig {
|
||||
/// Sets the clock source of RTC config
|
||||
pub fn clock_config(mut self, cfg: RtcClockSource) -> Self {
|
||||
self.clock_config = cfg;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the asynchronous prescaler of RTC config
|
||||
pub fn async_prescaler(mut self, prescaler: u8) -> Self {
|
||||
self.async_prescaler = prescaler;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the synchronous prescaler of RTC config
|
||||
pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
|
||||
self.sync_prescaler = prescaler;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum RtcCalibrationCyclePeriod {
|
||||
/// 8-second calibration period
|
||||
Seconds8,
|
||||
/// 16-second calibration period
|
||||
Seconds16,
|
||||
/// 32-second calibration period
|
||||
Seconds32,
|
||||
}
|
||||
|
||||
impl Default for RtcCalibrationCyclePeriod {
|
||||
fn default() -> Self {
|
||||
RtcCalibrationCyclePeriod::Seconds32
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Rtc<'d, T> {
|
||||
pub fn new(_rtc: impl Peripheral<P = T> + 'd, rtc_config: RtcConfig) -> Self {
|
||||
unsafe { enable_peripheral_clk() };
|
||||
|
||||
let mut rtc_struct = Self {
|
||||
phantom: PhantomData,
|
||||
rtc_config,
|
||||
};
|
||||
|
||||
rtc_struct.apply_config(rtc_config);
|
||||
|
||||
rtc_struct
|
||||
}
|
||||
|
||||
/// Set the datetime to a new value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
|
||||
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
|
||||
self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the current datetime.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
|
||||
pub fn now(&self) -> Result<DateTime, RtcError> {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let tr = r.tr().read();
|
||||
let second = bcd2_to_byte((tr.st(), tr.su()));
|
||||
let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
|
||||
let hour = bcd2_to_byte((tr.ht(), tr.hu()));
|
||||
// Reading either RTC_SSR or RTC_TR locks the values in the higher-order
|
||||
// calendar shadow registers until RTC_DR is read.
|
||||
let dr = r.dr().read();
|
||||
|
||||
let weekday = dr.wdu();
|
||||
let day = bcd2_to_byte((dr.dt(), dr.du()));
|
||||
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
|
||||
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
|
||||
|
||||
self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if daylight savings time is active.
|
||||
pub fn get_daylight_savings(&self) -> bool {
|
||||
let cr = unsafe { T::regs().cr().read() };
|
||||
cr.bkp()
|
||||
}
|
||||
|
||||
/// Enable/disable daylight savings time.
|
||||
pub fn set_daylight_savings(&mut self, daylight_savings: bool) {
|
||||
self.write(true, |rtc| {
|
||||
unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) };
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_config(&self) -> RtcConfig {
|
||||
self.rtc_config
|
||||
}
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT;
|
||||
|
||||
/// Read content of the backup register.
|
||||
///
|
||||
/// The registers retain their values during wakes from standby mode or system resets. They also
|
||||
/// retain their value when Vdd is switched off as long as V_BAT is powered.
|
||||
pub fn read_backup_register(&self, register: usize) -> Option<u32> {
|
||||
read_backup_register(&T::regs(), register)
|
||||
}
|
||||
|
||||
/// Set content of the backup register.
|
||||
///
|
||||
/// The registers retain their values during wakes from standby mode or system resets. They also
|
||||
/// retain their value when Vdd is switched off as long as V_BAT is powered.
|
||||
pub fn write_backup_register(&self, register: usize, value: u32) {
|
||||
write_backup_register(&T::regs(), register, value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {
|
||||
let mut bcd_high: u8 = 0;
|
||||
let mut value = byte;
|
||||
|
||||
while value >= 10 {
|
||||
bcd_high += 1;
|
||||
value -= 10;
|
||||
}
|
||||
|
||||
(bcd_high, ((bcd_high << 4) | value) as u8)
|
||||
}
|
||||
|
||||
pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {
|
||||
let value = bcd.1 | bcd.0 << 4;
|
||||
|
||||
let tmp = ((value & 0xF0) >> 0x4) * 10;
|
||||
|
||||
tmp + (value & 0x0F)
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> crate::pac::rtc::Rtc;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
|
||||
impl sealed::Instance for crate::peripherals::RTC {
|
||||
fn regs() -> crate::pac::rtc::Rtc {
|
||||
crate::pac::RTC
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for crate::peripherals::RTC {}
|
171
embassy-stm32/src/rtc/v2/mod.rs
Normal file
171
embassy-stm32/src/rtc/v2/mod.rs
Normal file
@ -0,0 +1,171 @@
|
||||
use stm32_metapac::rtc::vals::{Init, Osel, Pol};
|
||||
|
||||
use super::{Instance, RtcConfig};
|
||||
use crate::pac::rtc::Rtc;
|
||||
|
||||
#[cfg_attr(rtc_v2f0, path = "v2f0.rs")]
|
||||
#[cfg_attr(rtc_v2f2, path = "v2f2.rs")]
|
||||
#[cfg_attr(rtc_v2f3, path = "v2f3.rs")]
|
||||
#[cfg_attr(rtc_v2f4, path = "v2f4.rs")]
|
||||
#[cfg_attr(rtc_v2f7, path = "v2f7.rs")]
|
||||
#[cfg_attr(rtc_v2h7, path = "v2h7.rs")]
|
||||
#[cfg_attr(rtc_v2l0, path = "v2l0.rs")]
|
||||
#[cfg_attr(rtc_v2l1, path = "v2l1.rs")]
|
||||
#[cfg_attr(rtc_v2l4, path = "v2l4.rs")]
|
||||
#[cfg_attr(rtc_v2wb, path = "v2wb.rs")]
|
||||
mod family;
|
||||
|
||||
pub use family::*;
|
||||
|
||||
impl<'d, T: Instance> super::Rtc<'d, T> {
|
||||
/// Applies the RTC config
|
||||
/// It this changes the RTC clock source the time will be reset
|
||||
pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
|
||||
// Unlock the backup domain
|
||||
unsafe {
|
||||
unlock_backup_domain(rtc_config.clock_config as u8);
|
||||
}
|
||||
|
||||
self.write(true, |rtc| unsafe {
|
||||
rtc.cr().modify(|w| {
|
||||
#[cfg(rtc_v2f2)]
|
||||
w.set_fmt(false);
|
||||
#[cfg(not(rtc_v2f2))]
|
||||
w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
|
||||
w.set_osel(Osel::DISABLED);
|
||||
w.set_pol(Pol::HIGH);
|
||||
});
|
||||
|
||||
rtc.prer().modify(|w| {
|
||||
w.set_prediv_s(rtc_config.sync_prescaler);
|
||||
w.set_prediv_a(rtc_config.async_prescaler);
|
||||
});
|
||||
});
|
||||
|
||||
self.rtc_config = rtc_config;
|
||||
}
|
||||
|
||||
/// Calibrate the clock drift.
|
||||
///
|
||||
/// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
|
||||
/// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
|
||||
#[cfg(not(rtc_v2f2))]
|
||||
pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {
|
||||
const RTC_CALR_MIN_PPM: f32 = -487.1;
|
||||
const RTC_CALR_MAX_PPM: f32 = 488.5;
|
||||
const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
|
||||
|
||||
if clock_drift < RTC_CALR_MIN_PPM {
|
||||
clock_drift = RTC_CALR_MIN_PPM;
|
||||
} else if clock_drift > RTC_CALR_MAX_PPM {
|
||||
clock_drift = RTC_CALR_MAX_PPM;
|
||||
}
|
||||
|
||||
clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
|
||||
|
||||
self.write(false, |rtc| {
|
||||
unsafe {
|
||||
rtc.calr().write(|w| {
|
||||
match period {
|
||||
super::RtcCalibrationCyclePeriod::Seconds8 => {
|
||||
w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
|
||||
}
|
||||
super::RtcCalibrationCyclePeriod::Seconds16 => {
|
||||
w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
|
||||
}
|
||||
super::RtcCalibrationCyclePeriod::Seconds32 => {
|
||||
// Set neither `calw8` nor `calw16` to use 32 seconds
|
||||
}
|
||||
}
|
||||
|
||||
// Extra pulses during calibration cycle period: CALP * 512 - CALM
|
||||
//
|
||||
// CALP sets whether pulses are added or omitted.
|
||||
//
|
||||
// CALM contains how many pulses (out of 512) are masked in a
|
||||
// given calibration cycle period.
|
||||
if clock_drift > 0.0 {
|
||||
// Maximum (about 512.2) rounds to 512.
|
||||
clock_drift += 0.5;
|
||||
|
||||
// When the offset is positive (0 to 512), the opposite of
|
||||
// the offset (512 - offset) is masked, i.e. for the
|
||||
// maximum offset (512), 0 pulses are masked.
|
||||
w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
|
||||
w.set_calm(512 - clock_drift as u16);
|
||||
} else {
|
||||
// Minimum (about -510.7) rounds to -511.
|
||||
clock_drift -= 0.5;
|
||||
|
||||
// When the offset is negative or zero (-511 to 0),
|
||||
// the absolute offset is masked, i.e. for the minimum
|
||||
// offset (-511), 511 pulses are masked.
|
||||
w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
|
||||
w.set_calm((clock_drift * -1.0) as u16);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
|
||||
{
|
||||
let r = T::regs();
|
||||
// Disable write protection.
|
||||
// This is safe, as we're only writin the correct and expected values.
|
||||
unsafe {
|
||||
r.wpr().write(|w| w.set_key(0xca));
|
||||
r.wpr().write(|w| w.set_key(0x53));
|
||||
|
||||
// true if initf bit indicates RTC peripheral is in init mode
|
||||
if init_mode && !r.isr().read().initf() {
|
||||
// to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
|
||||
r.isr().modify(|w| w.set_init(Init::INITMODE));
|
||||
// wait till init state entered
|
||||
// ~2 RTCCLK cycles
|
||||
while !r.isr().read().initf() {}
|
||||
}
|
||||
}
|
||||
|
||||
let result = f(&r);
|
||||
|
||||
unsafe {
|
||||
if init_mode {
|
||||
r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
|
||||
}
|
||||
|
||||
// Re-enable write protection.
|
||||
// This is safe, as the field accepts the full range of 8-bit values.
|
||||
r.wpr().write(|w| w.set_key(0xff));
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Read content of the backup register.
|
||||
///
|
||||
/// The registers retain their values during wakes from standby mode or system resets. They also
|
||||
/// retain their value when Vdd is switched off as long as V_BAT is powered.
|
||||
pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
|
||||
if register < BACKUP_REGISTER_COUNT {
|
||||
Some(unsafe { rtc.bkpr(register).read().bkp() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Set content of the backup register.
|
||||
///
|
||||
/// The registers retain their values during wakes from standby mode or system resets. They also
|
||||
/// retain their value when Vdd is switched off as long as V_BAT is powered.
|
||||
pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
|
||||
if register < BACKUP_REGISTER_COUNT {
|
||||
unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) }
|
||||
}
|
||||
}
|
41
embassy-stm32/src/rtc/v2/v2f0.rs
Normal file
41
embassy-stm32/src/rtc/v2/v2f0.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
// Restore bcdr
|
||||
w.set_lscosel(reg.lscosel());
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
31
embassy-stm32/src/rtc/v2/v2f2.rs
Normal file
31
embassy-stm32/src/rtc/v2/v2f2.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
31
embassy-stm32/src/rtc/v2/v2f3.rs
Normal file
31
embassy-stm32/src/rtc/v2/v2f3.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
31
embassy-stm32/src/rtc/v2/v2f4.rs
Normal file
31
embassy-stm32/src/rtc/v2/v2f4.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
41
embassy-stm32/src/rtc/v2/v2f7.rs
Normal file
41
embassy-stm32/src/rtc/v2/v2f7.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
// Restore bcdr
|
||||
w.set_lscosel(reg.lscosel());
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
33
embassy-stm32/src/rtc/v2/v2h7.rs
Normal file
33
embassy-stm32/src/rtc/v2/v2h7.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
26
embassy-stm32/src/rtc/v2/v2l0.rs
Normal file
26
embassy-stm32/src/rtc/v2/v2l0.rs
Normal file
@ -0,0 +1,26 @@
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
// TODO: Missing from PAC?
|
||||
// crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
|
||||
// while !crate::pac::PWR.cr().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.csr().read();
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.csr().modify(|w| {
|
||||
// Select RTC source
|
||||
w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
24
embassy-stm32/src/rtc/v2/v2l1.rs
Normal file
24
embassy-stm32/src/rtc/v2/v2l1.rs
Normal file
@ -0,0 +1,24 @@
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.csr().read();
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.csr().modify(|w| {
|
||||
// Select RTC source
|
||||
w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
41
embassy-stm32/src/rtc/v2/v2l4.rs
Normal file
41
embassy-stm32/src/rtc/v2/v2l4.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use stm32_metapac::rcc::vals::Rtcsel;
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel().0 != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(Rtcsel(clock_config));
|
||||
w.set_rtcen(true);
|
||||
|
||||
// Restore bcdr
|
||||
w.set_lscosel(reg.lscosel());
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
39
embassy-stm32/src/rtc/v2/v2wb.rs
Normal file
39
embassy-stm32/src/rtc/v2/v2wb.rs
Normal file
@ -0,0 +1,39 @@
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 20;
|
||||
|
||||
/// Unlock the backup domain
|
||||
pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel() != clock_config {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(clock_config);
|
||||
w.set_rtcen(true);
|
||||
|
||||
// Restore bcdr
|
||||
w.set_lscosel(reg.lscosel());
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_peripheral_clk() {
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
226
embassy-stm32/src/rtc/v3.rs
Normal file
226
embassy-stm32/src/rtc/v3.rs
Normal file
@ -0,0 +1,226 @@
|
||||
use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
|
||||
|
||||
use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig};
|
||||
use crate::pac::rtc::Rtc;
|
||||
|
||||
impl<'d, T: Instance> super::Rtc<'d, T> {
|
||||
/// Applies the RTC config
|
||||
/// It this changes the RTC clock source the time will be reset
|
||||
pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
|
||||
// Unlock the backup domain
|
||||
unsafe {
|
||||
#[cfg(feature = "stm32g0c1ve")]
|
||||
{
|
||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "stm32g0c1ve",
|
||||
feature = "stm32g491re",
|
||||
feature = "stm32u585zi",
|
||||
feature = "stm32g473cc"
|
||||
)))]
|
||||
{
|
||||
crate::pac::PWR
|
||||
.cr1()
|
||||
.modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED));
|
||||
while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {}
|
||||
}
|
||||
|
||||
let reg = crate::pac::RCC.bdcr().read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
let config_rtcsel = rtc_config.clock_config as u8;
|
||||
#[cfg(not(any(
|
||||
feature = "stm32wl54jc-cm0p",
|
||||
feature = "stm32wle5ub",
|
||||
feature = "stm32g0c1ve",
|
||||
feature = "stm32wl55jc-cm4",
|
||||
feature = "stm32wl55uc-cm4",
|
||||
feature = "stm32g491re",
|
||||
feature = "stm32g473cc",
|
||||
feature = "stm32u585zi",
|
||||
feature = "stm32wle5jb"
|
||||
)))]
|
||||
let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
|
||||
#[cfg(feature = "stm32g0c1ve")]
|
||||
let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel);
|
||||
|
||||
if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
|
||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
||||
|
||||
crate::pac::RCC.bdcr().modify(|w| {
|
||||
// Reset
|
||||
w.set_bdrst(false);
|
||||
|
||||
// Select RTC source
|
||||
w.set_rtcsel(config_rtcsel);
|
||||
|
||||
w.set_rtcen(true);
|
||||
|
||||
// Restore bcdr
|
||||
w.set_lscosel(reg.lscosel());
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.write(true, |rtc| {
|
||||
unsafe {
|
||||
rtc.cr().modify(|w| {
|
||||
w.set_fmt(Fmt::TWENTYFOURHOUR);
|
||||
w.set_osel(Osel::DISABLED);
|
||||
w.set_pol(Pol::HIGH);
|
||||
});
|
||||
|
||||
rtc.prer().modify(|w| {
|
||||
w.set_prediv_s(rtc_config.sync_prescaler);
|
||||
w.set_prediv_a(rtc_config.async_prescaler);
|
||||
});
|
||||
|
||||
// TODO: configuration for output pins
|
||||
rtc.cr().modify(|w| {
|
||||
w.set_out2en(false);
|
||||
w.set_tampalrm_type(TampalrmType::PUSHPULL);
|
||||
w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.rtc_config = rtc_config;
|
||||
}
|
||||
|
||||
const RTC_CALR_MIN_PPM: f32 = -487.1;
|
||||
const RTC_CALR_MAX_PPM: f32 = 488.5;
|
||||
const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
|
||||
|
||||
/// Calibrate the clock drift.
|
||||
///
|
||||
/// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
|
||||
/// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
|
||||
pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {
|
||||
if clock_drift < Self::RTC_CALR_MIN_PPM {
|
||||
clock_drift = Self::RTC_CALR_MIN_PPM;
|
||||
} else if clock_drift > Self::RTC_CALR_MAX_PPM {
|
||||
clock_drift = Self::RTC_CALR_MAX_PPM;
|
||||
}
|
||||
|
||||
clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
|
||||
|
||||
self.write(false, |rtc| {
|
||||
unsafe {
|
||||
rtc.calr().write(|w| {
|
||||
match period {
|
||||
RtcCalibrationCyclePeriod::Seconds8 => {
|
||||
w.set_calw8(Calw8::EIGHTSECONDS);
|
||||
}
|
||||
RtcCalibrationCyclePeriod::Seconds16 => {
|
||||
w.set_calw16(Calw16::SIXTEENSECONDS);
|
||||
}
|
||||
RtcCalibrationCyclePeriod::Seconds32 => {
|
||||
// Set neither `calw8` nor `calw16` to use 32 seconds
|
||||
}
|
||||
}
|
||||
|
||||
// Extra pulses during calibration cycle period: CALP * 512 - CALM
|
||||
//
|
||||
// CALP sets whether pulses are added or omitted.
|
||||
//
|
||||
// CALM contains how many pulses (out of 512) are masked in a
|
||||
// given calibration cycle period.
|
||||
if clock_drift > 0.0 {
|
||||
// Maximum (about 512.2) rounds to 512.
|
||||
clock_drift += 0.5;
|
||||
|
||||
// When the offset is positive (0 to 512), the opposite of
|
||||
// the offset (512 - offset) is masked, i.e. for the
|
||||
// maximum offset (512), 0 pulses are masked.
|
||||
w.set_calp(Calp::INCREASEFREQ);
|
||||
w.set_calm(512 - clock_drift as u16);
|
||||
} else {
|
||||
// Minimum (about -510.7) rounds to -511.
|
||||
clock_drift -= 0.5;
|
||||
|
||||
// When the offset is negative or zero (-511 to 0),
|
||||
// the absolute offset is masked, i.e. for the minimum
|
||||
// offset (-511), 511 pulses are masked.
|
||||
w.set_calp(Calp::NOCHANGE);
|
||||
w.set_calm((clock_drift * -1.0) as u16);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
|
||||
{
|
||||
let r = T::regs();
|
||||
// Disable write protection.
|
||||
// This is safe, as we're only writin the correct and expected values.
|
||||
unsafe {
|
||||
r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
|
||||
r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
|
||||
|
||||
if init_mode && !r.icsr().read().initf() {
|
||||
r.icsr().modify(|w| w.set_init(Init::INITMODE));
|
||||
// wait till init state entered
|
||||
// ~2 RTCCLK cycles
|
||||
while !r.icsr().read().initf() {}
|
||||
}
|
||||
}
|
||||
|
||||
let result = f(&r);
|
||||
|
||||
unsafe {
|
||||
if init_mode {
|
||||
r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
|
||||
}
|
||||
|
||||
// Re-enable write protection.
|
||||
// This is safe, as the field accepts the full range of 8-bit values.
|
||||
r.wpr().write(|w| w.set_key(Key::ACTIVATE));
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn enable_peripheral_clk() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = 32;
|
||||
|
||||
/// Read content of the backup register.
|
||||
///
|
||||
/// The registers retain their values during wakes from standby mode or system resets. They also
|
||||
/// retain their value when Vdd is switched off as long as V_BAT is powered.
|
||||
pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
|
||||
if register < BACKUP_REGISTER_COUNT {
|
||||
//Some(rtc.bkpr()[register].read().bits())
|
||||
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Set content of the backup register.
|
||||
///
|
||||
/// The registers retain their values during wakes from standby mode or system resets. They also
|
||||
/// retain their value when Vdd is switched off as long as V_BAT is powered.
|
||||
pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
|
||||
if register < BACKUP_REGISTER_COUNT {
|
||||
// RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
|
||||
//unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,7 @@ use embassy_futures::join::join;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
|
||||
use self::sealed::WordSize;
|
||||
use crate::dma::{slice_ptr_parts, Transfer};
|
||||
use crate::dma::{slice_ptr_parts, word, Transfer};
|
||||
use crate::gpio::sealed::{AFType, Pin as _};
|
||||
use crate::gpio::{AnyPin, Pull};
|
||||
use crate::pac::spi::{regs, vals, Spi as Regs};
|
||||
@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> {
|
||||
miso: Option<PeripheralRef<'d, AnyPin>>,
|
||||
txdma: PeripheralRef<'d, Tx>,
|
||||
rxdma: PeripheralRef<'d, Rx>,
|
||||
current_word_size: WordSize,
|
||||
current_word_size: word_impl::Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
@ -178,10 +177,27 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_txonly_nosck(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T>> + 'd,
|
||||
txdma: impl Peripheral<P = Tx> + 'd,
|
||||
rxdma: impl Peripheral<P = Rx> + 'd, // TODO: remove
|
||||
freq: Hertz,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(mosi);
|
||||
unsafe {
|
||||
mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down);
|
||||
mosi.set_speed(crate::gpio::Speed::Medium);
|
||||
}
|
||||
|
||||
Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config)
|
||||
}
|
||||
|
||||
/// Useful for on chip peripherals like SUBGHZ which are hardwired.
|
||||
/// The bus can optionally be exposed externally with `Spi::new()` still.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new_internal(
|
||||
pub fn new_subghz(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
txdma: impl Peripheral<P = Tx> + 'd,
|
||||
rxdma: impl Peripheral<P = Rx> + 'd,
|
||||
@ -234,14 +250,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
if mosi.is_none() {
|
||||
w.set_rxonly(vals::Rxonly::OUTPUTDISABLED);
|
||||
}
|
||||
w.set_dff(WordSize::EightBit.dff())
|
||||
w.set_dff(<u8 as sealed::Word>::CONFIG)
|
||||
});
|
||||
}
|
||||
#[cfg(spi_v2)]
|
||||
unsafe {
|
||||
T::REGS.cr2().modify(|w| {
|
||||
w.set_frxth(WordSize::EightBit.frxth());
|
||||
w.set_ds(WordSize::EightBit.ds());
|
||||
let (ds, frxth) = <u8 as sealed::Word>::CONFIG;
|
||||
w.set_frxth(frxth);
|
||||
w.set_ds(ds);
|
||||
w.set_ssoe(false);
|
||||
});
|
||||
T::REGS.cr1().modify(|w| {
|
||||
@ -279,7 +296,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
T::REGS.cfg1().modify(|w| {
|
||||
w.set_crcen(false);
|
||||
w.set_mbr(br);
|
||||
w.set_dsize(WordSize::EightBit.dsize());
|
||||
w.set_dsize(<u8 as sealed::Word>::CONFIG);
|
||||
w.set_fthlv(vals::Fthlv::ONEFRAME);
|
||||
});
|
||||
T::REGS.cr2().modify(|w| {
|
||||
w.set_tsize(0);
|
||||
@ -297,7 +315,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
miso,
|
||||
txdma,
|
||||
rxdma,
|
||||
current_word_size: WordSize::EightBit,
|
||||
current_word_size: <u8 as sealed::Word>::CONFIG,
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +373,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_word_size(&mut self, word_size: WordSize) {
|
||||
fn set_word_size(&mut self, word_size: word_impl::Config) {
|
||||
if self.current_word_size == word_size {
|
||||
return;
|
||||
}
|
||||
@ -364,7 +382,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
unsafe {
|
||||
T::REGS.cr1().modify(|reg| {
|
||||
reg.set_spe(false);
|
||||
reg.set_dff(word_size.dff())
|
||||
reg.set_dff(word_size)
|
||||
});
|
||||
T::REGS.cr1().modify(|reg| {
|
||||
reg.set_spe(true);
|
||||
@ -376,8 +394,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
w.set_spe(false);
|
||||
});
|
||||
T::REGS.cr2().modify(|w| {
|
||||
w.set_frxth(word_size.frxth());
|
||||
w.set_ds(word_size.ds());
|
||||
w.set_frxth(word_size.1);
|
||||
w.set_ds(word_size.0);
|
||||
});
|
||||
T::REGS.cr1().modify(|w| {
|
||||
w.set_spe(true);
|
||||
@ -393,7 +411,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
w.set_spe(false);
|
||||
});
|
||||
T::REGS.cfg1().modify(|w| {
|
||||
w.set_dsize(word_size.dsize());
|
||||
w.set_dsize(word_size);
|
||||
});
|
||||
T::REGS.cr1().modify(|w| {
|
||||
w.set_csusp(false);
|
||||
@ -412,7 +430,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
unsafe {
|
||||
T::REGS.cr1().modify(|w| {
|
||||
w.set_spe(false);
|
||||
@ -421,8 +439,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
|
||||
let tx_request = self.txdma.request();
|
||||
let tx_dst = T::REGS.tx_ptr();
|
||||
unsafe { self.txdma.start_write(tx_request, data, tx_dst, Default::default()) }
|
||||
let tx_f = Transfer::new(&mut self.txdma);
|
||||
let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) };
|
||||
|
||||
unsafe {
|
||||
set_txdmaen(T::REGS, true);
|
||||
@ -451,7 +468,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
unsafe {
|
||||
T::REGS.cr1().modify(|w| {
|
||||
w.set_spe(false);
|
||||
@ -468,13 +485,21 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
|
||||
let rx_request = self.rxdma.request();
|
||||
let rx_src = T::REGS.rx_ptr();
|
||||
unsafe { self.rxdma.start_read(rx_request, rx_src, data, Default::default()) };
|
||||
let rx_f = Transfer::new(&mut self.rxdma);
|
||||
let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) };
|
||||
|
||||
let tx_request = self.txdma.request();
|
||||
let tx_dst = T::REGS.tx_ptr();
|
||||
let clock_byte = 0x00u8;
|
||||
let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst);
|
||||
let tx_f = unsafe {
|
||||
Transfer::new_write_repeated(
|
||||
&mut self.txdma,
|
||||
tx_request,
|
||||
&clock_byte,
|
||||
clock_byte_count,
|
||||
tx_dst,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
set_txdmaen(T::REGS, true);
|
||||
@ -506,7 +531,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
unsafe {
|
||||
T::REGS.cr1().modify(|w| {
|
||||
w.set_spe(false);
|
||||
@ -521,13 +546,11 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
|
||||
let rx_request = self.rxdma.request();
|
||||
let rx_src = T::REGS.rx_ptr();
|
||||
unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) };
|
||||
let rx_f = Transfer::new(&mut self.rxdma);
|
||||
let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) };
|
||||
|
||||
let tx_request = self.txdma.request();
|
||||
let tx_dst = T::REGS.tx_ptr();
|
||||
unsafe { self.txdma.start_write(tx_request, write, tx_dst, Default::default()) }
|
||||
let tx_f = Transfer::new(&mut self.txdma);
|
||||
let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) };
|
||||
|
||||
unsafe {
|
||||
set_txdmaen(T::REGS, true);
|
||||
@ -566,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
|
||||
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
|
||||
flush_rx_fifo(T::REGS);
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
for word in words.iter() {
|
||||
let _ = transfer_word(T::REGS, *word)?;
|
||||
}
|
||||
@ -576,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
||||
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
|
||||
flush_rx_fifo(T::REGS);
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
for word in words.iter_mut() {
|
||||
*word = transfer_word(T::REGS, W::default())?;
|
||||
}
|
||||
@ -586,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
|
||||
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
|
||||
flush_rx_fifo(T::REGS);
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
for word in words.iter_mut() {
|
||||
*word = transfer_word(T::REGS, *word)?;
|
||||
}
|
||||
@ -596,7 +619,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
|
||||
pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
|
||||
unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
|
||||
flush_rx_fifo(T::REGS);
|
||||
self.set_word_size(W::WORDSIZE);
|
||||
self.set_word_size(W::CONFIG);
|
||||
let len = read.len().max(write.len());
|
||||
for i in 0..len {
|
||||
let wb = write.get(i).copied().unwrap_or_default();
|
||||
@ -928,70 +951,89 @@ pub(crate) mod sealed {
|
||||
const REGS: Regs;
|
||||
}
|
||||
|
||||
pub trait Word: Copy + 'static {
|
||||
const WORDSIZE: WordSize;
|
||||
}
|
||||
|
||||
impl Word for u8 {
|
||||
const WORDSIZE: WordSize = WordSize::EightBit;
|
||||
}
|
||||
impl Word for u16 {
|
||||
const WORDSIZE: WordSize = WordSize::SixteenBit;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
||||
pub enum WordSize {
|
||||
EightBit,
|
||||
SixteenBit,
|
||||
}
|
||||
|
||||
impl WordSize {
|
||||
#[cfg(any(spi_v1, spi_f1))]
|
||||
pub fn dff(&self) -> vals::Dff {
|
||||
match self {
|
||||
WordSize::EightBit => vals::Dff::EIGHTBIT,
|
||||
WordSize::SixteenBit => vals::Dff::SIXTEENBIT,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(spi_v2)]
|
||||
pub fn ds(&self) -> vals::Ds {
|
||||
match self {
|
||||
WordSize::EightBit => vals::Ds::EIGHTBIT,
|
||||
WordSize::SixteenBit => vals::Ds::SIXTEENBIT,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(spi_v2)]
|
||||
pub fn frxth(&self) -> vals::Frxth {
|
||||
match self {
|
||||
WordSize::EightBit => vals::Frxth::QUARTER,
|
||||
WordSize::SixteenBit => vals::Frxth::HALF,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||
pub fn dsize(&self) -> u8 {
|
||||
match self {
|
||||
WordSize::EightBit => 0b0111,
|
||||
WordSize::SixteenBit => 0b1111,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||
pub fn _frxth(&self) -> vals::Fthlv {
|
||||
match self {
|
||||
WordSize::EightBit => vals::Fthlv::ONEFRAME,
|
||||
WordSize::SixteenBit => vals::Fthlv::ONEFRAME,
|
||||
}
|
||||
}
|
||||
pub trait Word {
|
||||
const CONFIG: word_impl::Config;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {}
|
||||
pub trait Word: word::Word + sealed::Word {}
|
||||
|
||||
impl Word for u8 {}
|
||||
impl Word for u16 {}
|
||||
macro_rules! impl_word {
|
||||
($T:ty, $config:expr) => {
|
||||
impl sealed::Word for $T {
|
||||
const CONFIG: Config = $config;
|
||||
}
|
||||
impl Word for $T {}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(any(spi_v1, spi_f1))]
|
||||
mod word_impl {
|
||||
use super::*;
|
||||
|
||||
pub type Config = vals::Dff;
|
||||
|
||||
impl_word!(u8, vals::Dff::EIGHTBIT);
|
||||
impl_word!(u16, vals::Dff::SIXTEENBIT);
|
||||
}
|
||||
|
||||
#[cfg(any(spi_v2))]
|
||||
mod word_impl {
|
||||
use super::*;
|
||||
|
||||
pub type Config = (vals::Ds, vals::Frxth);
|
||||
|
||||
impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER));
|
||||
impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER));
|
||||
impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER));
|
||||
impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER));
|
||||
impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER));
|
||||
impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF));
|
||||
impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF));
|
||||
impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF));
|
||||
impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF));
|
||||
impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF));
|
||||
impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF));
|
||||
impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF));
|
||||
impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF));
|
||||
}
|
||||
|
||||
#[cfg(any(spi_v3, spi_v4, spi_v5))]
|
||||
mod word_impl {
|
||||
use super::*;
|
||||
|
||||
pub type Config = u8;
|
||||
|
||||
impl_word!(word::U4, 4 - 1);
|
||||
impl_word!(word::U5, 5 - 1);
|
||||
impl_word!(word::U6, 6 - 1);
|
||||
impl_word!(word::U7, 7 - 1);
|
||||
impl_word!(u8, 8 - 1);
|
||||
impl_word!(word::U9, 9 - 1);
|
||||
impl_word!(word::U10, 10 - 1);
|
||||
impl_word!(word::U11, 11 - 1);
|
||||
impl_word!(word::U12, 12 - 1);
|
||||
impl_word!(word::U13, 13 - 1);
|
||||
impl_word!(word::U14, 14 - 1);
|
||||
impl_word!(word::U15, 15 - 1);
|
||||
impl_word!(u16, 16 - 1);
|
||||
impl_word!(word::U17, 17 - 1);
|
||||
impl_word!(word::U18, 18 - 1);
|
||||
impl_word!(word::U19, 19 - 1);
|
||||
impl_word!(word::U20, 20 - 1);
|
||||
impl_word!(word::U21, 21 - 1);
|
||||
impl_word!(word::U22, 22 - 1);
|
||||
impl_word!(word::U23, 23 - 1);
|
||||
impl_word!(word::U24, 24 - 1);
|
||||
impl_word!(word::U25, 25 - 1);
|
||||
impl_word!(word::U26, 26 - 1);
|
||||
impl_word!(word::U27, 27 - 1);
|
||||
impl_word!(word::U28, 28 - 1);
|
||||
impl_word!(word::U29, 29 - 1);
|
||||
impl_word!(word::U30, 30 - 1);
|
||||
impl_word!(word::U31, 31 - 1);
|
||||
impl_word!(u32, 32 - 1);
|
||||
}
|
||||
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||
pin_trait!(SckPin, Instance);
|
||||
|
@ -224,7 +224,7 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> {
|
||||
let mut config = SpiConfig::default();
|
||||
config.mode = MODE_0;
|
||||
config.bit_order = BitOrder::MsbFirst;
|
||||
let spi = Spi::new_internal(peri, txdma, rxdma, clk, config);
|
||||
let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config);
|
||||
|
||||
unsafe { wakeup() };
|
||||
|
||||
|
@ -34,7 +34,7 @@ macro_rules! dma_trait_impl {
|
||||
(crate::$mod:ident::$trait:ident, $instance:ident, {dmamux: $dmamux:ident}, $request:expr) => {
|
||||
impl<T> crate::$mod::$trait<crate::peripherals::$instance> for T
|
||||
where
|
||||
T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
|
||||
T: crate::dma::Channel + crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
|
||||
{
|
||||
fn request(&self) -> crate::dma::Request {
|
||||
$request
|
||||
|
@ -6,11 +6,11 @@ use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_cortex_m::interrupt::InterruptExt;
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use futures::future::{select, Either};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
use crate::dma::{NoDma, Transfer};
|
||||
use crate::gpio::sealed::AFType;
|
||||
#[cfg(any(lpuart_v1, lpuart_v2))]
|
||||
use crate::pac::lpuart::{regs, vals, Lpuart as Regs};
|
||||
@ -91,7 +91,7 @@ enum ReadCompletionEvent {
|
||||
// DMA Read transfer completed first
|
||||
DmaCompleted,
|
||||
// Idle line detected first
|
||||
Idle,
|
||||
Idle(usize),
|
||||
}
|
||||
|
||||
pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
|
||||
@ -183,7 +183,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
|
||||
}
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
let transfer = crate::dma::write(ch, request, buffer, tdr(T::regs()));
|
||||
let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) };
|
||||
transfer.await;
|
||||
Ok(())
|
||||
}
|
||||
@ -430,10 +430,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
let ch = &mut self.rx_dma;
|
||||
let request = ch.request();
|
||||
|
||||
let buffer_len = buffer.len();
|
||||
|
||||
// Start USART DMA
|
||||
// will not do anything yet because DMAR is not yet set
|
||||
// future which will complete when DMA Read request completes
|
||||
let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer);
|
||||
let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) };
|
||||
|
||||
// SAFETY: The only way we might have a problem is using split rx and tx
|
||||
// here we only modify or read Rx related flags, interrupts and DMA channel
|
||||
@ -565,13 +567,15 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
// when transfer is dropped, it will stop the DMA request
|
||||
let r = match select(transfer, idle).await {
|
||||
// DMA transfer completed first
|
||||
Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted),
|
||||
Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted),
|
||||
|
||||
// Idle line detected first
|
||||
Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle),
|
||||
Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle(
|
||||
buffer_len - transfer.get_remaining_transfers() as usize,
|
||||
)),
|
||||
|
||||
// error occurred
|
||||
Either::Second(Err(e)) => Err(e),
|
||||
Either::Right((Err(e), _)) => Err(e),
|
||||
};
|
||||
|
||||
drop(on_drop);
|
||||
@ -594,14 +598,9 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
// wait for DMA to complete or IDLE line detection if requested
|
||||
let res = self.inner_read_run(buffer, enable_idle_line_detection).await;
|
||||
|
||||
let ch = &mut self.rx_dma;
|
||||
|
||||
match res {
|
||||
Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),
|
||||
Ok(ReadCompletionEvent::Idle) => {
|
||||
let n = buffer_len - (ch.remaining_transfers() as usize);
|
||||
Ok(n)
|
||||
}
|
||||
Ok(ReadCompletionEvent::Idle(n)) => Ok(n),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
@ -973,73 +972,6 @@ mod eio {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
feature = "_todo_embedded_hal_serial"
|
||||
))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma>
|
||||
where
|
||||
TxDma: crate::usart::TxDma<T>,
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma>
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma>
|
||||
where
|
||||
TxDma: crate::usart::TxDma<T>,
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma>
|
||||
where
|
||||
RxDma: crate::usart::RxDma<T>,
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use buffered::*;
|
||||
#[cfg(feature = "nightly")]
|
||||
|
24
embassy-time/CHANGELOG.md
Normal file
24
embassy-time/CHANGELOG.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 0.1.1 - 2023-04-13
|
||||
|
||||
- Update `embedded-hal-async` to `0.2.0-alpha.1` (uses `async fn` in traits).
|
||||
- Update `embedded-hal v1` to `1.0.0-alpha.10`. (Note: v0.2 support is kept unchanged).
|
||||
- Remove dep on `embassy-sync`.
|
||||
- Fix reentrancy issues in the `std` time driver (#1177)
|
||||
- Add `Duration::from_hz()`.
|
||||
- impl `From` conversions to/from `core::time::Duration`.
|
||||
- Add `#[must_use]` to all futures.
|
||||
- Add inherent `async fn tick()` to `Ticker`, so you can use it directly without the `Stream` trait.
|
||||
- Add more tick rates.
|
||||
- impl `Default` for `Signal`
|
||||
- Remove unnecessary uses of `atomic-polyfill`
|
||||
|
||||
## 0.1.0 - 2022-08-26
|
||||
|
||||
- First release
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "embassy-time"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
description = "Instant and Duration for embedded no-std systems, with async timer support"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
@ -156,7 +156,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", option
|
||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
|
||||
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
atomic-polyfill = "1.0.1"
|
||||
critical-section = "1.1"
|
||||
cfg-if = "1.0.0"
|
||||
|
@ -5,8 +5,7 @@ use std::time::{Duration as StdDuration, Instant as StdInstant};
|
||||
use std::{mem, ptr, thread};
|
||||
|
||||
use atomic_polyfill::{AtomicU8, Ordering};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex as EmbassyMutex;
|
||||
use critical_section::Mutex as CsMutex;
|
||||
|
||||
use crate::driver::{AlarmHandle, Driver};
|
||||
|
||||
@ -40,7 +39,7 @@ struct TimeDriver {
|
||||
// The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't
|
||||
// Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
|
||||
// themselves are reentrant
|
||||
alarms: UninitCell<EmbassyMutex<CriticalSectionRawMutex, RefCell<[AlarmState; ALARM_COUNT]>>>,
|
||||
alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>,
|
||||
zero_instant: UninitCell<StdInstant>,
|
||||
signaler: UninitCell<Signaler>,
|
||||
}
|
||||
@ -58,8 +57,7 @@ crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
|
||||
impl TimeDriver {
|
||||
fn init(&self) {
|
||||
self.once.call_once(|| unsafe {
|
||||
self.alarms
|
||||
.write(EmbassyMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT])));
|
||||
self.alarms.write(CsMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT])));
|
||||
self.zero_instant.write(StdInstant::now());
|
||||
self.signaler.write(Signaler::new());
|
||||
|
||||
@ -72,7 +70,8 @@ impl TimeDriver {
|
||||
loop {
|
||||
let now = DRIVER.now();
|
||||
|
||||
let next_alarm = unsafe { DRIVER.alarms.as_ref() }.lock(|alarms| {
|
||||
let next_alarm = critical_section::with(|cs| {
|
||||
let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs);
|
||||
loop {
|
||||
let pending = alarms
|
||||
.borrow_mut()
|
||||
@ -139,8 +138,8 @@ impl Driver for TimeDriver {
|
||||
|
||||
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
self.init();
|
||||
unsafe { self.alarms.as_ref() }.lock(|alarms| {
|
||||
let mut alarms = alarms.borrow_mut();
|
||||
critical_section::with(|cs| {
|
||||
let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
|
||||
let alarm = &mut alarms[alarm.id() as usize];
|
||||
alarm.callback = callback as *const ();
|
||||
alarm.ctx = ctx;
|
||||
@ -149,9 +148,8 @@ impl Driver for TimeDriver {
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
self.init();
|
||||
unsafe { self.alarms.as_ref() }.lock(|alarms| {
|
||||
let mut alarms = alarms.borrow_mut();
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
|
||||
let alarm = &mut alarms[alarm.id() as usize];
|
||||
alarm.timestamp = timestamp;
|
||||
unsafe { self.signaler.as_ref() }.signal();
|
||||
|
@ -2,8 +2,7 @@ use core::cell::RefCell;
|
||||
use core::cmp::{min, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use critical_section::Mutex;
|
||||
use heapless::Vec;
|
||||
|
||||
use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
|
||||
@ -129,7 +128,7 @@ impl InnerQueue {
|
||||
}
|
||||
|
||||
struct Queue {
|
||||
inner: Mutex<CriticalSectionRawMutex, RefCell<Option<InnerQueue>>>,
|
||||
inner: Mutex<RefCell<Option<InnerQueue>>>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
@ -140,8 +139,8 @@ impl Queue {
|
||||
}
|
||||
|
||||
fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
|
||||
self.inner.lock(|inner| {
|
||||
let mut inner = inner.borrow_mut();
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow_ref_mut(cs);
|
||||
|
||||
if inner.is_none() {}
|
||||
|
||||
@ -159,8 +158,7 @@ impl Queue {
|
||||
}
|
||||
|
||||
fn handle_alarm(&self) {
|
||||
self.inner
|
||||
.lock(|inner| inner.borrow_mut().as_mut().unwrap().handle_alarm());
|
||||
critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm())
|
||||
}
|
||||
|
||||
fn handle_alarm_callback(ctx: *mut ()) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips`
|
||||
runner = "probe-run --chip nRF52840_xxAA"
|
||||
# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list`
|
||||
runner = "probe-rs-cli run --chip nRF52840_xxAA"
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
@ -6,11 +6,11 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
|
||||
embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" }
|
||||
embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" }
|
||||
embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] }
|
||||
embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf", features = ["nightly"] }
|
||||
embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
@ -18,9 +18,9 @@ defmt-rtt = { version = "0.4", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
|
||||
[features]
|
||||
ed25519-dalek = ["embassy-boot/ed25519-dalek"]
|
||||
ed25519-salty = ["embassy-boot/ed25519-salty"]
|
||||
ed25519-salty = ["embassy-boot/ed25519-salty"]
|
||||
|
@ -3,7 +3,7 @@ build-std = ["core"]
|
||||
build-std-features = ["panic_immediate_abort"]
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
runner = "probe-run --chip RP2040"
|
||||
runner = "probe-rs-cli run --chip RP2040"
|
||||
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user