Compare commits

..

1 commit

Author SHA1 Message Date
74abbd6588
hack: don't crash on unimplemented function 2024-03-14 10:21:44 +01:00
380 changed files with 7524 additions and 21446 deletions

4
.gitattributes vendored
View file

@ -15,8 +15,6 @@
*.x text
*.yml text
.vscode/*.json linguist-language=JSON-with-Comments
*.raw binary
*.bin binary
*.png binary
@ -40,4 +38,4 @@
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary
*.swp binary

3
.github/ci/doc.sh vendored
View file

@ -1,7 +1,7 @@
#!/bin/bash
## on push branch=main
set -euxo pipefail
set -euo pipefail
export RUSTUP_HOME=/ci/cache/rustup
export CARGO_HOME=/ci/cache/cargo
@ -35,7 +35,6 @@ docserver-builder -i ./embassy-time-driver -o webroot/crates/embassy-time-driver
docserver-builder -i ./embassy-time-queue-driver -o webroot/crates/embassy-time-queue-driver/git.zup
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup

View file

@ -1,4 +1,5 @@
{
"editor.formatOnSave": true,
"[toml]": {
"editor.formatOnSave": false
},

View file

@ -2,7 +2,7 @@
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
## <a href="https://embassy.dev/book/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
## <a href="https://embassy.dev/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
## Rust + async ❤️ embedded
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.

View file

@ -28,6 +28,3 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \
--- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \
cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p
cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,integrated-timers,avr-device/atmega328p

74
ci.sh
View file

@ -15,8 +15,7 @@ if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then
BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --out-dir out/examples/std"
fi
# CI intentionally does not use -eabihf on thumbv7em to minimize dep compile time.
cargo batch \
cargo batch \
--- 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 \
@ -24,8 +23,6 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers,rtos-trace \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,integrated-timers \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
@ -50,7 +47,6 @@ cargo batch \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time,time-driver-rtc1 \
@ -71,9 +67,6 @@ cargo batch \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics \
@ -88,16 +81,6 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any \
@ -124,7 +107,6 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb15cc,defmt,exti,time-driver-any,time \
@ -133,21 +115,20 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l051k8,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073cz,defmt,exti,time-driver-any,low-power,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,low-power,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100c4,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32h503rb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32h562ag,defmt,exti,time-driver-any,time \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
@ -167,29 +148,28 @@ cargo batch \
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
--- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9160 \
--- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/nrf51 \
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
--- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
--- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f3 \
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f334 \
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f7 \
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
--- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \
--- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \
--- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32h5 \
--- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h5 \
--- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
--- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wl \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
@ -198,14 +178,13 @@ cargo batch \
--- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l0 \
--- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32wl \
--- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32wb-dfu \
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
--- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wb-dfu \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \
--- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h747xi-cm7 \
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf \
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
@ -217,10 +196,10 @@ cargo batch \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --out-dir out/tests/stm32h753zi \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --out-dir out/tests/stm32h7a3zi \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h563zi --out-dir out/tests/stm32h563zi \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u585ai --out-dir out/tests/stm32u585ai \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5a5zj --out-dir out/tests/stm32u5a5zj \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba52cg --out-dir out/tests/stm32wba52cg \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u5a5zj --out-dir out/tests/stm32u5a5zj \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wba52cg --out-dir out/tests/stm32wba52cg \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --out-dir out/tests/stm32l073rz \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --out-dir out/tests/stm32l152re \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
@ -231,18 +210,21 @@ cargo batch \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --out-dir out/tests/stm32f091rc \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --out-dir out/tests/stm32h503rb \
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
--- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
--- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
$BUILD_EXTRA
# failed, a wire must've come loose, will check asap.
rm out/tests/rpi-pico/i2c
rm out/tests/stm32wb55rg/wpan_mac
rm out/tests/stm32wb55rg/wpan_ble
# not in CI yet.
rm -rf out/tests/stm32f446re
# unstable, I think it's running out of RAM?
rm out/tests/stm32f207zg/eth

View file

@ -7,24 +7,25 @@ use core::slice;
use cyw43::SpiBusCyw43;
use embassy_rp::dma::Channel;
use embassy_rp::gpio::{Drive, Level, Output, Pull, SlewRate};
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
use embassy_rp::pio::{instr, Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
use embassy_rp::{Peripheral, PeripheralRef};
use fixed::FixedU32;
use pio_proc::pio_asm;
/// SPI comms driven by PIO.
pub struct PioSpi<'d, PIO: Instance, const SM: usize, DMA> {
cs: Output<'d>,
pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> {
cs: Output<'d, CS>,
sm: StateMachine<'d, PIO, SM>,
irq: Irq<'d, PIO, 0>,
dma: PeripheralRef<'d, DMA>,
wrap_target: u8,
}
impl<'d, PIO, const SM: usize, DMA> PioSpi<'d, PIO, SM, DMA>
impl<'d, CS, PIO, const SM: usize, DMA> PioSpi<'d, CS, PIO, SM, DMA>
where
DMA: Channel,
CS: Pin,
PIO: Instance,
{
/// Create a new instance of PioSpi.
@ -32,7 +33,7 @@ where
common: &mut Common<'d, PIO>,
mut sm: StateMachine<'d, PIO, SM>,
irq: Irq<'d, PIO, 0>,
cs: Output<'d>,
cs: Output<'d, CS>,
dio: DIO,
clk: CLK,
dma: impl Peripheral<P = DMA> + 'd,
@ -205,8 +206,9 @@ where
}
}
impl<'d, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, PIO, SM, DMA>
impl<'d, CS, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, CS, PIO, SM, DMA>
where
CS: Pin,
PIO: Instance,
DMA: Channel,
{

View file

@ -10,7 +10,7 @@ repository = "https://github.com/embassy-rs/embassy"
documentation = "https://docs.embassy.dev/cyw43"
[features]
defmt = ["dep:defmt", "heapless/defmt-03", "embassy-time/defmt"]
defmt = ["dep:defmt"]
log = ["dep:log"]
# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`.
@ -32,8 +32,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
num_enum = { version = "0.5.7", default-features = false }
heapless = "0.8.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"

View file

@ -3,7 +3,7 @@ use core::iter::zip;
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
use embassy_time::{Duration, Timer};
use embassy_time::Timer;
use crate::consts::*;
use crate::events::{Event, EventSubscriber, Events};
@ -35,43 +35,6 @@ pub struct Control<'a> {
ioctl_state: &'a IoctlState,
}
#[derive(Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ScanType {
Active,
Passive,
}
#[derive(Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ScanOptions {
pub ssid: Option<heapless::String<32>>,
/// If set to `None`, all APs will be returned. If set to `Some`, only APs
/// with the specified BSSID will be returned.
pub bssid: Option<[u8; 6]>,
/// Number of probes to send on each channel.
pub nprobes: Option<u16>,
/// Time to spend waiting on the home channel.
pub home_time: Option<Duration>,
/// Scan type: active or passive.
pub scan_type: ScanType,
/// Period of time to wait on each channel when passive scanning.
pub dwell_time: Option<Duration>,
}
impl Default for ScanOptions {
fn default() -> Self {
Self {
ssid: None,
bssid: None,
nprobes: None,
home_time: None,
scan_type: ScanType::Passive,
dwell_time: None,
}
}
}
impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
Self {
@ -508,54 +471,22 @@ impl<'a> Control<'a> {
/// # Note
/// Device events are currently implemented using a bounded queue.
/// To not miss any events, you should make sure to always await the stream.
pub async fn scan(&mut self, scan_opts: ScanOptions) -> Scanner<'_> {
const SCANTYPE_ACTIVE: u8 = 0;
pub async fn scan(&mut self) -> Scanner<'_> {
const SCANTYPE_PASSIVE: u8 = 1;
let dwell_time = match scan_opts.dwell_time {
None => !0,
Some(t) => {
let mut t = t.as_millis() as u32;
if t == !0 {
t = !0 - 1;
}
t
}
};
let mut active_time = !0;
let mut passive_time = !0;
let scan_type = match scan_opts.scan_type {
ScanType::Active => {
active_time = dwell_time;
SCANTYPE_ACTIVE
}
ScanType::Passive => {
passive_time = dwell_time;
SCANTYPE_PASSIVE
}
};
let scan_params = ScanParams {
version: 1,
action: 1,
sync_id: 1,
ssid_len: scan_opts.ssid.as_ref().map(|e| e.as_bytes().len() as u32).unwrap_or(0),
ssid: scan_opts
.ssid
.map(|e| {
let mut ssid = [0; 32];
ssid[..e.as_bytes().len()].copy_from_slice(e.as_bytes());
ssid
})
.unwrap_or([0; 32]),
bssid: scan_opts.bssid.unwrap_or([0xff; 6]),
ssid_len: 0,
ssid: [0; 32],
bssid: [0xff; 6],
bss_type: 2,
scan_type,
nprobes: scan_opts.nprobes.unwrap_or(!0).into(),
active_time,
passive_time,
home_time: scan_opts.home_time.map(|e| e.as_millis() as u32).unwrap_or(!0),
scan_type: SCANTYPE_PASSIVE,
nprobes: !0,
active_time: !0,
passive_time: !0,
home_time: !0,
channel_num: 0,
channel_list: [0; 1],
};

View file

@ -311,13 +311,13 @@ pub struct Status {
pub status: u32,
}
#[derive(Copy, Clone)]
#[derive(Clone, Copy)]
pub enum Payload {
None,
BssInfo(BssInfo),
}
#[derive(Copy, Clone)]
#[derive(Clone, Copy)]
pub struct Message {
pub header: Status,

View file

@ -242,12 +242,13 @@ where
cmd,
iface,
}) => {
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await;
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await;
self.check_status(&mut buf).await;
}
Either3::Second(packet) => {
trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
let mut buf = [0; 512];
let buf8 = slice8_mut(&mut buf);
// There MUST be 2 bytes of padding between the SDPCM and BDC headers.
@ -479,8 +480,9 @@ where
self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
}
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
let buf8 = slice8_mut(buf);
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) {
let mut buf = [0; 512];
let buf8 = slice8_mut(&mut buf);
let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();

View file

@ -491,44 +491,6 @@ pub struct BssInfo {
pub ssid_len: u8,
/// SSID.
pub ssid: [u8; 32],
reserved1: [u8; 1],
/// Number of rates in the rates field.
pub rateset_count: u32,
/// Rates in 500kpbs units.
pub rates: [u8; 16],
/// Channel specification.
pub chanspec: u16,
/// Announcement traffic indication message.
pub atim_window: u16,
/// Delivery traffic indication message.
pub dtim_period: u8,
reserved2: [u8; 1],
/// Receive signal strength (in dbM).
pub rssi: i16,
/// Received noise (in dbM).
pub phy_noise: i8,
/// 802.11n capability.
pub n_cap: u8,
reserved3: [u8; 2],
/// 802.11n BSS capabilities.
pub nbss_cap: u32,
/// 802.11n control channel number.
pub ctl_ch: u8,
reserved4: [u8; 3],
reserved32: [u32; 1],
/// Flags.
pub flags: u8,
/// VHT capability.
pub vht_cap: u8,
reserved5: [u8; 2],
/// 802.11n BSS required MCS.
pub basic_mcs: [u8; 16],
/// Information Elements (IE) offset.
pub ie_offset: u16,
/// Length of Information Elements (IE) in bytes.
pub ie_length: u32,
/// Average signal-to-noise (SNR) ratio during frame reception.
pub snr: i16,
// there will be more stuff here
}
impl_bytes!(BssInfo);

View file

@ -4,11 +4,12 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_nrf::gpio::{Level, Output, OutputDrive};
use embassy_nrf::peripherals::P0_13;
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _}; // global logger
#[embassy_executor::task]
async fn blinker(mut led: Output<'static>, interval: Duration) {
async fn blinker(mut led: Output<'static, P0_13>, interval: Duration) {
loop {
led.set_high();
Timer::after(interval).await;

View file

@ -3,14 +3,14 @@
use embassy_executor::Spawner;
use embassy_stm32::exti::ExtiInput;
use embassy_stm32::gpio::{Level, Output, Pull, Speed};
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh);
let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up);
let mut button = ExtiInput::new(Input::new(p.PC13, Pull::Up), p.EXTI13);
loop {
button.wait_for_any_edge().await;

View file

@ -6,12 +6,13 @@ use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use cortex_m::peripheral::NVIC;
use cortex_m_rt::entry;
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
use embassy_stm32::peripherals::{PB14, PC13};
use embassy_stm32::{interrupt, pac};
use {defmt_rtt as _, panic_probe as _};
static BUTTON: Mutex<RefCell<Option<Input<'static>>>> = Mutex::new(RefCell::new(None));
static LED: Mutex<RefCell<Option<Output<'static>>>> = Mutex::new(RefCell::new(None));
static BUTTON: Mutex<RefCell<Option<Input<'static, PC13>>>> = Mutex::new(RefCell::new(None));
static LED: Mutex<RefCell<Option<Output<'static, PB14>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
@ -61,14 +62,14 @@ fn EXTI15_10() {
const PORT: u8 = 2;
const PIN: usize = 13;
fn check_interrupt(_pin: &mut Input<'static>) -> bool {
fn check_interrupt<P: Pin>(_pin: &mut Input<'static, P>) -> bool {
let exti = pac::EXTI;
let pin = PIN;
let lines = exti.pr(0).read();
lines.line(pin)
}
fn clear_interrupt(_pin: &mut Input<'static>) {
fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
let exti = pac::EXTI;
let pin = PIN;
let mut lines = exti.pr(0).read();
@ -76,7 +77,7 @@ fn clear_interrupt(_pin: &mut Input<'static>) {
exti.pr(0).write_value(lines);
}
fn enable_interrupt(_pin: &mut Input<'static>) {
fn enable_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
cortex_m::interrupt::free(|_| {
let rcc = pac::RCC;
rcc.apb2enr().modify(|w| w.set_syscfgen(true));

View file

@ -17,13 +17,22 @@ The first thing youll notice are two attributes at the top of the file. These
include::example$basic/src/main.rs[lines="1..2"]
----
=== Rust Nightly
The next declaration is a Rust Unstable feature, which means that Embassy requires Rust Nightly:
[source,rust]
----
include::example$basic/src/main.rs[lines="3"]
----
=== Dealing with errors
Then, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal:
[source,rust]
----
include::example$basic/src/main.rs[lines="8"]
include::example$basic/src/main.rs[lines="10"]
----
=== Task declaration
@ -32,7 +41,7 @@ After a bit of import declaration, the tasks run by the application should be de
[source,rust]
----
include::example$basic/src/main.rs[lines="10..18"]
include::example$basic/src/main.rs[lines="12..20"]
----
An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking.
@ -47,7 +56,7 @@ We then initialize the HAL with a default config, which gives us a `Peripherals`
[source,rust]
----
include::example$basic/src/main.rs[lines="20..-1"]
include::example$basic/src/main.rs[lines="22..-1"]
----
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:

View file

@ -3,10 +3,8 @@
Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework.
== Passing Buffers by Reference
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`],
to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't
want to spend resources on an allocator and end up placing buffers on the stack. This, however, can easily blow up
your stack if you are not careful.
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack.
This, however, can easily blow up your stack if you are not careful.
Consider the following example:
[,rust]

View file

@ -7,5 +7,3 @@ Here are known examples of real-world projects which make use of Embassy. Feel f
* link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device
** Targets the ESP32-S3 or ESP32-C6 MCU
* The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL
** link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system]
*** Targets nRF52 and uses nrf-softdevice

View file

@ -29,10 +29,11 @@ If you see an error like this:
You are likely missing some features of the `embassy-executor` crate.
For Cortex-M targets, check whether ALL of the following features are enabled in your `Cargo.toml` for the `embassy-executor` crate:
For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate:
* `arch-cortex-m`
* `executor-thread`
* `nightly`
For ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
@ -43,12 +44,11 @@ The first step to managing your binary size is to set up your link:https://doc.r
[source,toml]
----
[profile.release]
debug = false
lto = true
opt-level = "s"
incremental = false
codegen-units = 1
# note: debug = true is okay - debuginfo isn't flashed to the device!
debug = true
----
All of these flags are elaborated on in the Rust Book page linked above.
@ -118,31 +118,21 @@ features = [
]
----
If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source:
[source,rust]
----
use embassy_stm32 as _;
----
== Error: `Only one package in the dependency graph may specify the same links value.`
You have multiple versions of the same crate in your dependency tree. This means that some of your
embassy crates are coming from crates.io, and some from git, each of them pulling in a different set
of dependencies.
To resolve this issue, make sure to only use a single source for all your embassy crates!
To do this, you should patch your dependencies to use git sources using `[patch.crates.io]`
and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
To resolve this issue, make sure to only use a single source for all your embassy crates! To do this,
you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
Example:
[source,toml]
----
[patch.crates-io]
embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
# embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
----
Note that the git revision should match any other embassy patches or git dependencies that you are using!
@ -168,43 +158,4 @@ Note that the git revision should match any other embassy patches or git depende
When you aren't using the `nightly` feature of `embassy-executor`, the executor uses a bump allocator, which may require configuration.
Something like this error will occur at **compile time** if the task arena is *too large* for the target's RAM:
[source,plain]
----
rust-lld: error: section '.bss' will not fit in region 'RAM': overflowed by _ bytes
rust-lld: error: section '.uninit' will not fit in region 'RAM': overflowed by _ bytes
----
And this message will appear at **runtime** if the task arena is *too small* for the tasks running:
[source,plain]
----
ERROR panicked at 'embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/'
----
NOTE: If all tasks are spawned at startup, this panic will occur immediately.
Check out link:https://docs.embassy.dev/embassy-executor/git/cortex-m/index.html#task-arena[Task Arena Documentation] for more details.
== Can I use manual ISRs alongside Embassy?
Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. Simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project].
== How can I measure resource usage (CPU, RAM, etc.)?
=== For CPU Usage:
There are a couple techniques that have been documented, generally you want to measure how long you are spending in the idle or low priority loop.
We need to document specifically how to do this in embassy, but link:https://blog.japaric.io/cpu-monitor/[this older post] describes the general process.
If you end up doing this, please update this section with more specific examples!
=== For Static Memory Usage
Tools like `cargo size` and `cargo nm` can tell you the size of any globals or other static usage. Specifically you will want to see the size of the `.data` and `.bss` sections, which together make up the total global/static memory usage.
=== For Max Stack Usage
Check out link:https://github.com/Dirbaio/cargo-call-stack/[`cargo-call-stack`] for statically calculating worst-case stack usage. There are some caveats and inaccuracies possible with this, but this is a good way to get the general idea. See link:https://github.com/dirbaio/cargo-call-stack#known-limitations[the README] for more details.

View file

@ -3,7 +3,7 @@
So you want to try Embassy, great! To get started, there are a few tools you need to install:
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
* link:https://probe.rs/[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
@ -82,19 +82,19 @@ If everything worked correctly, you should see a blinking LED on your board, and
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27
----
NOTE: How does the `+cargo run+` command know how to connect to our board and program it? In each `examples` folder, theres a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip its programming, so youll have to edit this file if you want to run examples on other chips.
NOTE: How does the `cargo run` command know how to connect to our board and program it? In each `examples` folder, theres a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip its programming, so youll have to edit this file if you want to run examples on other chips.
=== It didnt work!
If you hare having issues when running `+cargo run --release+`, please check the following:
If you hare having issues when running `cargo run --release`, please check the following:
* You are specifying the correct `+--chip+` on the command line, OR
* You have set `+.cargo/config.toml+`s run line to the correct chip, AND
* You have changed `+examples/Cargo.toml+`s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)
* You are specifying the correct `--chip on the command line``, OR
* You have set `.cargo/config.toml`'s run line to the correct chip, AND
* You have changed `examples/Cargo.toml`'s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)
At this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin.
If you are trying to run an example with `+cargo run --release+` and you see the following output:
If you are trying to run an example with `cargo run --release` and you see the following output:
[source]
----
0.000000 INFO Hello World!
@ -115,22 +115,6 @@ To get rid of the frame-index error add the following to your `Cargo.toml`:
debug = 2
----
If youre getting an extremely long error message containing something like the following:
[source]
----
error[E0463]: can't find crate for `std`
|
= note: the `thumbv6m-none-eabi` target may not support the standard library
= note: `std` is required by `stable_deref_trait` because it does not declare `#![no_std]`
----
Make sure that you didnt accidentally run `+cargo add probe-rs+` (which adds it as a dependency) instead of link:https://probe.rs/docs/getting-started/installation/[correctly installing probe-rs].
If youre using a raspberry pi pico-w, make sure youre running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-ws on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked.
If youre using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state].
If youre still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room].
== What's next?
@ -140,4 +124,3 @@ Congratulations, you have your first Embassy application running! Here are some
* Read more about the xref:runtime.adoc[executor].
* Read more about the xref:hal.adoc[HAL].
* Start xref:basic_application.adoc[writing your application].
* Learn how to xref:new_project.adoc[start a new embassy project by adapting an example].

View file

@ -1,17 +1,6 @@
= Starting a new Embassy project
Once youve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project.
There are some tools for generating Embassy projects: (WIP)
==== CLI
- link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF)
==== cargo-generate
- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP)
- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP)
But if you want to start from scratch:
Once youve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. The easiest way to do this is to adapt an example for a similar chip to the one youre targeting.
As an example, lets create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes.
@ -177,13 +166,13 @@ should result in a blinking LED (if theres one attached to the pin in `src/ma
Erasing sectors ✔ [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s )
Programming pages ✔ [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s ) Finished in 0.817s
0.000000 TRACE BDCR configured: 00008200
└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117
└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117
0.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) }
└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130
└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130
0.000000 INFO Hello World!
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14
0.000091 INFO high
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19
0.300201 INFO low
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
----
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
----

View file

@ -38,18 +38,13 @@ DEFMT_LOG = "trace" # <- can change to info, warn, or error
== build.rs
This is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
== Cargo.toml
This is your manifest file, where you can configure all of the embassy components to use the features you need.
==== Features
===== Time
- tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes.
- defmt-timestamp-uptime: defmt log entries will display the uptime in seconds.
...more to come
TODO: someone should exhaustively describe every feature for every component!
== memory.x

View file

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot-nrf"
version = "0.2.0"
version = "0.1.0"
description = "Bootloader lib for nRF chips"
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
@ -25,7 +25,7 @@ defmt = { version = "0.3", optional = true }
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", default-features = false }
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
embassy-boot = { version = "0.1.0", path = "../embassy-boot" }
cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.1"

View file

@ -4,8 +4,8 @@
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,
FirmwareUpdater, FirmwareUpdaterConfig,
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
FirmwareUpdaterConfig,
};
use embassy_nrf::nvmc::PAGE_SIZE;
use embassy_nrf::peripherals::WDT;
@ -16,21 +16,14 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;
impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware.
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
Self::try_prepare::<ACTIVE, DFU, STATE>(config).expect("Boot prepare error")
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Result<Self, BootError> {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
let _state = boot.prepare_boot(aligned_buf.as_mut())?;
Ok(Self)
boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error");
Self
}
/// Boots the application without softdevice mechanisms.

View file

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot-rp"
version = "0.2.0"
version = "0.1.0"
description = "Bootloader lib for RP2040 chips"
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
@ -26,7 +26,7 @@ log = { version = "0.4", optional = true }
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
embassy-rp = { version = "0.1.0", path = "../embassy-rp", default-features = false }
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
embassy-boot = { version = "0.1.0", path = "../embassy-boot" }
embassy-time = { version = "0.3.0", path = "../embassy-time" }
cortex-m = { version = "0.7.6" }

View file

@ -4,8 +4,8 @@
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,
FirmwareUpdater, FirmwareUpdaterConfig, State,
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
FirmwareUpdaterConfig, State,
};
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
use embassy_rp::peripherals::{FLASH, WATCHDOG};
@ -21,17 +21,10 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
Self::try_prepare::<ACTIVE, DFU, STATE>(config).expect("Boot prepare error")
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Result<Self, BootError> {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
let _state = boot.prepare_boot(aligned_buf.as_mut())?;
Ok(Self)
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
Self
}
/// Boots the application.

View file

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot-stm32"
version = "0.2.0"
version = "0.1.0"
description = "Bootloader lib for STM32 chips"
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
@ -27,7 +27,7 @@ log = { version = "0.4", optional = true }
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false }
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
embassy-boot = { version = "0.1.0", path = "../embassy-boot" }
cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.1"

View file

@ -4,8 +4,8 @@
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,
FirmwareUpdater, FirmwareUpdaterConfig, State,
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
FirmwareUpdaterConfig, State,
};
use embedded_storage::nor_flash::NorFlash;
@ -20,17 +20,10 @@ impl BootLoader {
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
Self::try_prepare::<ACTIVE, DFU, STATE, BUFFER_SIZE>(config).expect("Boot prepare error")
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Result<Self, BootError> {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
let state = boot.prepare_boot(aligned_buf.as_mut())?;
Ok(Self { state })
let state = boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
Self { state }
}
/// Boots the application.

View file

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot"
version = "0.2.0"
version = "0.1.1"
description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks."
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"

View file

@ -49,51 +49,16 @@ pub struct BootLoaderConfig<ACTIVE, DFU, STATE> {
pub state: STATE,
}
impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>
impl<'a, FLASH: NorFlash>
BootLoaderConfig<
BlockingPartition<'a, NoopRawMutex, ACTIVE>,
BlockingPartition<'a, NoopRawMutex, DFU>,
BlockingPartition<'a, NoopRawMutex, STATE>,
BlockingPartition<'a, NoopRawMutex, FLASH>,
BlockingPartition<'a, NoopRawMutex, FLASH>,
BlockingPartition<'a, NoopRawMutex, FLASH>,
>
{
/// Constructs a `BootLoaderConfig` instance from flash memory and address symbols defined in the linker file.
///
/// This method initializes `BlockingPartition` instances for the active, DFU (Device Firmware Update),
/// and state partitions, leveraging start and end addresses specified by the linker. These partitions
/// are critical for managing firmware updates, application state, and boot operations within the bootloader.
///
/// # Parameters
/// - `active_flash`: A reference to a mutex-protected `RefCell` for the active partition's flash interface.
/// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.
/// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.
///
/// # Safety
/// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses
/// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined
/// in the memory.x file to prevent undefined behavior.
///
/// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory
/// interfaces provided are compatible with these regions.
///
/// # Returns
/// A `BootLoaderConfig` instance with `BlockingPartition` instances for the active, DFU, and state partitions.
///
/// # Example
/// ```ignore
/// // Assume `active_flash`, `dfu_flash`, and `state_flash` all share the same flash memory interface.
/// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
/// let flash = Mutex::new(RefCell::new(layout.bank1_region));
///
/// let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
/// // `config` can now be used to create a `BootLoader` instance for managing boot operations.
/// ```
/// Working examples can be found in the bootloader examples folder.
/// Create a bootloader config from the flash and address symbols defined in the linkerfile
// #[cfg(target_os = "none")]
pub fn from_linkerfile_blocking(
active_flash: &'a Mutex<NoopRawMutex, RefCell<ACTIVE>>,
dfu_flash: &'a Mutex<NoopRawMutex, RefCell<DFU>>,
state_flash: &'a Mutex<NoopRawMutex, RefCell<STATE>>,
) -> Self {
pub fn from_linkerfile_blocking(flash: &'a Mutex<NoopRawMutex, RefCell<FLASH>>) -> Self {
extern "C" {
static __bootloader_state_start: u32;
static __bootloader_state_end: u32;
@ -108,21 +73,21 @@ impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>
let end = &__bootloader_active_end as *const u32 as u32;
trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end);
BlockingPartition::new(active_flash, start, end - start)
BlockingPartition::new(flash, start, end - start)
};
let dfu = unsafe {
let start = &__bootloader_dfu_start as *const u32 as u32;
let end = &__bootloader_dfu_end as *const u32 as u32;
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
BlockingPartition::new(dfu_flash, start, end - start)
BlockingPartition::new(flash, start, end - start)
};
let state = unsafe {
let start = &__bootloader_state_start as *const u32 as u32;
let end = &__bootloader_state_end as *const u32 as u32;
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
BlockingPartition::new(state_flash, start, end - start)
BlockingPartition::new(flash, start, end - start)
};
Self { active, dfu, state }

View file

@ -13,18 +13,14 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
dfu: DFU,
state: FirmwareState<'d, STATE>,
last_erased_dfu_sector_index: Option<usize>,
}
#[cfg(target_os = "none")]
impl<'a, DFU: NorFlash, STATE: NorFlash>
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, DFU>, Partition<'a, NoopRawMutex, STATE>>
impl<'a, FLASH: NorFlash>
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>>
{
/// Create a firmware updater config from the flash and address symbols defined in the linkerfile
pub fn from_linkerfile(
dfu_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, DFU>,
state_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, STATE>,
) -> Self {
pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, FLASH>) -> Self {
extern "C" {
static __bootloader_state_start: u32;
static __bootloader_state_end: u32;
@ -37,14 +33,14 @@ impl<'a, DFU: NorFlash, STATE: NorFlash>
let end = &__bootloader_dfu_end as *const u32 as u32;
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
Partition::new(dfu_flash, start, end - start)
Partition::new(flash, start, end - start)
};
let state = unsafe {
let start = &__bootloader_state_start as *const u32 as u32;
let end = &__bootloader_state_end as *const u32 as u32;
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
Partition::new(state_flash, start, end - start)
Partition::new(flash, start, end - start)
};
Self { dfu, state }
@ -57,7 +53,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
Self {
dfu: config.dfu,
state: FirmwareState::new(config.state, aligned),
last_erased_dfu_sector_index: None,
}
}
@ -74,7 +69,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
/// proceed with updating the firmware as it must be signed with a
/// corresponding private key (otherwise it could be malicious firmware).
///
/// Mark to trigger firmware swap on next boot if verify succeeds.
/// Mark to trigger firmware swap on next boot if verify suceeds.
///
/// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
/// been generated from a SHA-512 digest of the firmware bytes.
@ -174,68 +169,21 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
self.state.mark_booted().await
}
/// Writes firmware data to the device.
/// Write data to a flash page.
///
/// This function writes the given data to the firmware area starting at the specified offset.
/// It handles sector erasures and data writes while verifying the device is in a proper state
/// for firmware updates. The function ensures that only unerased sectors are erased before
/// writing and efficiently handles the writing process across sector boundaries and in
/// various configurations (data size, sector size, etc.).
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
///
/// # Arguments
/// # Safety
///
/// * `offset` - The starting offset within the firmware area where data writing should begin.
/// * `data` - A slice of bytes representing the firmware data to be written. It must be a
/// multiple of NorFlash WRITE_SIZE.
///
/// # Returns
///
/// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
///
/// # Errors
///
/// This function will return an error if:
///
/// - The device is not in a proper state to receive firmware updates (e.g., not booted).
/// - There is a failure erasing a sector before writing.
/// - There is a failure writing data to the device.
/// Failing to meet alignment and size requirements may result in a panic.
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
// Make sure we are running a booted firmware to avoid reverting to a bad state.
assert!(data.len() >= DFU::ERASE_SIZE);
self.state.verify_booted().await?;
// Initialize variables to keep track of the remaining data and the current offset.
let mut remaining_data = data;
let mut offset = offset;
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
// Continue writing as long as there is data left to write.
while !remaining_data.is_empty() {
// Compute the current sector and its boundaries.
let current_sector = offset / DFU::ERASE_SIZE;
let sector_start = current_sector * DFU::ERASE_SIZE;
let sector_end = sector_start + DFU::ERASE_SIZE;
// Determine if the current sector needs to be erased before writing.
let need_erase = self
.last_erased_dfu_sector_index
.map_or(true, |last_erased_sector| current_sector != last_erased_sector);
// If the sector needs to be erased, erase it and update the last erased sector index.
if need_erase {
self.dfu.erase(sector_start as u32, sector_end as u32).await?;
self.last_erased_dfu_sector_index = Some(current_sector);
}
// Calculate the size of the data chunk that can be written in the current iteration.
let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
// Split the data to get the current chunk to be written and the remaining data.
let (data_chunk, rest) = remaining_data.split_at(write_size);
// Write the current data chunk.
self.dfu.write(offset as u32, data_chunk).await?;
// Update the offset and remaining data for the next iteration.
remaining_data = rest;
offset += write_size;
}
self.dfu.write(offset as u32, data).await?;
Ok(())
}
@ -325,25 +273,16 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned).await?;
if self.aligned[..STATE::WRITE_SIZE].iter().any(|&b| b != magic) {
if self.aligned.iter().any(|&b| b != magic) {
// Read progress validity
if STATE::READ_SIZE <= 2 * STATE::WRITE_SIZE {
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
} else {
self.aligned.rotate_left(STATE::WRITE_SIZE);
}
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
if self.aligned[..STATE::WRITE_SIZE]
.iter()
.any(|&b| b != STATE_ERASE_VALUE)
{
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
self.aligned.fill(!STATE_ERASE_VALUE);
self.state
.write(STATE::WRITE_SIZE as u32, &self.aligned[..STATE::WRITE_SIZE])
.await?;
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?;
}
// Clear magic and progress
@ -351,7 +290,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
// Set magic
self.aligned.fill(magic);
self.state.write(0, &self.aligned[..STATE::WRITE_SIZE]).await?;
self.state.write(0, &self.aligned).await?;
}
Ok(())
}
@ -387,76 +326,4 @@ mod tests {
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
#[test]
fn can_verify_sha1_sector_bigger_than_chunk() {
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
let state = Partition::new(&flash, 0, 4096);
let dfu = Partition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
let mut offset = 0;
for chunk in to_write.chunks(1024) {
block_on(updater.write_firmware(offset, chunk)).unwrap();
offset += chunk.len();
}
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
#[test]
fn can_verify_sha1_sector_smaller_than_chunk() {
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
let state = Partition::new(&flash, 0, 4096);
let dfu = Partition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
let mut offset = 0;
for chunk in to_write.chunks(2048) {
block_on(updater.write_firmware(offset, chunk)).unwrap();
offset += chunk.len();
}
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
#[test]
fn can_verify_sha1_cross_sector_boundary() {
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
let state = Partition::new(&flash, 0, 4096);
let dfu = Partition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
let mut offset = 0;
for chunk in to_write.chunks(896) {
block_on(updater.write_firmware(offset, chunk)).unwrap();
offset += chunk.len();
}
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
}

View file

@ -13,47 +13,15 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
dfu: DFU,
state: BlockingFirmwareState<'d, STATE>,
last_erased_dfu_sector_index: Option<usize>,
}
#[cfg(target_os = "none")]
impl<'a, DFU: NorFlash, STATE: NorFlash>
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, DFU>, BlockingPartition<'a, NoopRawMutex, STATE>>
impl<'a, FLASH: NorFlash>
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>>
{
/// Constructs a `FirmwareUpdaterConfig` instance from flash memory and address symbols defined in the linker file.
///
/// This method initializes `BlockingPartition` instances for the DFU (Device Firmware Update), and state
/// partitions, leveraging start and end addresses specified by the linker. These partitions are critical
/// for managing firmware updates, application state, and boot operations within the bootloader.
///
/// # Parameters
/// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.
/// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.
///
/// # Safety
/// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses
/// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined
/// in the memory.x file to prevent undefined behavior.
///
/// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory
/// interfaces provided are compatible with these regions.
///
/// # Returns
/// A `FirmwareUpdaterConfig` instance with `BlockingPartition` instances for the DFU, and state partitions.
///
/// # Example
/// ```ignore
/// // Assume `dfu_flash`, and `state_flash` share the same flash memory interface.
/// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
/// let flash = Mutex::new(RefCell::new(layout.bank1_region));
///
/// let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
/// // `config` can now be used to create a `FirmwareUpdater` instance for managing boot operations.
/// ```
/// Working examples can be found in the bootloader examples folder.
/// Create a firmware updater config from the flash and address symbols defined in the linkerfile
pub fn from_linkerfile_blocking(
dfu_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<DFU>>,
state_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<STATE>>,
flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<FLASH>>,
) -> Self {
extern "C" {
static __bootloader_state_start: u32;
@ -67,14 +35,14 @@ impl<'a, DFU: NorFlash, STATE: NorFlash>
let end = &__bootloader_dfu_end as *const u32 as u32;
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
BlockingPartition::new(dfu_flash, start, end - start)
BlockingPartition::new(flash, start, end - start)
};
let state = unsafe {
let start = &__bootloader_state_start as *const u32 as u32;
let end = &__bootloader_state_end as *const u32 as u32;
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
BlockingPartition::new(state_flash, start, end - start)
BlockingPartition::new(flash, start, end - start)
};
Self { dfu, state }
@ -92,7 +60,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
Self {
dfu: config.dfu,
state: BlockingFirmwareState::new(config.state, aligned),
last_erased_dfu_sector_index: None,
}
}
@ -109,7 +76,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
/// proceed with updating the firmware as it must be signed with a
/// corresponding private key (otherwise it could be malicious firmware).
///
/// Mark to trigger firmware swap on next boot if verify succeeds.
/// Mark to trigger firmware swap on next boot if verify suceeds.
///
/// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
/// been generated from a SHA-512 digest of the firmware bytes.
@ -209,68 +176,20 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
self.state.mark_booted()
}
/// Writes firmware data to the device.
/// Write data to a flash page.
///
/// This function writes the given data to the firmware area starting at the specified offset.
/// It handles sector erasures and data writes while verifying the device is in a proper state
/// for firmware updates. The function ensures that only unerased sectors are erased before
/// writing and efficiently handles the writing process across sector boundaries and in
/// various configurations (data size, sector size, etc.).
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
///
/// # Arguments
/// # Safety
///
/// * `offset` - The starting offset within the firmware area where data writing should begin.
/// * `data` - A slice of bytes representing the firmware data to be written. It must be a
/// multiple of NorFlash WRITE_SIZE.
///
/// # Returns
///
/// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
///
/// # Errors
///
/// This function will return an error if:
///
/// - The device is not in a proper state to receive firmware updates (e.g., not booted).
/// - There is a failure erasing a sector before writing.
/// - There is a failure writing data to the device.
/// Failing to meet alignment and size requirements may result in a panic.
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
// Make sure we are running a booted firmware to avoid reverting to a bad state.
assert!(data.len() >= DFU::ERASE_SIZE);
self.state.verify_booted()?;
// Initialize variables to keep track of the remaining data and the current offset.
let mut remaining_data = data;
let mut offset = offset;
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
// Continue writing as long as there is data left to write.
while !remaining_data.is_empty() {
// Compute the current sector and its boundaries.
let current_sector = offset / DFU::ERASE_SIZE;
let sector_start = current_sector * DFU::ERASE_SIZE;
let sector_end = sector_start + DFU::ERASE_SIZE;
// Determine if the current sector needs to be erased before writing.
let need_erase = self
.last_erased_dfu_sector_index
.map_or(true, |last_erased_sector| current_sector != last_erased_sector);
// If the sector needs to be erased, erase it and update the last erased sector index.
if need_erase {
self.dfu.erase(sector_start as u32, sector_end as u32)?;
self.last_erased_dfu_sector_index = Some(current_sector);
}
// Calculate the size of the data chunk that can be written in the current iteration.
let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
// Split the data to get the current chunk to be written and the remaining data.
let (data_chunk, rest) = remaining_data.split_at(write_size);
// Write the current data chunk.
self.dfu.write(offset as u32, data_chunk)?;
// Update the offset and remaining data for the next iteration.
remaining_data = rest;
offset += write_size;
}
self.dfu.write(offset as u32, data)?;
Ok(())
}
@ -418,82 +337,4 @@ mod tests {
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
#[test]
fn can_verify_sha1_sector_bigger_than_chunk() {
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
let state = BlockingPartition::new(&flash, 0, 4096);
let dfu = BlockingPartition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
let mut offset = 0;
for chunk in to_write.chunks(1024) {
updater.write_firmware(offset, chunk).unwrap();
offset += chunk.len();
}
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
updater
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
.unwrap();
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
#[test]
fn can_verify_sha1_sector_smaller_than_chunk() {
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
let state = BlockingPartition::new(&flash, 0, 4096);
let dfu = BlockingPartition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
let mut offset = 0;
for chunk in to_write.chunks(2048) {
updater.write_firmware(offset, chunk).unwrap();
offset += chunk.len();
}
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
updater
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
.unwrap();
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
#[test]
fn can_verify_sha1_cross_sector_boundary() {
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
let state = BlockingPartition::new(&flash, 0, 4096);
let dfu = BlockingPartition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
let mut offset = 0;
for chunk in to_write.chunks(896) {
updater.write_firmware(offset, chunk).unwrap();
offset += chunk.len();
}
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
updater
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
.unwrap();
assert_eq!(Sha1::digest(update).as_slice(), hash);
}
}

View file

@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
/// Firmware updater flash configuration holding the two flashes used by the updater
///
/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use.
/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile_blocking`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
/// the provided flash according to symbols defined in the linkerfile.
pub struct FirmwareUpdaterConfig<DFU, STATE> {
/// The dfu flash partition

View file

@ -1,6 +1,6 @@
[package]
name = "embassy-executor-macros"
version = "0.4.1"
version = "0.4.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "macros for creating the entry point and tasks for embassy-executor"

View file

@ -62,13 +62,6 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
task::run(&args.meta, f).unwrap_or_else(|x| x).into()
}
#[proc_macro_attribute]
pub fn main_avr(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as Args);
let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(&args.meta, f, main::avr()).unwrap_or_else(|x| x).into()
}
/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.
///
/// The following restrictions apply:

View file

@ -12,20 +12,6 @@ struct Args {
entry: Option<String>,
}
pub fn avr() -> TokenStream {
quote! {
#[avr_device::entry]
fn main() -> ! {
let mut executor = ::embassy_executor::Executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
}
}
pub fn riscv(args: &[NestedMeta]) -> TokenStream {
let maybe_entry = match Args::from_list(args) {
Ok(args) => args.entry,

View file

@ -40,7 +40,8 @@ critical-section = "1.1"
document-features = "0.2.7"
# needed for AVR
# needed for riscv
# remove when https://github.com/rust-lang/rust/pull/114499 is merged
portable-atomic = { version = "1.5", optional = true }
# arch-cortex-m dependencies
@ -50,9 +51,6 @@ cortex-m = { version = "0.7.6", optional = true }
wasm-bindgen = { version = "0.2.82", optional = true }
js-sys = { version = "0.3", optional = true }
# arch-avr dependencies
avr-device = { version = "0.5.3", optional = true }
[dev-dependencies]
critical-section = { version = "1.1", features = ["std"] }
@ -77,11 +75,9 @@ arch-std = ["_arch", "critical-section/std"]
## Cortex-M
arch-cortex-m = ["_arch", "dep:cortex-m"]
## RISC-V 32
arch-riscv32 = ["_arch"]
arch-riscv32 = ["_arch", "dep:portable-atomic"]
## WASM
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
## AVR
arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"]
#! ### Executor
@ -92,11 +88,11 @@ executor-interrupt = []
#! ### Task Arena Size
#! Sets the [task arena](#task-arena) size. Necessary if youre not using `nightly`.
#!
#!
#! <details>
#! <summary>Preconfigured Task Arena Sizes:</summary>
#! <!-- rustdoc requires the following blank line for the feature list to render correctly! -->
#!
#!
# BEGIN AUTOGENERATED CONFIG FEATURES
# Generated by gen_config.py. DO NOT EDIT.

View file

@ -1,72 +0,0 @@
#[cfg(feature = "executor-interrupt")]
compile_error!("`executor-interrupt` is not supported with `arch-avr`.");
#[cfg(feature = "executor-thread")]
pub use thread::*;
#[cfg(feature = "executor-thread")]
mod thread {
use core::marker::PhantomData;
pub use embassy_executor_macros::main_avr as main;
use portable_atomic::{AtomicBool, Ordering};
use crate::{raw, Spawner};
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
#[export_name = "__pender"]
fn __pender(_context: *mut ()) {
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
}
/// avr Executor
pub struct Executor {
inner: raw::Executor,
not_send: PhantomData<*mut ()>,
}
impl Executor {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData,
}
}
/// Run the executor.
///
/// The `init` closure is called with a [`Spawner`] that spawns tasks on
/// this executor. Use it to spawn the initial task(s). After `init` returns,
/// the executor starts running the tasks.
///
/// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
/// for example by passing it as an argument to the initial tasks.
///
/// This function requires `&'static mut self`. This means you have to store the
/// Executor instance in a place where it'll live forever and grants you mutable
/// access. There's a few ways to do this:
///
/// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
/// - a `static mut` (unsafe)
/// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
///
/// This function never returns.
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
init(self.inner.spawner());
loop {
unsafe {
avr_device::interrupt::disable();
if !SIGNAL_WORK_THREAD_MODE.swap(false, Ordering::SeqCst) {
avr_device::interrupt::enable();
avr_device::asm::sleep();
} else {
avr_device::interrupt::enable();
self.inner.poll();
}
}
}
}
}
}

View file

@ -6,9 +6,9 @@ pub use thread::*;
#[cfg(feature = "executor-thread")]
mod thread {
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
pub use embassy_executor_macros::main_riscv as main;
use portable_atomic::{AtomicBool, Ordering};
use crate::{raw, Spawner};

View file

@ -23,10 +23,9 @@ macro_rules! check_at_most_one {
check_at_most_one!(@amo [$($f)*] [$($f)*] []);
};
}
check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",);
check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",);
#[cfg(feature = "_arch")]
#[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")]
#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")]
#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]

View file

@ -581,15 +581,6 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue {
#[cfg(feature = "integrated-timers")]
embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))]
const fn gcd(a: u64, b: u64) -> u64 {
if b == 0 {
a
} else {
gcd(b, a % b)
}
}
#[cfg(feature = "rtos-trace")]
impl rtos_trace::RtosTraceOSCallbacks for Executor {
fn task_list() {
@ -597,8 +588,7 @@ impl rtos_trace::RtosTraceOSCallbacks for Executor {
}
#[cfg(feature = "integrated-timers")]
fn time() -> u64 {
const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000);
embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M)
Instant::now().as_micros()
}
#[cfg(not(feature = "integrated-timers"))]
fn time() -> u64 {

View file

@ -1,6 +1,6 @@
//! Atomic reusable ringbuffer.
use core::slice;
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use core::{ptr, slice};
/// Atomic reusable ringbuffer
///
@ -73,7 +73,6 @@ impl RingBuffer {
pub unsafe fn deinit(&self) {
// Ordering: it's OK to use `Relaxed` because this is not called
// concurrently with other methods.
self.buf.store(ptr::null_mut(), Ordering::Relaxed);
self.len.store(0, Ordering::Relaxed);
self.start.store(0, Ordering::Relaxed);
self.end.store(0, Ordering::Relaxed);
@ -83,46 +82,20 @@ impl RingBuffer {
///
/// # Safety
///
/// - Only one reader can exist at a time.
/// - Ringbuffer must be initialized.
/// Only one reader can exist at a time.
pub unsafe fn reader(&self) -> Reader<'_> {
Reader(self)
}
/// Try creating a reader, fails if not initialized.
///
/// # Safety
///
/// Only one reader can exist at a time.
pub unsafe fn try_reader(&self) -> Option<Reader<'_>> {
if self.buf.load(Ordering::Relaxed).is_null() {
return None;
}
Some(Reader(self))
}
/// Create a writer.
///
/// # Safety
///
/// - Only one writer can exist at a time.
/// - Ringbuffer must be initialized.
/// Only one writer can exist at a time.
pub unsafe fn writer(&self) -> Writer<'_> {
Writer(self)
}
/// Try creating a writer, fails if not initialized.
///
/// # Safety
///
/// Only one writer can exist at a time.
pub unsafe fn try_writer(&self) -> Option<Writer<'_>> {
if self.buf.load(Ordering::Relaxed).is_null() {
return None;
}
Some(Writer(self))
}
/// Return length of buffer.
pub fn len(&self) -> usize {
self.len.load(Ordering::Relaxed)

View file

@ -1,5 +1,5 @@
use core::marker::PhantomData;
use core::ops::Deref;
use core::ops::{Deref, DerefMut};
/// An exclusive reference to a peripheral.
///
@ -86,6 +86,13 @@ impl<'a, T> Deref for PeripheralRef<'a, T> {
}
}
impl<'a, T> DerefMut for PeripheralRef<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
/// Trait for any type that can be used as a peripheral of type `P`.
///
/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
@ -155,7 +162,7 @@ pub trait Peripheral: Sized {
}
}
impl<'b, T: Deref> Peripheral for T
impl<'b, T: DerefMut> Peripheral for T
where
T::Target: Peripheral,
{

View file

@ -18,4 +18,4 @@ libc = "0.2.101"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/"
target = "x86_64-unknown-linux-gnu"
target = "thumbv7em-none-eabi"

View file

@ -16,11 +16,11 @@ categories = [
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"]
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"]
features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
[features]
default = []

View file

@ -342,7 +342,7 @@ impl<'a> TcpSocket<'a> {
self.io.with(|s, _| s.may_send())
}
/// return whether the receive half of the full-duplex connection is open.
/// return whether the recieve half of the full-duplex connection is open.
/// This function returns true if its possible to receive data from the remote endpoint.
/// It will return true while there is data in the receive buffer, and if there isnt,
/// as long as the remote endpoint has not closed the connection.
@ -471,7 +471,7 @@ impl<'d> TcpIo<'d> {
s.register_recv_waker(cx.waker());
Poll::Pending
} else {
// if we can't receive because the receive half of the duplex connection is closed then return an error
// if we can't receive because the recieve half of the duplex connection is closed then return an error
Poll::Ready(Err(Error::ConnectionReset))
}
} else {
@ -491,16 +491,10 @@ impl<'d> TcpIo<'d> {
async fn flush(&mut self) -> Result<(), Error> {
poll_fn(move |cx| {
self.with_mut(|s, _| {
let data_pending = s.send_queue() > 0;
let fin_pending = matches!(
s.state(),
tcp::State::FinWait1 | tcp::State::Closing | tcp::State::LastAck
);
let rst_pending = s.state() == tcp::State::Closed && s.remote_endpoint().is_some();
let waiting_close = s.state() == tcp::State::Closed && s.remote_endpoint().is_some();
// If there are outstanding send operations, register for wake up and wait
// smoltcp issues wake-ups when octets are dequeued from the send buffer
if data_pending || fin_pending || rst_pending {
if s.send_queue() > 0 || waiting_close {
s.register_send_waker(cx.waker());
Poll::Pending
// No outstanding sends, socket is flushed

View file

@ -15,7 +15,6 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/s
features = ["time", "defmt", "unstable-pac", "gpiote", "time-driver-rtc1"]
flavors = [
{ regex_feature = "nrf51", target = "thumbv6m-none-eabi" },
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
{ regex_feature = "nrf91.*", target = "thumbv8m.main-none-eabihf" },
@ -29,7 +28,6 @@ rustdoc-args = ["--cfg", "docsrs"]
default = ["rt"]
## Cortex-M runtime (enabled by default)
rt = [
"nrf51-pac?/rt",
"nrf52805-pac?/rt",
"nrf52810-pac?/rt",
"nrf52811-pac?/rt",
@ -73,8 +71,6 @@ reset-pin-as-gpio = []
qspi-multiwrite-flash = []
#! ### Chip selection features
## nRF51
nrf51 = ["nrf51-pac", "_nrf51"]
## nRF52805
nrf52805 = ["nrf52805-pac", "_nrf52"]
## nRF52810
@ -108,7 +104,6 @@ _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"]
_nrf5340 = ["_gpio-p1", "_dppi"]
_nrf9160 = ["nrf9160-pac", "_dppi"]
_nrf52 = ["_ppi"]
_nrf51 = ["_ppi"]
_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"]
@ -138,7 +133,6 @@ embedded-io = { version = "0.6.0" }
embedded-io-async = { version = "0.6.1" }
defmt = { version = "0.3", optional = true }
bitflags = "2.4.2"
log = { version = "0.4.14", optional = true }
cortex-m-rt = ">=0.6.15,<0.8"
cortex-m = "0.7.6"
@ -146,11 +140,10 @@ critical-section = "1.1"
rand_core = "0.6.3"
fixed = "1.10.0"
embedded-storage = "0.3.1"
embedded-storage-async = "0.4.1"
embedded-storage-async = "0.4.0"
cfg-if = "1.0.0"
document-features = "0.2.7"
nrf51-pac = { version = "0.12.0", optional = true }
nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true }

View file

@ -14,12 +14,11 @@ For a complete list of available peripherals and features, see the [embassy-nrf
The `embassy-nrf` HAL supports most variants of the nRF family:
* nRF51 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf51))
* nRF52 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf52840))
* nRF53 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf5340))
* nRF91 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf9160))
Most peripherals are supported, but can vary between chip families. To check what's available, make sure to pick the MCU you're targeting in the top menu in the [documentation](https://docs.embassy.dev/embassy-nrf).
Most peripherals are supported. To check what's available, make sure to pick the MCU you're targeting in the top menu in the [documentation](https://docs.embassy.dev/embassy-nrf).
For MCUs with TrustZone support, both Secure (S) and Non-Secure (NS) modes are supported. Running in Secure mode
allows running Rust code without a SPM or TF-M binary, saving flash space and simplifying development.

File diff suppressed because it is too large Load diff

View file

@ -1,174 +0,0 @@
pub use nrf51_pac as pac;
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
pub const EASY_DMA_SIZE: usize = (1 << 14) - 1;
pub const FLASH_SIZE: usize = 128 * 1024;
embassy_hal_internal::peripherals! {
// RTC
RTC0,
RTC1,
// WDT
WDT,
// NVMC
NVMC,
// RNG
RNG,
// UARTE
UART0,
// SPI/TWI
TWI0,
SPI0,
// ADC
ADC,
// TIMER
TIMER0,
TIMER1,
TIMER2,
// GPIOTE
GPIOTE_CH0,
GPIOTE_CH1,
GPIOTE_CH2,
GPIOTE_CH3,
// PPI
PPI_CH0,
PPI_CH1,
PPI_CH2,
PPI_CH3,
PPI_CH4,
PPI_CH5,
PPI_CH6,
PPI_CH7,
PPI_CH8,
PPI_CH9,
PPI_CH10,
PPI_CH11,
PPI_CH12,
PPI_CH13,
PPI_CH14,
PPI_CH15,
PPI_GROUP0,
PPI_GROUP1,
PPI_GROUP2,
PPI_GROUP3,
// GPIO port 0
P0_00,
P0_01,
P0_02,
P0_03,
P0_04,
P0_05,
P0_06,
P0_07,
P0_08,
P0_09,
P0_10,
P0_11,
P0_12,
P0_13,
P0_14,
P0_15,
P0_16,
P0_17,
P0_18,
P0_19,
P0_20,
P0_21,
P0_22,
P0_23,
P0_24,
P0_25,
P0_26,
P0_27,
P0_28,
P0_29,
P0_30,
P0_31,
// TEMP
TEMP,
// Radio
RADIO,
}
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2);
impl_pin!(P0_03, 0, 3);
impl_pin!(P0_04, 0, 4);
impl_pin!(P0_05, 0, 5);
impl_pin!(P0_06, 0, 6);
impl_pin!(P0_07, 0, 7);
impl_pin!(P0_08, 0, 8);
impl_pin!(P0_09, 0, 9);
impl_pin!(P0_10, 0, 10);
impl_pin!(P0_11, 0, 11);
impl_pin!(P0_12, 0, 12);
impl_pin!(P0_13, 0, 13);
impl_pin!(P0_14, 0, 14);
impl_pin!(P0_15, 0, 15);
impl_pin!(P0_16, 0, 16);
impl_pin!(P0_17, 0, 17);
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
impl_pin!(P0_21, 0, 21);
impl_pin!(P0_22, 0, 22);
impl_pin!(P0_23, 0, 23);
impl_pin!(P0_24, 0, 24);
impl_pin!(P0_25, 0, 25);
impl_pin!(P0_26, 0, 26);
impl_pin!(P0_27, 0, 27);
impl_pin!(P0_28, 0, 28);
impl_pin!(P0_29, 0, 29);
impl_pin!(P0_30, 0, 30);
impl_pin!(P0_31, 0, 31);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,
UART0,
SPI0_TWI0,
SPI1_TWI1,
GPIOTE,
ADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
LPCOMP,
SWI0,
SWI1,
SWI2,
SWI3,
SWI4,
SWI5,
);

View file

@ -129,9 +129,6 @@ embassy_hal_internal::peripherals! {
// QDEC
QDEC,
// Radio
RADIO,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@ -212,8 +209,6 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -135,9 +135,6 @@ embassy_hal_internal::peripherals! {
// PDM
PDM,
// Radio
RADIO,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@ -238,8 +235,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -135,9 +135,6 @@ embassy_hal_internal::peripherals! {
// PDM
PDM,
// Radio
RADIO,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@ -240,8 +237,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -130,9 +130,6 @@ embassy_hal_internal::peripherals! {
// QDEC
QDEC,
// Radio
RADIO,
}
impl_usb!(USBD, USBD, USBD);
@ -227,8 +224,6 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -150,9 +150,6 @@ embassy_hal_internal::peripherals! {
// PDM
PDM,
// Radio
RADIO,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@ -267,8 +264,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -170,9 +170,6 @@ embassy_hal_internal::peripherals! {
// I2S
I2S,
// Radio
RADIO,
}
impl_usb!(USBD, USBD, USBD);
@ -309,8 +306,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -173,9 +173,6 @@ embassy_hal_internal::peripherals! {
// I2S
I2S,
// Radio
RADIO,
}
impl_usb!(USBD, USBD, USBD);
@ -314,8 +311,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View file

@ -248,9 +248,6 @@ embassy_hal_internal::peripherals! {
P1_13,
P1_14,
P1_15,
// Radio
RADIO,
}
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
@ -348,8 +345,6 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
impl_ppi_channel!(PPI_CH30, 30 => configurable);
impl_ppi_channel!(PPI_CH31, 31 => configurable);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
CLOCK_POWER,
RADIO,

View file

@ -8,13 +8,7 @@ use cfg_if::cfg_if;
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use self::sealed::Pin as _;
#[cfg(feature = "nrf51")]
use crate::pac::gpio;
#[cfg(feature = "nrf51")]
use crate::pac::gpio::pin_cnf::{DRIVE_A, PULL_A};
#[cfg(not(feature = "nrf51"))]
use crate::pac::p0 as gpio;
#[cfg(not(feature = "nrf51"))]
use crate::pac::p0::pin_cnf::{DRIVE_A, PULL_A};
use crate::{pac, Peripheral};
@ -42,14 +36,14 @@ pub enum Pull {
}
/// GPIO input driver.
pub struct Input<'d> {
pub(crate) pin: Flex<'d>,
pub struct Input<'d, T: Pin> {
pub(crate) pin: Flex<'d, T>,
}
impl<'d> Input<'d> {
impl<'d, T: Pin> Input<'d, T> {
/// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, pull: Pull) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd, pull: Pull) -> Self {
let mut pin = Flex::new(pin);
pin.set_as_input(pull);
@ -128,14 +122,14 @@ pub enum OutputDrive {
}
/// GPIO output driver.
pub struct Output<'d> {
pub(crate) pin: Flex<'d>,
pub struct Output<'d, T: Pin> {
pub(crate) pin: Flex<'d, T>,
}
impl<'d> Output<'d> {
impl<'d, T: Pin> Output<'d, T> {
/// Create GPIO output driver for a [Pin] with the provided [Level] and [OutputDriver] configuration.
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, drive: OutputDrive) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, drive: OutputDrive) -> Self {
let mut pin = Flex::new(pin);
match initial_output {
Level::High => pin.set_high(),
@ -189,7 +183,7 @@ impl<'d> Output<'d> {
}
}
pub(crate) fn convert_drive(drive: OutputDrive) -> DRIVE_A {
fn convert_drive(drive: OutputDrive) -> DRIVE_A {
match drive {
OutputDrive::Standard => DRIVE_A::S0S1,
OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1,
@ -215,20 +209,20 @@ fn convert_pull(pull: Pull) -> PULL_A {
/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
/// mode.
pub struct Flex<'d> {
pub(crate) pin: PeripheralRef<'d, AnyPin>,
pub struct Flex<'d, T: Pin> {
pub(crate) pin: PeripheralRef<'d, T>,
}
impl<'d> Flex<'d> {
impl<'d, T: Pin> Flex<'d, T> {
/// Wrap the pin in a `Flex`.
///
/// The pin remains disconnected. The initial output level is unspecified, but can be changed
/// before the pin is put into output mode.
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(pin);
// Pin will be in disconnected state.
Self { pin: pin.map_into() }
Self { pin }
}
/// Put the pin into input mode.
@ -355,7 +349,7 @@ impl<'d> Flex<'d> {
}
}
impl<'d> Drop for Flex<'d> {
impl<'d, T: Pin> Drop for Flex<'d, T> {
fn drop(&mut self) {
self.pin.conf().reset();
}
@ -382,9 +376,6 @@ pub(crate) mod sealed {
fn block(&self) -> &gpio::RegisterBlock {
unsafe {
match self.pin_port() / 32 {
#[cfg(feature = "nrf51")]
0 => &*pac::GPIO::ptr(),
#[cfg(not(feature = "nrf51"))]
0 => &*pac::P0::ptr(),
#[cfg(feature = "_gpio-p1")]
1 => &*pac::P1::ptr(),
@ -487,7 +478,6 @@ impl<'a, P: Pin> PselBits for Option<PeripheralRef<'a, P>> {
}
}
#[allow(dead_code)]
pub(crate) fn deconfigure_pin(psel_bits: u32) {
if psel_bits & 0x8000_0000 != 0 {
return;
@ -520,7 +510,7 @@ macro_rules! impl_pin {
mod eh02 {
use super::*;
impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Input<'d, T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
@ -532,7 +522,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::OutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Output<'d, T> {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
@ -544,7 +534,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -554,7 +544,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
@ -566,7 +556,7 @@ mod eh02 {
/// Implement [`embedded_hal_02::digital::v2::InputPin`] for [`Flex`];
///
/// If the pin is not in input mode the result is unspecified.
impl<'d> embedded_hal_02::digital::v2::InputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Flex<'d, T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
@ -578,7 +568,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::OutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Flex<'d, T> {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
@ -590,7 +580,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -600,7 +590,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
@ -610,11 +600,11 @@ mod eh02 {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for Input<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::InputPin for Input<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}
@ -624,11 +614,11 @@ impl<'d> embedded_hal_1::digital::InputPin for Input<'d> {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for Output<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Output<'d, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -638,7 +628,7 @@ impl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {
}
}
impl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_high())
}
@ -648,14 +638,14 @@ impl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> {
type Error = Infallible;
}
/// Implement [`InputPin`] for [`Flex`];
///
/// If the pin is not in input mode the result is unspecified.
impl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}
@ -665,7 +655,7 @@ impl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {
}
}
impl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -675,7 +665,7 @@ impl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {
}
}
impl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_high())
}

View file

@ -13,10 +13,6 @@ use crate::interrupt::InterruptExt;
use crate::ppi::{Event, Task};
use crate::{interrupt, pac, peripherals};
#[cfg(feature = "nrf51")]
/// Amount of GPIOTE channels in the chip.
const CHANNEL_COUNT: usize = 4;
#[cfg(not(feature = "_nrf51"))]
/// Amount of GPIOTE channels in the chip.
const CHANNEL_COUNT: usize = 8;
@ -65,20 +61,16 @@ fn regs() -> &'static pac::gpiote::RegisterBlock {
}
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
// no latched GPIO detect in nrf51.
#[cfg(not(feature = "_nrf51"))]
{
#[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] };
#[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840")))]
let ports = unsafe { &[&*pac::P0::ptr()] };
#[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] };
#[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))]
let ports = unsafe { &[&*pac::P0::ptr()] };
for &p in ports {
// Enable latched detection
p.detectmode.write(|w| w.detectmode().ldetect());
// Clear latch
p.latch.write(|w| unsafe { w.bits(0xFFFFFFFF) })
}
for &p in ports {
// Enable latched detection
p.detectmode.write(|w| w.detectmode().ldetect());
// Clear latch
p.latch.write(|w| unsafe { w.bits(0xFFFFFFFF) })
}
// Enable interrupts
@ -86,7 +78,7 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
let irq = interrupt::GPIOTE0;
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
let irq = interrupt::GPIOTE1;
#[cfg(any(feature = "_nrf51", feature = "_nrf52", feature = "nrf5340-net"))]
#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))]
let irq = interrupt::GPIOTE;
irq.unpend();
@ -111,7 +103,7 @@ fn GPIOTE1() {
unsafe { handle_gpiote_interrupt() };
}
#[cfg(any(feature = "_nrf51", feature = "_nrf52", feature = "nrf5340-net"))]
#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))]
#[cfg(feature = "rt")]
#[interrupt]
fn GPIOTE() {
@ -133,29 +125,9 @@ unsafe fn handle_gpiote_interrupt() {
#[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
let ports = &[&*pac::P0::ptr(), &*pac::P1::ptr()];
#[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840")))]
#[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))]
let ports = &[&*pac::P0::ptr()];
#[cfg(feature = "_nrf51")]
let ports = unsafe { &[&*pac::GPIO::ptr()] };
#[cfg(feature = "_nrf51")]
for (port, &p) in ports.iter().enumerate() {
let inp = p.in_.read().bits();
for pin in 0..32 {
let fired = match p.pin_cnf[pin as usize].read().sense().variant() {
Some(pac::gpio::pin_cnf::SENSE_A::HIGH) => inp & (1 << pin) != 0,
Some(pac::gpio::pin_cnf::SENSE_A::LOW) => inp & (1 << pin) == 0,
_ => false,
};
if fired {
PORT_WAKERS[port * 32 + pin as usize].wake();
p.pin_cnf[pin as usize].modify(|_, w| w.sense().disabled());
}
}
}
#[cfg(not(feature = "_nrf51"))]
for (port, &p) in ports.iter().enumerate() {
let bits = p.latch.read().bits();
for pin in BitIter(bits) {
@ -184,12 +156,12 @@ impl Iterator for BitIter {
}
/// GPIOTE channel driver in input mode
pub struct InputChannel<'d> {
ch: PeripheralRef<'d, AnyChannel>,
pin: Input<'d>,
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
ch: PeripheralRef<'d, C>,
pin: Input<'d, T>,
}
impl<'d> Drop for InputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> {
fn drop(&mut self) {
let g = regs();
let num = self.ch.number();
@ -198,9 +170,9 @@ impl<'d> Drop for InputChannel<'d> {
}
}
impl<'d> InputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
/// Create a new GPIOTE input channel driver.
pub fn new(ch: impl Peripheral<P = impl Channel> + 'd, pin: Input<'d>, polarity: InputChannelPolarity) -> Self {
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
into_ref!(ch);
let g = regs();
@ -223,7 +195,7 @@ impl<'d> InputChannel<'d> {
g.events_in[num].reset();
InputChannel { ch: ch.map_into(), pin }
InputChannel { ch, pin }
}
/// Asynchronously wait for an event in this channel.
@ -255,12 +227,12 @@ impl<'d> InputChannel<'d> {
}
/// GPIOTE channel driver in output mode
pub struct OutputChannel<'d> {
ch: PeripheralRef<'d, AnyChannel>,
_pin: Output<'d>,
pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
ch: PeripheralRef<'d, C>,
_pin: Output<'d, T>,
}
impl<'d> Drop for OutputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
fn drop(&mut self) {
let g = regs();
let num = self.ch.number();
@ -269,9 +241,9 @@ impl<'d> Drop for OutputChannel<'d> {
}
}
impl<'d> OutputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
/// Create a new GPIOTE output channel driver.
pub fn new(ch: impl Peripheral<P = impl Channel> + 'd, pin: Output<'d>, polarity: OutputChannelPolarity) -> Self {
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
into_ref!(ch);
let g = regs();
let num = ch.number();
@ -295,10 +267,7 @@ impl<'d> OutputChannel<'d> {
unsafe { w.psel().bits(pin.pin.pin.pin()) }
});
OutputChannel {
ch: ch.map_into(),
_pin: pin,
}
OutputChannel { ch, _pin: pin }
}
/// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle).
@ -379,7 +348,7 @@ impl<'a> Future for PortInputFuture<'a> {
}
}
impl<'d> Input<'d> {
impl<'d, T: GpioPin> Input<'d, T> {
/// Wait until the pin is high. If it is already high, return immediately.
pub async fn wait_for_high(&mut self) {
self.pin.wait_for_high().await
@ -406,7 +375,7 @@ impl<'d> Input<'d> {
}
}
impl<'d> Flex<'d> {
impl<'d, T: GpioPin> Flex<'d, T> {
/// Wait until the pin is high. If it is already high, return immediately.
pub async fn wait_for_high(&mut self) {
self.pin.conf().modify(|_, w| w.sense().high());
@ -451,7 +420,7 @@ mod sealed {
/// GPIOTE channel trait.
///
/// Implemented by all GPIOTE channels.
pub trait Channel: sealed::Channel + Into<AnyChannel> + Sized + 'static {
pub trait Channel: sealed::Channel + Sized {
/// Get the channel number.
fn number(&self) -> usize;
@ -491,12 +460,6 @@ macro_rules! impl_channel {
$number as usize
}
}
impl From<peripherals::$type> for AnyChannel {
fn from(val: peripherals::$type) -> Self {
Channel::degrade(val)
}
}
};
}
@ -504,13 +467,9 @@ impl_channel!(GPIOTE_CH0, 0);
impl_channel!(GPIOTE_CH1, 1);
impl_channel!(GPIOTE_CH2, 2);
impl_channel!(GPIOTE_CH3, 3);
#[cfg(not(feature = "nrf51"))]
impl_channel!(GPIOTE_CH4, 4);
#[cfg(not(feature = "nrf51"))]
impl_channel!(GPIOTE_CH5, 5);
#[cfg(not(feature = "nrf51"))]
impl_channel!(GPIOTE_CH6, 6);
#[cfg(not(feature = "nrf51"))]
impl_channel!(GPIOTE_CH7, 7);
// ====================
@ -518,7 +477,7 @@ impl_channel!(GPIOTE_CH7, 7);
mod eh02 {
use super::*;
impl<'d> embedded_hal_02::digital::v2::InputPin for InputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> embedded_hal_02::digital::v2::InputPin for InputChannel<'d, C, T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
@ -531,11 +490,11 @@ mod eh02 {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for InputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::ErrorType for InputChannel<'d, C, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::InputPin for InputChannel<'d> {
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.pin.is_high())
}
@ -545,7 +504,7 @@ impl<'d> embedded_hal_1::digital::InputPin for InputChannel<'d> {
}
}
impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_high().await)
}
@ -567,7 +526,7 @@ impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
}
}
impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_high().await)
}

View file

@ -40,16 +40,10 @@ pub(crate) mod util;
#[cfg(feature = "_time-driver")]
mod time_driver;
#[cfg(not(feature = "nrf51"))]
pub mod buffered_uarte;
pub mod gpio;
#[cfg(feature = "gpiote")]
pub mod gpiote;
// TODO: tested on other chips
#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340-app")))]
pub mod radio;
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
pub mod i2s;
pub mod nvmc;
@ -64,12 +58,7 @@ pub mod nvmc;
))]
pub mod pdm;
pub mod ppi;
#[cfg(not(any(
feature = "nrf51",
feature = "nrf52805",
feature = "nrf52820",
feature = "_nrf5340-net"
)))]
#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod pwm;
#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))]
pub mod qdec;
@ -77,20 +66,15 @@ pub mod qdec;
pub mod qspi;
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))]
pub mod rng;
#[cfg(not(any(feature = "nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc;
#[cfg(not(feature = "nrf51"))]
pub mod spim;
#[cfg(not(feature = "nrf51"))]
pub mod spis;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub mod temp;
pub mod timer;
#[cfg(not(feature = "nrf51"))]
pub mod twim;
#[cfg(not(feature = "nrf51"))]
pub mod twis;
#[cfg(not(feature = "nrf51"))]
pub mod uarte;
#[cfg(any(
feature = "_nrf5340-app",
@ -103,7 +87,6 @@ pub mod usb;
pub mod wdt;
// This mod MUST go last, so that it sees all the `impl_foo!` macros
#[cfg_attr(feature = "nrf51", path = "chips/nrf51.rs")]
#[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")]
#[cfg_attr(feature = "nrf52810", path = "chips/nrf52810.rs")]
#[cfg_attr(feature = "nrf52811", path = "chips/nrf52811.rs")]
@ -341,7 +324,6 @@ mod consts {
pub const APPROTECT_DISABLED: u32 = 0x0000_005a;
}
#[cfg(not(feature = "nrf51"))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum WriteResult {
@ -353,12 +335,10 @@ enum WriteResult {
Failed,
}
#[cfg(not(feature = "nrf51"))]
unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult {
uicr_write_masked(address, value, 0xFFFF_FFFF)
}
#[cfg(not(feature = "nrf51"))]
unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult {
let curr_val = address.read_volatile();
if curr_val & mask == value & mask {
@ -391,11 +371,9 @@ pub fn init(config: config::Config) -> Peripherals {
// before doing anything important.
let peripherals = Peripherals::take();
#[allow(unused_mut)]
let mut needs_reset = false;
// Setup debug protection.
#[cfg(not(feature = "nrf51"))]
match config.debug {
config::Debug::Allowed => {
#[cfg(feature = "_nrf52")]
@ -511,7 +489,7 @@ pub fn init(config: config::Config) -> Peripherals {
}
// Configure LFCLK.
#[cfg(not(any(feature = "nrf51", feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
match config.lfclk_source {
config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().rc()),
config::LfclkSource::Synthesized => r.lfclksrc.write(|w| w.src().synth()),

View file

@ -160,7 +160,7 @@ impl<'d> NorFlash for Nvmc<'d> {
if offset as usize + bytes.len() > FLASH_SIZE {
return Err(Error::OutOfBounds);
}
if offset as usize % 4 != 0 || bytes.len() % 4 != 0 {
if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 {
return Err(Error::Unaligned);
}

View file

@ -1,4 +1,4 @@
//! Pulse Density Modulation (PDM) mirophone driver
//! Pulse Density Modulation (PDM) mirophone driver.
#![macro_use]
@ -26,7 +26,7 @@ pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
use crate::{interrupt, Peripheral};
/// Interrupt handler
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
@ -56,12 +56,12 @@ pub struct Pdm<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
}
/// PDM error
/// PDM error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Buffer is too long
/// Buffer is too long.
BufferTooLong,
/// Buffer is empty
BufferZeroLength,
@ -75,13 +75,13 @@ static DUMMY_BUFFER: [i16; 1] = [0; 1];
/// The state of a continuously running sampler. While it reflects
/// the progress of a sampler, it also signals what should be done
/// next. For example, if the sampler has stopped then the PDM implementation
/// can then tear down its infrastructure
/// next. For example, if the sampler has stopped then the Pdm implementation
/// can then tear down its infrastructure.
#[derive(PartialEq)]
pub enum SamplerState {
/// The sampler processed the samples and is ready for more
/// The sampler processed the samples and is ready for more.
Sampled,
/// The sampler is done processing samples
/// The sampler is done processing samples.
Stopped,
}
@ -145,12 +145,15 @@ impl<'d, T: Instance> Pdm<'d, T> {
}
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
let gain_to_bits = |gain: I7F1| -> u8 {
let gain = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50);
unsafe { core::mem::transmute(gain) }
};
let gain_left = gain_to_bits(gain_left);
let gain_right = gain_to_bits(gain_right);
let gain_left = gain_left
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.clamp(0, 0x50);
let gain_right = gain_right
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.clamp(0, 0x50);
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
}
@ -160,12 +163,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
Self::_set_gain(T::regs(), gain_left, gain_right)
}
/// Start sampling microphone data into a dummy buffer.
/// Useful to start the microphone and keep it active between recording samples.
/// Start sampling microphon data into a dummy buffer
/// Usefull to start the microphon and keep it active between recording samples
pub async fn start(&mut self) {
let r = T::regs();
// start dummy sampling because microphone needs some setup time
// start dummy sampling because microphon needs some setup time
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
@ -176,16 +179,16 @@ impl<'d, T: Instance> Pdm<'d, T> {
r.tasks_start.write(|w| unsafe { w.bits(1) });
}
/// Stop sampling microphone data inta a dummy buffer
/// Stop sampling microphon data inta a dummy buffer
pub async fn stop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.events_started.reset();
}
/// Sample data into the given buffer
/// Sample data into the given buffer.
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
@ -300,7 +303,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
});
// Don't reorder the start event before the previous writes. Hopefully self
// wouldn't happen anyway
// wouldn't happen anyway.
compiler_fence(Ordering::SeqCst);
r.tasks_start.write(|w| unsafe { w.bits(1) });
@ -311,11 +314,11 @@ impl<'d, T: Instance> Pdm<'d, T> {
let drop = OnDrop::new(|| {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
// N.B. It would be better if this were async, but Drop only support sync code
// N.B. It would be better if this were async, but Drop only support sync code.
while r.events_stopped.read().bits() != 0 {}
});
// Wait for events and complete when the sampler indicates it has had enough
// Wait for events and complete when the sampler indicates it has had enough.
poll_fn(|cx| {
let r = T::regs();
@ -328,7 +331,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
r.intenset.write(|w| w.end().set());
if !done {
// Discard the last buffer after the user requested a stop
// Discard the last buffer after the user requested a stop.
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
let next_buffer = 1 - current_buffer;
current_buffer = next_buffer;
@ -402,7 +405,7 @@ impl Default for Config {
}
}
/// PDM operation mode
/// PDM operation mode.
#[derive(PartialEq)]
pub enum OperationMode {
/// Mono (1 channel)
@ -473,9 +476,9 @@ pub(crate) mod sealed {
}
}
/// PDM peripheral instance
/// PDM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}

View file

@ -284,7 +284,6 @@ impl ConfigurableChannel for AnyConfigurableChannel {
}
}
#[cfg(not(feature = "nrf51"))]
macro_rules! impl_ppi_channel {
($type:ident, $number:expr) => {
impl crate::ppi::sealed::Channel for peripherals::$type {}

View file

@ -1,6 +1,6 @@
use embassy_hal_internal::into_ref;
use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
use crate::{pac, Peripheral};
impl<'d> Task<'d> {
@ -19,7 +19,7 @@ pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock {
}
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
impl<'d, C: super::StaticChannel> Ppi<'d, C, 0, 1> {
impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
/// Configure PPI channel to trigger `task`.
pub fn new_zero_to_one(ch: impl Peripheral<P = C> + 'd, task: Task) -> Self {
into_ref!(ch);
@ -84,7 +84,6 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for
let n = self.ch.number();
r.ch[n].eep.write(|w| unsafe { w.bits(0) });
r.ch[n].tep.write(|w| unsafe { w.bits(0) });
#[cfg(not(feature = "nrf51"))]
r.fork[n].tep.write(|w| unsafe { w.bits(0) });
}
}

View file

@ -47,8 +47,6 @@ pub enum Error {
}
const MAX_SEQUENCE_LEN: usize = 32767;
/// The used pwm clock frequency
pub const PWM_CLK_HZ: u32 = 16_000_000;
impl<'d, T: Instance> SequencePwm<'d, T> {
/// Create a new 1-channel PWM
@ -444,7 +442,7 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> {
return Err(Error::SequenceTimesAtLeastOne);
}
self.stop();
let _ = self.stop();
let r = T::regs();
@ -507,7 +505,7 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> {
impl<'d, 's, T: Instance> Drop for Sequencer<'d, 's, T> {
fn drop(&mut self) {
self.stop();
let _ = self.stop();
}
}
@ -697,7 +695,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
// Enable
r.enable.write(|w| w.enable().enabled());
r.seq0.ptr.write(|w| unsafe { w.bits((pwm.duty).as_ptr() as u32) });
r.seq0.ptr.write(|w| unsafe { w.bits((&pwm.duty).as_ptr() as u32) });
r.seq0.cnt.write(|w| unsafe { w.bits(4) });
r.seq0.refresh.write(|w| unsafe { w.bits(0) });
@ -715,13 +713,6 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
pwm
}
/// Returns the enable state of the pwm counter
#[inline(always)]
pub fn is_enabled(&self) -> bool {
let r = T::regs();
r.enable.read().enable().bit_is_set()
}
/// Enables the PWM generator.
#[inline(always)]
pub fn enable(&self) {
@ -736,11 +727,6 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
r.enable.write(|w| w.enable().disabled());
}
/// Returns the current duty of the channel
pub fn duty(&self, channel: usize) -> u16 {
self.duty[channel]
}
/// Sets duty cycle (15 bit) for a PWM channel.
pub fn set_duty(&mut self, channel: usize, duty: u16) {
let r = T::regs();
@ -748,7 +734,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
self.duty[channel] = duty & 0x7FFF;
// reload ptr in case self was moved
r.seq0.ptr.write(|w| unsafe { w.bits((self.duty).as_ptr() as u32) });
r.seq0.ptr.write(|w| unsafe { w.bits((&self.duty).as_ptr() as u32) });
// defensive before seqstart
compiler_fence(Ordering::SeqCst);
@ -760,9 +746,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
// defensive wait until waveform is loaded after seqstart so set_duty
// can't be called again while dma is still reading
if self.is_enabled() {
while r.events_seqend[0].read().bits() == 0 {}
}
while r.events_seqend[0].read().bits() == 0 {}
}
/// Sets the PWM clock prescaler.
@ -804,7 +788,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
/// Sets the PWM output frequency.
#[inline(always)]
pub fn set_period(&self, freq: u32) {
let clk = PWM_CLK_HZ >> (self.prescaler() as u8);
let clk = 16_000_000u32 >> (self.prescaler() as u8);
let duty = clk / freq;
self.set_max_duty(duty.min(32767) as u16);
}
@ -812,7 +796,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
/// Returns the PWM output frequency.
#[inline(always)]
pub fn period(&self) -> u32 {
let clk = PWM_CLK_HZ >> (self.prescaler() as u8);
let clk = 16_000_000u32 >> (self.prescaler() as u8);
let max_duty = self.max_duty() as u32;
clk / max_duty
}

View file

@ -172,17 +172,18 @@ impl<'d, T: Instance> Qdec<'d, T> {
t.intenset.write(|w| w.reportrdy().set());
unsafe { t.tasks_readclracc.write(|w| w.bits(1)) };
poll_fn(|cx| {
let value = poll_fn(|cx| {
T::state().waker.register(cx.waker());
if t.events_reportrdy.read().bits() == 0 {
Poll::Pending
return Poll::Pending;
} else {
t.events_reportrdy.reset();
let acc = t.accread.read().bits();
Poll::Ready(acc as i16)
}
})
.await
.await;
value
}
}

View file

@ -402,7 +402,7 @@ impl<'d, T: Instance> Qspi<'d, T> {
/// a raw bus, not with flash memory.
pub async fn read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
// Avoid blocking_wait_ready() blocking forever on zero-length buffers.
if data.is_empty() {
if data.len() == 0 {
return Ok(());
}
@ -423,7 +423,7 @@ impl<'d, T: Instance> Qspi<'d, T> {
/// a raw bus, not with flash memory.
pub async fn write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
// Avoid blocking_wait_ready() blocking forever on zero-length buffers.
if data.is_empty() {
if data.len() == 0 {
return Ok(());
}
@ -444,7 +444,7 @@ impl<'d, T: Instance> Qspi<'d, T> {
/// a raw bus, not with flash memory.
pub fn blocking_read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
// Avoid blocking_wait_ready() blocking forever on zero-length buffers.
if data.is_empty() {
if data.len() == 0 {
return Ok(());
}
@ -460,7 +460,7 @@ impl<'d, T: Instance> Qspi<'d, T> {
/// a raw bus, not with flash memory.
pub fn blocking_write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
// Avoid blocking_wait_ready() blocking forever on zero-length buffers.
if data.is_empty() {
if data.len() == 0 {
return Ok(());
}

View file

@ -1,417 +0,0 @@
//! Radio driver implementation focused on Bluetooth Low-Energy transmission.
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
pub use pac::radio::mode::MODE_A as Mode;
#[cfg(not(feature = "nrf51"))]
use pac::radio::pcnf0::PLEN_A as PreambleLength;
use crate::interrupt::typelevel::Interrupt;
use crate::radio::*;
pub use crate::radio::{Error, TxPower};
use crate::util::slice_in_ram_or;
/// Radio driver.
pub struct Radio<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Radio<'d, T> {
/// Create a new radio driver.
pub fn new(
radio: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
into_ref!(radio);
let r = T::regs();
r.pcnf1.write(|w| unsafe {
// It is 0 bytes long in a standard BLE packet
w.statlen()
.bits(0)
// MaxLen configures the maximum packet payload plus add-on size in
// number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure
// that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means
// that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
// packet larger than MAXLEN, the payload will be truncated at MAXLEN
//
// To simplify the implementation, It is setted as the maximum value
// and the length of the packet is controlled only by the LENGTH field in the packet
.maxlen()
.bits(255)
// Configure the length of the address field in the packet
// The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
// The base address is truncated from the least significant byte if the BALEN is less than 4
//
// BLE address is always 4 bytes long
.balen()
.bits(3) // 3 bytes base address (+ 1 prefix);
// Configure the endianess
// For BLE is always little endian (LSB first)
.endian()
.little()
// Data whitening is used to avoid long sequences of zeros or
// ones, e.g., 0b0000000 or 0b1111111, in the data bit stream.
// The whitener and de-whitener are defined the same way,
// using a 7-bit linear feedback shift register with the
// polynomial x7 + x4 + 1.
//
// In BLE Whitening shall be applied on the PDU and CRC of all
// Link Layer packets and is performed after the CRC generation
// in the transmitter. No other parts of the packets are whitened.
// De-whitening is performed before the CRC checking in the receiver
// Before whitening or de-whitening, the shift register should be
// initialized based on the channel index.
.whiteen()
.set_bit()
});
// Configure CRC
r.crccnf.write(|w| {
// In BLE the CRC shall be calculated on the PDU of all Link Layer
// packets (even if the packet is encrypted).
// It skips the address field
w.skipaddr()
.skip()
// In BLE 24-bit CRC = 3 bytes
.len()
.three()
});
// Ch map between 2400 MHZ .. 2500 MHz
// All modes use this range
#[cfg(not(feature = "nrf51"))]
r.frequency.write(|w| w.map().default());
// Configure shortcuts to simplify and speed up sending and receiving packets.
r.shorts.write(|w| {
// start transmission/recv immediately after ramp-up
// disable radio when transmission/recv is done
w.ready_start().enabled().end_disable().enabled()
});
// Enable NVIC interrupt
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: radio }
}
fn state(&self) -> RadioState {
super::state(T::regs())
}
/// Set the radio mode
///
/// The radio must be disabled before calling this function
pub fn set_mode(&mut self, mode: Mode) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.mode.write(|w| w.mode().variant(mode));
#[cfg(not(feature = "nrf51"))]
r.pcnf0.write(|w| {
w.plen().variant(match mode {
Mode::BLE_1MBIT => PreambleLength::_8BIT,
Mode::BLE_2MBIT => PreambleLength::_16BIT,
#[cfg(any(
feature = "nrf52811",
feature = "nrf52820",
feature = "nrf52833",
feature = "nrf52840",
feature = "_nrf5340-net"
))]
Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE,
_ => unimplemented!(),
})
});
}
/// Set the header size changing the S1's len field
///
/// The radio must be disabled before calling this function
pub fn set_header_expansion(&mut self, use_s1_field: bool) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
// s1 len in bits
let s1len: u8 = match use_s1_field {
false => 0,
true => 8,
};
r.pcnf0.write(|w| unsafe {
w
// Configure S0 to 1 byte length, this will represent the Data/Adv header flags
.s0len()
.set_bit()
// Configure the length (in bits) field to 1 byte length, this will represent the length of the payload
// and also be used to know how many bytes to read/write from/to the buffer
.lflen()
.bits(8)
// Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE.
.s1len()
.bits(s1len)
});
}
/// Set initial data whitening value
/// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream
/// On BLE the initial value is the channel index | 0x40
///
/// The radio must be disabled before calling this function
pub fn set_whitening_init(&mut self, whitening_init: u8) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(whitening_init) });
}
/// Set the central frequency to be used
/// It should be in the range 2400..2500
///
/// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change)
pub fn set_frequency(&mut self, frequency: u32) {
assert!(self.state() == RadioState::DISABLED);
assert!((2400..=2500).contains(&frequency));
let r = T::regs();
r.frequency
.write(|w| unsafe { w.frequency().bits((frequency - 2400) as u8) });
}
/// Set the acess address
/// This address is always constants for advertising
/// And a random value generate on each connection
/// It is used to filter the packages
///
/// The radio must be disabled before calling this function
pub fn set_access_address(&mut self, access_address: u32) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
// Configure logical address
// The byte ordering on air is always least significant byte first for the address
// So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA
// The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA
r.prefix0
.write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) });
// The base address is truncated from the least significant byte (because the BALEN is less than 4)
// So it shifts the address to the right
r.base0.write(|w| unsafe { w.bits(access_address << 8) });
// Don't match tx address
r.txaddress.write(|w| unsafe { w.txaddress().bits(0) });
// Match on logical address
// This config only filter the packets by the address,
// so only packages send to the previous address
// will finish the reception (TODO: check the explanation)
r.rxaddresses.write(|w| {
w.addr0()
.enabled()
.addr1()
.enabled()
.addr2()
.enabled()
.addr3()
.enabled()
.addr4()
.enabled()
});
}
/// Set the CRC polynomial
/// It only uses the 24 least significant bits
///
/// The radio must be disabled before calling this function
pub fn set_crc_poly(&mut self, crc_poly: u32) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.crcpoly.write(|w| unsafe {
// Configure the CRC polynomial
// Each term in the CRC polynomial is mapped to a bit in this
// register which index corresponds to the term's exponent.
// The least significant term/bit is hard-wired internally to
// 1, and bit number 0 of the register content is ignored by
// the hardware. The following example is for an 8 bit CRC
// polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
w.crcpoly().bits(crc_poly & 0xFFFFFF)
});
}
/// Set the CRC init value
/// It only uses the 24 least significant bits
/// The CRC initial value varies depending of the PDU type
///
/// The radio must be disabled before calling this function
pub fn set_crc_init(&mut self, crc_init: u32) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) });
}
/// Set the radio tx power
///
/// The radio must be disabled before calling this function
pub fn set_tx_power(&mut self, tx_power: TxPower) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.txpower.write(|w| w.txpower().variant(tx_power));
}
/// Set buffer to read/write
///
/// This method is unsound. You should guarantee that the buffer will live
/// for the life time of the transmission or if the buffer will be modified.
/// Also if the buffer is smaller than the packet length, the radio will
/// read/write memory out of the buffer bounds.
fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
let r = T::regs();
// Here it consider that the length of the packet is
// correctly set in the buffer, otherwise it will send
// unowned regions of memory
let ptr = buffer.as_ptr();
// Configure the payload
r.packetptr.write(|w| unsafe { w.bits(ptr as u32) });
Ok(())
}
/// Send packet
/// If the length byte in the package is greater than the buffer length
/// the radio will read memory out of the buffer bounds
pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> {
self.set_buffer(buffer)?;
let r = T::regs();
self.trigger_and_wait_end(move || {
// Initialize the transmission
// trace!("txen");
r.tasks_txen.write(|w| unsafe { w.bits(1) });
})
.await;
Ok(())
}
/// Receive packet
/// If the length byte in the received package is greater than the buffer length
/// the radio will write memory out of the buffer bounds
pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.set_buffer(buffer)?;
let r = T::regs();
self.trigger_and_wait_end(move || {
// Initialize the transmission
// trace!("rxen");
r.tasks_rxen.write(|w| unsafe { w.bits(1) });
})
.await;
Ok(())
}
async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {
//self.trace_state();
let r = T::regs();
let s = T::state();
// If the Future is dropped before the end of the transmission
// it disable the interrupt and stop the transmission
// to keep the state consistent
let drop = OnDrop::new(|| {
trace!("radio drop: stopping");
r.intenclr.write(|w| w.end().clear());
r.events_end.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
// The docs don't explicitly mention any event to acknowledge the stop task
while r.events_end.read().bits() == 0 {}
trace!("radio drop: stopped");
});
// trace!("radio:enable interrupt");
// Clear some remnant side-effects (TODO: check if this is necessary)
r.events_end.reset();
// Enable interrupt
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
// Trigger the transmission
trigger();
// self.trace_state();
// On poll check if interrupt happen
poll_fn(|cx| {
s.event_waker.register(cx.waker());
if r.events_end.read().bits() == 1 {
// trace!("radio:end");
return core::task::Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
r.events_disabled.reset(); // ACK
// Everthing ends fine, so it disable the drop
drop.defuse();
}
/// Disable the radio
fn disable(&mut self) {
let r = T::regs();
compiler_fence(Ordering::SeqCst);
// If it is already disabled, do nothing
if self.state() != RadioState::DISABLED {
trace!("radio:disable");
// Trigger the disable task
r.tasks_disable.write(|w| unsafe { w.bits(1) });
// Wait until the radio is disabled
while r.events_disabled.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
// Acknowledge it
r.events_disabled.reset();
}
}
}
impl<'d, T: Instance> Drop for Radio<'d, T> {
fn drop(&mut self) {
self.disable();
}
}

View file

@ -1,546 +0,0 @@
//! IEEE 802.15.4 radio driver
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower};
use crate::interrupt::typelevel::Interrupt;
use crate::interrupt::{self};
use crate::Peripheral;
/// Default (IEEE compliant) Start of Frame Delimiter
pub const DEFAULT_SFD: u8 = 0xA7;
// TODO expose the other variants in `pac::CCAMODE_A`
/// Clear Channel Assessment method
pub enum Cca {
/// Carrier sense
CarrierSense,
/// Energy Detection / Energy Above Threshold
EnergyDetection {
/// Energy measurements above this value mean that the channel is assumed to be busy.
/// Note the measurement range is 0..0xFF - where 0 means that the received power was
/// less than 10 dB above the selected receiver sensitivity. This value is not given in dBm,
/// but can be converted. See the nrf52840 Product Specification Section 6.20.12.4
/// for details.
ed_threshold: u8,
},
}
/// IEEE 802.15.4 radio driver.
pub struct Radio<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
needs_enable: bool,
}
impl<'d, T: Instance> Radio<'d, T> {
/// Create a new IEEE 802.15.4 radio driver.
pub fn new(
radio: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
into_ref!(radio);
let r = T::regs();
// Disable and enable to reset peripheral
r.power.write(|w| w.power().disabled());
r.power.write(|w| w.power().enabled());
// Enable 802.15.4 mode
r.mode.write(|w| w.mode().ieee802154_250kbit());
// Configure CRC skip address
r.crccnf.write(|w| w.len().two().skipaddr().ieee802154());
unsafe {
// Configure CRC polynomial and init
r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021));
r.crcinit.write(|w| w.crcinit().bits(0));
r.pcnf0.write(|w| {
// 8-bit on air length
w.lflen()
.bits(8)
// Zero bytes S0 field length
.s0len()
.clear_bit()
// Zero bytes S1 field length
.s1len()
.bits(0)
// Do not include S1 field in RAM if S1 length > 0
.s1incl()
.clear_bit()
// Zero code Indicator length
.cilen()
.bits(0)
// 32-bit zero preamble
.plen()
._32bit_zero()
// Include CRC in length
.crcinc()
.include()
});
r.pcnf1.write(|w| {
// Maximum packet length
w.maxlen()
.bits(Packet::MAX_PSDU_LEN)
// Zero static length
.statlen()
.bits(0)
// Zero base address length
.balen()
.bits(0)
// Little-endian
.endian()
.clear_bit()
// Disable packet whitening
.whiteen()
.clear_bit()
});
}
// Enable NVIC interrupt
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
let mut radio = Self {
_p: radio,
needs_enable: false,
};
radio.set_sfd(DEFAULT_SFD);
radio.set_transmission_power(0);
radio.set_channel(11);
radio.set_cca(Cca::CarrierSense);
radio
}
/// Changes the radio channel
pub fn set_channel(&mut self, channel: u8) {
let r = T::regs();
if channel < 11 || channel > 26 {
panic!("Bad 802.15.4 channel");
}
let frequency_offset = (channel - 10) * 5;
self.needs_enable = true;
r.frequency
.write(|w| unsafe { w.frequency().bits(frequency_offset).map().default() });
}
/// Changes the Clear Channel Assessment method
pub fn set_cca(&mut self, cca: Cca) {
let r = T::regs();
self.needs_enable = true;
match cca {
Cca::CarrierSense => r.ccactrl.write(|w| w.ccamode().carrier_mode()),
Cca::EnergyDetection { ed_threshold } => {
// "[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL
// and writing the CCAEDTHRES field to a chosen value."
r.ccactrl
.write(|w| unsafe { w.ccamode().ed_mode().ccaedthres().bits(ed_threshold) });
}
}
}
/// Changes the Start of Frame Delimiter (SFD)
pub fn set_sfd(&mut self, sfd: u8) {
let r = T::regs();
r.sfd.write(|w| unsafe { w.sfd().bits(sfd) });
}
/// Clear interrupts
pub fn clear_all_interrupts(&mut self) {
let r = T::regs();
r.intenclr.write(|w| unsafe { w.bits(0xffff_ffff) });
}
/// Changes the radio transmission power
pub fn set_transmission_power(&mut self, power: i8) {
let r = T::regs();
self.needs_enable = true;
let tx_power: TxPower = match power {
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
8 => TxPower::POS8D_BM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
7 => TxPower::POS7D_BM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
6 => TxPower::POS6D_BM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
5 => TxPower::POS5D_BM,
#[cfg(not(feature = "_nrf5340-net"))]
4 => TxPower::POS4D_BM,
#[cfg(not(feature = "_nrf5340-net"))]
3 => TxPower::POS3D_BM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
2 => TxPower::POS2D_BM,
0 => TxPower::_0D_BM,
#[cfg(feature = "_nrf5340-net")]
-1 => TxPower::NEG1D_BM,
#[cfg(feature = "_nrf5340-net")]
-2 => TxPower::NEG2D_BM,
#[cfg(feature = "_nrf5340-net")]
-3 => TxPower::NEG3D_BM,
-4 => TxPower::NEG4D_BM,
#[cfg(feature = "_nrf5340-net")]
-5 => TxPower::NEG5D_BM,
#[cfg(feature = "_nrf5340-net")]
-6 => TxPower::NEG6D_BM,
#[cfg(feature = "_nrf5340-net")]
-7 => TxPower::NEG7D_BM,
-8 => TxPower::NEG8D_BM,
-12 => TxPower::NEG12D_BM,
-16 => TxPower::NEG16D_BM,
-20 => TxPower::NEG20D_BM,
-30 => TxPower::NEG30D_BM,
-40 => TxPower::NEG40D_BM,
_ => panic!("Invalid transmission power value"),
};
r.txpower.write(|w| w.txpower().variant(tx_power));
}
/// Waits until the radio state matches the given `state`
fn wait_for_radio_state(&self, state: RadioState) {
while self.state() != state {}
}
/// Get the current radio state
fn state(&self) -> RadioState {
state(T::regs())
}
/// Moves the radio from any state to the DISABLED state
fn disable(&mut self) {
let r = T::regs();
// See figure 110 in nRF52840-PS
loop {
match self.state() {
RadioState::DISABLED => return,
// idle or ramping up
RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => {
r.tasks_disable.write(|w| w.tasks_disable().set_bit());
self.wait_for_radio_state(RadioState::DISABLED);
return;
}
// ramping down
RadioState::RX_DISABLE | RadioState::TX_DISABLE => {
self.wait_for_radio_state(RadioState::DISABLED);
return;
}
// cancel ongoing transfer or ongoing CCA
RadioState::RX => {
r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit());
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
self.wait_for_radio_state(RadioState::RX_IDLE);
}
RadioState::TX => {
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
self.wait_for_radio_state(RadioState::TX_IDLE);
}
}
}
}
fn set_buffer(&mut self, buffer: &[u8]) {
let r = T::regs();
r.packetptr.write(|w| unsafe { w.bits(buffer.as_ptr() as u32) });
}
/// Moves the radio to the RXIDLE state
fn receive_prepare(&mut self) {
// clear related events
T::regs().events_ccabusy.reset();
T::regs().events_phyend.reset();
// NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE
let disable = match self.state() {
RadioState::DISABLED => false,
RadioState::RX_IDLE => self.needs_enable,
_ => true,
};
if disable {
self.disable();
}
self.needs_enable = false;
}
/// Prepare radio for receiving a packet
fn receive_start(&mut self, packet: &mut Packet) {
// NOTE we do NOT check the address of `packet` because the mutable reference ensures it's
// allocated in RAM
let r = T::regs();
self.receive_prepare();
// Configure shortcuts
//
// The radio goes through following states when receiving a 802.15.4 packet
//
// enable RX → ramp up RX → RX idle → Receive → end (PHYEND)
r.shorts.write(|w| w.rxready_start().enabled());
// set up RX buffer
self.set_buffer(packet.buffer.as_mut());
// start transfer
dma_start_fence();
match self.state() {
// Re-start receiver
RadioState::RX_IDLE => r.tasks_start.write(|w| w.tasks_start().set_bit()),
// Enable receiver
_ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()),
}
}
/// Cancel receiving packet
fn receive_cancel() {
let r = T::regs();
r.shorts.reset();
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
loop {
match state(r) {
RadioState::DISABLED | RadioState::RX_IDLE => break,
_ => (),
}
}
// DMA transfer may have been in progress so synchronize with its memory operations
dma_end_fence();
}
/// Receives one radio packet and copies its contents into the given `packet` buffer
///
/// This methods returns the `Ok` variant if the CRC included the packet was successfully
/// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet`
/// will be updated with the received packet's data
pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> {
let s = T::state();
let r = T::regs();
// Start the read
self.receive_start(packet);
let dropper = OnDrop::new(|| Self::receive_cancel());
self.clear_all_interrupts();
// wait until we have received something
core::future::poll_fn(|cx| {
s.event_waker.register(cx.waker());
if r.events_phyend.read().events_phyend().bit_is_set() {
r.events_phyend.reset();
trace!("RX done poll");
return Poll::Ready(());
} else {
r.intenset.write(|w| w.phyend().set());
};
Poll::Pending
})
.await;
dma_end_fence();
dropper.defuse();
let crc = r.rxcrc.read().rxcrc().bits() as u16;
if r.crcstatus.read().crcstatus().bit_is_set() {
Ok(())
} else {
Err(Error::CrcFailed(crc))
}
}
/// Tries to send the given `packet`
///
/// This method performs Clear Channel Assessment (CCA) first and sends the `packet` only if the
/// channel is observed to be *clear* (no transmission is currently ongoing), otherwise no
/// packet is transmitted and the `Err` variant is returned
///
/// NOTE this method will *not* modify the `packet` argument. The mutable reference is used to
/// ensure the `packet` buffer is allocated in RAM, which is required by the RADIO peripheral
// NOTE we do NOT check the address of `packet` because the mutable reference ensures it's
// allocated in RAM
pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> {
let s = T::state();
let r = T::regs();
// enable radio to perform cca
self.receive_prepare();
/// transmit result
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TransmitResult {
/// Success
Success,
/// Clear channel assessment reported channel in use
ChannelInUse,
}
// Configure shortcuts
//
// The radio goes through following states when sending a 802.15.4 packet
//
// enable RX → ramp up RX → clear channel assessment (CCA) → CCA result
// CCA idle → enable TX → start TX → TX → end (PHYEND) → disabled
//
// CCA might end up in the event CCABUSY in which there will be no transmission
r.shorts.write(|w| {
w.rxready_ccastart()
.enabled()
.ccaidle_txen()
.enabled()
.txready_start()
.enabled()
.ccabusy_disable()
.enabled()
.phyend_disable()
.enabled()
});
// Set transmission buffer
self.set_buffer(packet.buffer.as_mut());
// the DMA transfer will start at some point after the following write operation so
// we place the compiler fence here
dma_start_fence();
// start CCA. In case the channel is clear, the data at packetptr will be sent automatically
match self.state() {
// Re-start receiver
RadioState::RX_IDLE => r.tasks_ccastart.write(|w| w.tasks_ccastart().set_bit()),
// Enable receiver
_ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()),
}
self.clear_all_interrupts();
let result = core::future::poll_fn(|cx| {
s.event_waker.register(cx.waker());
if r.events_phyend.read().events_phyend().bit_is_set() {
r.events_phyend.reset();
r.events_ccabusy.reset();
trace!("TX done poll");
return Poll::Ready(TransmitResult::Success);
} else if r.events_ccabusy.read().events_ccabusy().bit_is_set() {
r.events_ccabusy.reset();
trace!("TX no CCA");
return Poll::Ready(TransmitResult::ChannelInUse);
}
r.intenset.write(|w| w.phyend().set().ccabusy().set());
Poll::Pending
})
.await;
match result {
TransmitResult::Success => Ok(()),
TransmitResult::ChannelInUse => Err(Error::ChannelInUse),
}
}
}
/// An IEEE 802.15.4 packet
///
/// This `Packet` is a PHY layer packet. It's made up of the physical header (PHR) and the PSDU
/// (PHY service data unit). The PSDU of this `Packet` will always include the MAC level CRC, AKA
/// the FCS (Frame Control Sequence) -- the CRC is fully computed in hardware and automatically
/// appended on transmission and verified on reception.
///
/// The API lets users modify the usable part (not the CRC) of the PSDU via the `deref` and
/// `copy_from_slice` methods. These methods will automatically update the PHR.
///
/// See figure 119 in the Product Specification of the nRF52840 for more details
pub struct Packet {
buffer: [u8; Self::SIZE],
}
// See figure 124 in nRF52840-PS
impl Packet {
// for indexing purposes
const PHY_HDR: usize = 0;
const DATA: core::ops::RangeFrom<usize> = 1..;
/// Maximum amount of usable payload (CRC excluded) a single packet can contain, in bytes
pub const CAPACITY: u8 = 125;
const CRC: u8 = 2; // size of the CRC, which is *never* copied to / from RAM
const MAX_PSDU_LEN: u8 = Self::CAPACITY + Self::CRC;
const SIZE: usize = 1 /* PHR */ + Self::MAX_PSDU_LEN as usize;
/// Returns an empty packet (length = 0)
pub fn new() -> Self {
let mut packet = Self {
buffer: [0; Self::SIZE],
};
packet.set_len(0);
packet
}
/// Fills the packet payload with given `src` data
///
/// # Panics
///
/// This function panics if `src` is larger than `Self::CAPACITY`
pub fn copy_from_slice(&mut self, src: &[u8]) {
assert!(src.len() <= Self::CAPACITY as usize);
let len = src.len() as u8;
self.buffer[Self::DATA][..len as usize].copy_from_slice(&src[..len.into()]);
self.set_len(len);
}
/// Returns the size of this packet's payload
pub fn len(&self) -> u8 {
self.buffer[Self::PHY_HDR] - Self::CRC
}
/// Changes the size of the packet's payload
///
/// # Panics
///
/// This function panics if `len` is larger than `Self::CAPACITY`
pub fn set_len(&mut self, len: u8) {
assert!(len <= Self::CAPACITY);
self.buffer[Self::PHY_HDR] = len + Self::CRC;
}
/// Returns the LQI (Link Quality Indicator) of the received packet
///
/// Note that the LQI is stored in the `Packet`'s internal buffer by the hardware so the value
/// returned by this method is only valid after a `Radio.recv` operation. Operations that
/// modify the `Packet`, like `copy_from_slice` or `set_len`+`deref_mut`, will overwrite the
/// stored LQI value.
///
/// Also note that the hardware will *not* compute a LQI for packets smaller than 3 bytes so
/// this method will return an invalid value for those packets.
pub fn lqi(&self) -> u8 {
self.buffer[1 /* PHY_HDR */ + self.len() as usize /* data */]
}
}
impl core::ops::Deref for Packet {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.buffer[Self::DATA][..self.len() as usize]
}
}
impl core::ops::DerefMut for Packet {
fn deref_mut(&mut self) -> &mut [u8] {
let len = self.len();
&mut self.buffer[Self::DATA][..len as usize]
}
}
/// NOTE must be followed by a volatile write operation
fn dma_start_fence() {
compiler_fence(Ordering::Release);
}
/// NOTE must be preceded by a volatile read operation
fn dma_end_fence() {
compiler_fence(Ordering::Acquire);
}

View file

@ -1,110 +0,0 @@
//! Integrated 2.4 GHz Radio
//!
//! The 2.4 GHz radio transceiver is compatible with multiple radio standards
//! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy.
#![macro_use]
/// Bluetooth Low Energy Radio driver.
pub mod ble;
#[cfg(any(
feature = "nrf52811",
feature = "nrf52820",
feature = "nrf52833",
feature = "nrf52840",
feature = "_nrf5340-net"
))]
/// IEEE 802.15.4
pub mod ieee802154;
use core::marker::PhantomData;
use pac::radio::state::STATE_A as RadioState;
pub use pac::radio::txpower::TXPOWER_A as TxPower;
use crate::{interrupt, pac, Peripheral};
/// RADIO error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Buffer was too long.
BufferTooLong,
/// Buffer was too short.
BufferTooShort,
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
BufferNotInRAM,
/// Clear channel assessment reported channel in use
ChannelInUse,
/// CRC check failed
CrcFailed(u16),
}
/// Interrupt handler
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
// clear all interrupts
r.intenclr.write(|w| w.bits(0xffff_ffff));
s.event_waker.wake();
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
pub struct State {
/// end packet transmission or reception
pub event_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
event_waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::radio::RegisterBlock;
fn state() -> &'static State;
}
}
macro_rules! impl_radio {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::radio::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::radio::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::radio::sealed::State {
static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new();
&STATE
}
}
impl crate::radio::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
/// Radio peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
/// Get the state of the radio
pub(crate) fn state(radio: &pac::radio::RegisterBlock) -> RadioState {
match radio.state.read().state().variant() {
Some(state) => state,
None => unreachable!(),
}
}

View file

@ -5,10 +5,12 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
@ -20,6 +22,7 @@ pub struct InterruptHandler<T: Instance> {
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let s = T::state();
let r = T::regs();
// Clear the event.
@ -27,25 +30,46 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
// Mutate the slice within a critical section,
// so that the future isn't dropped in between us loading the pointer and actually dereferencing it.
critical_section::with(|cs| {
let mut state = T::state().borrow_mut(cs);
let (ptr, end) = critical_section::with(|_| {
let ptr = s.ptr.load(Ordering::Relaxed);
// We need to make sure we haven't already filled the whole slice,
// in case the interrupt fired again before the executor got back to the future.
if !state.ptr.is_null() && state.ptr != state.end {
let end = s.end.load(Ordering::Relaxed);
if !ptr.is_null() && ptr != end {
// If the future was dropped, the pointer would have been set to null,
// so we're still good to mutate the slice.
// The safety contract of `Rng::new` means that the future can't have been dropped
// without calling its destructor.
unsafe {
*state.ptr = r.value.read().value().bits();
state.ptr = state.ptr.add(1);
}
if state.ptr == state.end {
state.waker.wake();
*ptr = r.value.read().value().bits();
}
}
(ptr, end)
});
if ptr.is_null() || ptr == end {
// If the future was dropped, there's nothing to do.
// If `ptr == end`, we were called by mistake, so return.
return;
}
let new_ptr = unsafe { ptr.add(1) };
match s
.ptr
.compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => {
let end = s.end.load(Ordering::Relaxed);
// It doesn't matter if `end` was changed under our feet, because then this will just be false.
if new_ptr == end {
s.waker.wake();
}
}
Err(_) => {
// If the future was dropped or finished, there's no point trying to wake it.
// It will have already stopped the RNG, so there's no need to do that either.
}
}
}
}
@ -108,18 +132,17 @@ impl<'d, T: Instance> Rng<'d, T> {
/// Fill the buffer with random bytes.
pub async fn fill_bytes(&mut self, dest: &mut [u8]) {
if dest.is_empty() {
if dest.len() == 0 {
return; // Nothing to fill
}
let s = T::state();
let range = dest.as_mut_ptr_range();
// Even if we've preempted the interrupt, it can't preempt us again,
// so we don't need to worry about the order we write these in.
critical_section::with(|cs| {
let mut state = T::state().borrow_mut(cs);
state.ptr = range.start;
state.end = range.end;
});
s.ptr.store(range.start, Ordering::Relaxed);
s.end.store(range.end, Ordering::Relaxed);
self.enable_irq();
self.start();
@ -128,24 +151,24 @@ impl<'d, T: Instance> Rng<'d, T> {
self.stop();
self.disable_irq();
critical_section::with(|cs| {
let mut state = T::state().borrow_mut(cs);
state.ptr = ptr::null_mut();
state.end = ptr::null_mut();
});
// The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here.
s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
s.end.store(ptr::null_mut(), Ordering::Relaxed);
});
poll_fn(|cx| {
critical_section::with(|cs| {
let mut s = T::state().borrow_mut(cs);
s.waker.register(cx.waker());
if s.ptr == s.end {
// We're done.
Poll::Ready(())
} else {
Poll::Pending
}
})
s.waker.register(cx.waker());
// The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`.
let end = s.end.load(Ordering::Relaxed);
let ptr = s.ptr.load(Ordering::Relaxed);
if ptr == end {
// We're done.
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
@ -171,11 +194,9 @@ impl<'d, T: Instance> Rng<'d, T> {
impl<'d, T: Instance> Drop for Rng<'d, T> {
fn drop(&mut self) {
self.stop();
critical_section::with(|cs| {
let mut state = T::state().borrow_mut(cs);
state.ptr = ptr::null_mut();
state.end = ptr::null_mut();
});
let s = T::state();
s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
s.end.store(ptr::null_mut(), Ordering::Relaxed);
}
}
@ -206,48 +227,21 @@ impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> {
impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {}
pub(crate) mod sealed {
use core::cell::{Ref, RefCell, RefMut};
use critical_section::{CriticalSection, Mutex};
use embassy_sync::waitqueue::WakerRegistration;
use super::*;
/// Peripheral static state
pub struct State {
inner: Mutex<RefCell<InnerState>>,
pub ptr: AtomicPtr<u8>,
pub end: AtomicPtr<u8>,
pub waker: AtomicWaker,
}
pub struct InnerState {
pub ptr: *mut u8,
pub end: *mut u8,
pub waker: WakerRegistration,
}
unsafe impl Send for InnerState {}
impl State {
pub const fn new() -> Self {
Self {
inner: Mutex::new(RefCell::new(InnerState::new())),
}
}
pub fn borrow<'cs>(&'cs self, cs: CriticalSection<'cs>) -> Ref<'cs, InnerState> {
self.inner.borrow(cs).borrow()
}
pub fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> {
self.inner.borrow(cs).borrow_mut()
}
}
impl InnerState {
pub const fn new() -> Self {
Self {
ptr: ptr::null_mut(),
end: ptr::null_mut(),
waker: WakerRegistration::new(),
ptr: AtomicPtr::new(ptr::null_mut()),
end: AtomicPtr::new(ptr::null_mut()),
waker: AtomicWaker::new(),
}
}
}

View file

@ -10,14 +10,13 @@ use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
pub use pac::spim0::config::ORDER_A as BitOrder;
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits};
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut};
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{interrupt, pac, Peripheral};
/// SPIM error
@ -25,6 +24,10 @@ use crate::{interrupt, pac, Peripheral};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// TX buffer was too long.
TxBufferTooLong,
/// RX buffer was too long.
RxBufferTooLong,
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
BufferNotInRAM,
}
@ -38,23 +41,11 @@ pub struct Config {
/// SPI mode
pub mode: Mode,
/// Bit order
pub bit_order: BitOrder,
/// Overread character.
///
/// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer,
/// this byte will be transmitted in the MOSI line for the left-over bytes.
pub orc: u8,
/// Drive strength for the SCK line.
pub sck_drive: OutputDrive,
/// Drive strength for the MOSI line.
pub mosi_drive: OutputDrive,
/// Drive strength for the MISO line.
pub miso_drive: OutputDrive,
}
impl Default for Config {
@ -62,11 +53,7 @@ impl Default for Config {
Self {
frequency: Frequency::M1,
mode: MODE_0,
bit_order: BitOrder::MSB_FIRST,
orc: 0x00,
sck_drive: OutputDrive::HighDrive,
mosi_drive: OutputDrive::HighDrive,
miso_drive: OutputDrive::HighDrive,
}
}
}
@ -82,13 +69,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
let s = T::state();
#[cfg(feature = "_nrf52832_anomaly_109")]
{
// Ideally we should call this only during the first chunk transfer,
// but so far calling this every time doesn't seem to be causing any issues.
if r.events_started.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.started().clear());
}
if r.events_started.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.started().clear());
}
if r.events_end.read().bits() != 0 {
@ -171,16 +154,13 @@ impl<'d, T: Instance> Spim<'d, T> {
// Configure pins
if let Some(sck) = &sck {
sck.conf()
.write(|w| w.dir().output().drive().variant(convert_drive(config.sck_drive)));
sck.conf().write(|w| w.dir().output().drive().h0h1());
}
if let Some(mosi) = &mosi {
mosi.conf()
.write(|w| w.dir().output().drive().variant(convert_drive(config.mosi_drive)));
mosi.conf().write(|w| w.dir().output().drive().h0h1());
}
if let Some(miso) = &miso {
miso.conf()
.write(|w| w.input().connect().drive().variant(convert_drive(config.miso_drive)));
miso.conf().write(|w| w.input().connect().drive().h0h1());
}
match config.mode.polarity {
@ -224,39 +204,27 @@ impl<'d, T: Instance> Spim<'d, T> {
spim
}
fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
// NOTE: RAM slice check for rx is not necessary, as a mutable
// slice can only be built from data located in RAM.
compiler_fence(Ordering::SeqCst);
let r = T::regs();
fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) {
if total > offset {
(ptr.wrapping_add(offset as _), core::cmp::min(total - offset, length))
} else {
(ptr, 0)
}
}
// Set up the DMA read.
let (ptr, len) = slice_ptr_parts_mut(rx);
let (rx_ptr, rx_len) = xfer_params(ptr as _, len as _, offset, length);
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(rx_ptr) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
// Set up the DMA write.
let (ptr, len) = slice_ptr_parts(tx);
let (tx_ptr, tx_len) = xfer_params(ptr as _, len as _, offset, length);
r.txd.ptr.write(|w| unsafe { w.ptr().bits(tx_ptr) });
let (ptr, tx_len) = slice_ptr_parts(tx);
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
/*
trace!("XFER: offset: {}, length: {}", offset, length);
trace!("RX(len: {}, ptr: {=u32:02x})", rx_len, rx_ptr as u32);
trace!("TX(len: {}, ptr: {=u32:02x})", tx_len, tx_ptr as u32);
*/
// Set up the DMA read.
let (ptr, rx_len) = slice_ptr_parts_mut(rx);
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
#[cfg(feature = "_nrf52832_anomaly_109")]
if offset == 0 {
{
let s = T::state();
r.events_started.reset();
@ -279,32 +247,21 @@ impl<'d, T: Instance> Spim<'d, T> {
// Start SPI transaction.
r.tasks_start.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn blocking_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
self.prepare_dma_transfer(rx, tx, offset, length);
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
self.prepare(rx, tx)?;
#[cfg(feature = "_nrf52832_anomaly_109")]
if offset == 0 {
while self.nrf52832_dma_workaround_status().is_pending() {}
}
while let Poll::Pending = self.nrf52832_dma_workaround_status() {}
// Wait for 'end' event.
while T::regs().events_end.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
}
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
// NOTE: RAM slice check for rx is not necessary, as a mutable
// slice can only be built from data located in RAM.
let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
self.blocking_inner_from_ram_chunk(rx, tx, offset, length);
}
Ok(())
}
@ -312,28 +269,27 @@ impl<'d, T: Instance> Spim<'d, T> {
match self.blocking_inner_from_ram(rx, tx) {
Ok(_) => Ok(()),
Err(Error::BufferNotInRAM) => {
// trace!("Copying SPIM tx buffer into RAM for DMA");
trace!("Copying SPIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.blocking_inner_from_ram(rx, tx_ram_buf)
}
Err(error) => Err(error),
}
}
async fn async_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
self.prepare_dma_transfer(rx, tx, offset, length);
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
self.prepare(rx, tx)?;
#[cfg(feature = "_nrf52832_anomaly_109")]
if offset == 0 {
poll_fn(|cx| {
let s = T::state();
poll_fn(|cx| {
let s = T::state();
s.waker.register(cx.waker());
s.waker.register(cx.waker());
self.nrf52832_dma_workaround_status()
})
.await;
}
self.nrf52832_dma_workaround_status()
})
.await;
// Wait for 'end' event.
poll_fn(|cx| {
@ -347,18 +303,7 @@ impl<'d, T: Instance> Spim<'d, T> {
.await;
compiler_fence(Ordering::SeqCst);
}
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
// NOTE: RAM slice check for rx is not necessary, as a mutable
// slice can only be built from data located in RAM.
let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
self.async_inner_from_ram_chunk(rx, tx, offset, length).await;
}
Ok(())
}
@ -366,11 +311,12 @@ impl<'d, T: Instance> Spim<'d, T> {
match self.async_inner_from_ram(rx, tx).await {
Ok(_) => Ok(()),
Err(Error::BufferNotInRAM) => {
// trace!("Copying SPIM tx buffer into RAM for DMA");
trace!("Copying SPIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.async_inner_from_ram(rx, tx_ram_buf).await
}
Err(error) => Err(error),
}
}
@ -569,6 +515,8 @@ mod eh02 {
impl embedded_hal_1::spi::Error for Error {
fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
match *self {
Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,
}
}
@ -632,22 +580,22 @@ impl<'d, T: Instance> SetConfig for Spim<'d, T> {
r.config.write(|w| {
match mode {
MODE_0 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_high();
w.cpha().leading();
}
MODE_1 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_high();
w.cpha().trailing();
}
MODE_2 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_low();
w.cpha().leading();
}
MODE_3 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_low();
w.cpha().trailing();
}

View file

@ -9,9 +9,8 @@ use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
pub use pac::spis0::config::ORDER_A as BitOrder;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
@ -37,9 +36,6 @@ pub struct Config {
/// SPI mode
pub mode: Mode,
/// Bit order
pub bit_order: BitOrder,
/// Overread character.
///
/// If the master keeps clocking the bus after all the bytes in the TX buffer have
@ -60,7 +56,6 @@ impl Default for Config {
fn default() -> Self {
Self {
mode: MODE_0,
bit_order: BitOrder::MSB_FIRST,
orc: 0x00,
def: 0x00,
auto_acquire: true,
@ -227,17 +222,11 @@ impl<'d, T: Instance> Spis<'d, T> {
// Set up the DMA write.
let (ptr, len) = slice_ptr_parts(tx);
if len > EASY_DMA_SIZE {
return Err(Error::TxBufferTooLong);
}
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
// Set up the DMA read.
let (ptr, len) = slice_ptr_parts_mut(rx);
if len > EASY_DMA_SIZE {
return Err(Error::RxBufferTooLong);
}
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
@ -514,22 +503,22 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> {
r.config.write(|w| {
match mode {
MODE_0 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_high();
w.cpha().leading();
}
MODE_1 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_high();
w.cpha().trailing();
}
MODE_2 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_low();
w.cpha().leading();
}
MODE_3 => {
w.order().variant(config.bit_order);
w.order().msb_first();
w.cpol().active_low();
w.cpha().trailing();
}

View file

@ -83,7 +83,7 @@ impl<'d> Temp<'d> {
let value = poll_fn(|cx| {
WAKER.register(cx.waker());
if t.events_datardy.read().bits() == 0 {
Poll::Pending
return Poll::Pending;
} else {
t.events_datardy.reset();
let raw = t.temp.read().bits();

View file

@ -171,8 +171,7 @@ impl RtcDriver {
fn next_period(&self) {
critical_section::with(|cs| {
let r = rtc();
let period = self.period.load(Ordering::Relaxed) + 1;
self.period.store(period, Ordering::Relaxed);
let period = self.period.fetch_add(1, Ordering::Relaxed) + 1;
let t = (period as u64) << 23;
for n in 0..ALARM_COUNT {
@ -220,15 +219,18 @@ impl Driver for RtcDriver {
}
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
critical_section::with(|_| {
let id = self.alarm_count.load(Ordering::Relaxed);
if id < ALARM_COUNT as u8 {
self.alarm_count.store(id + 1, Ordering::Relaxed);
Some(AlarmHandle::new(id))
let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
if x < ALARM_COUNT as u8 {
Some(x + 1)
} else {
None
}
})
});
match id {
Ok(id) => Some(AlarmHandle::new(id)),
Err(_) => None,
}
}
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {

View file

@ -111,7 +111,7 @@ impl<'d, T: Instance> Timer<'d, T> {
Self::new_inner(timer, true)
}
fn new_inner(timer: impl Peripheral<P = T> + 'd, _is_counter: bool) -> Self {
fn new_inner(timer: impl Peripheral<P = T> + 'd, is_counter: bool) -> Self {
into_ref!(timer);
let regs = T::regs();
@ -122,16 +122,12 @@ impl<'d, T: Instance> Timer<'d, T> {
// since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
this.stop();
#[cfg(not(feature = "nrf51"))]
if _is_counter {
if is_counter {
regs.mode.write(|w| w.mode().low_power_counter());
} else {
regs.mode.write(|w| w.mode().timer());
}
#[cfg(feature = "nrf51")]
regs.mode.write(|w| w.mode().timer());
// Make the counter's max value as high as possible.
// TODO: is there a reason someone would want to set this lower?
regs.bitmode.write(|w| w.bitmode()._32bit());
@ -242,11 +238,7 @@ pub struct Cc<'d, T: Instance> {
impl<'d, T: Instance> Cc<'d, T> {
/// Get the current value stored in the register.
pub fn read(&self) -> u32 {
#[cfg(not(feature = "nrf51"))]
return T::regs().cc[self.n].read().cc().bits();
#[cfg(feature = "nrf51")]
return T::regs().cc[self.n].read().bits();
T::regs().cc[self.n].read().cc().bits()
}
/// Set the value stored in the register.
@ -254,11 +246,7 @@ impl<'d, T: Instance> Cc<'d, T> {
/// `event_compare` will fire when the timer's counter reaches this value.
pub fn write(&self, value: u32) {
// SAFETY: there are no invalid values for the CC register.
#[cfg(not(feature = "nrf51"))]
T::regs().cc[self.n].write(|w| unsafe { w.cc().bits(value) });
#[cfg(feature = "nrf51")]
T::regs().cc[self.n].write(|w| unsafe { w.bits(value) });
T::regs().cc[self.n].write(|w| unsafe { w.cc().bits(value) })
}
/// Capture the current value of the timer's counter in this register, and return it.

View file

@ -372,7 +372,7 @@ impl<'d, T: Instance> Twim<'d, T> {
// Start write operation.
r.shorts.write(|w| w.lasttx_stop().enabled());
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
if buffer.is_empty() {
if buffer.len() == 0 {
// With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP ourselves.
r.tasks_stop.write(|w| unsafe { w.bits(1) });
}
@ -403,7 +403,7 @@ impl<'d, T: Instance> Twim<'d, T> {
// Start read operation.
r.shorts.write(|w| w.lastrx_stop().enabled());
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
if buffer.is_empty() {
if buffer.len() == 0 {
// With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves.
r.tasks_stop.write(|w| unsafe { w.bits(1) });
}
@ -447,7 +447,7 @@ impl<'d, T: Instance> Twim<'d, T> {
w
});
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
if wr_buffer.is_empty() && rd_buffer.is_empty() {
if wr_buffer.len() == 0 && rd_buffer.len() == 0 {
// With a zero-length buffer, LASTRX/LASTTX doesn't fire (because there's no last byte!), so do the STOP ourselves.
// TODO handle when only one of the buffers is zero length
r.tasks_stop.write(|w| unsafe { w.bits(1) });
@ -469,7 +469,7 @@ impl<'d, T: Instance> Twim<'d, T> {
trace!("Copying TWIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_write_read_from_ram(address, tx_ram_buf, rd_buffer, inten)
self.setup_write_read_from_ram(address, &tx_ram_buf, rd_buffer, inten)
}
Err(error) => Err(error),
}
@ -482,7 +482,7 @@ impl<'d, T: Instance> Twim<'d, T> {
trace!("Copying TWIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_write_from_ram(address, tx_ram_buf, inten)
self.setup_write_from_ram(address, &tx_ram_buf, inten)
}
Err(error) => Err(error),
}
@ -779,7 +779,7 @@ mod eh02 {
impl<'a, T: Instance> embedded_hal_02::blocking::i2c::Write for Twim<'a, T> {
type Error = Error;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
fn write<'w>(&mut self, addr: u8, bytes: &'w [u8]) -> Result<(), Error> {
if slice_in_ram(bytes) {
self.blocking_write(addr, bytes)
} else {
@ -796,7 +796,7 @@ mod eh02 {
impl<'a, T: Instance> embedded_hal_02::blocking::i2c::Read for Twim<'a, T> {
type Error = Error;
fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> {
fn read<'w>(&mut self, addr: u8, bytes: &'w mut [u8]) -> Result<(), Error> {
self.blocking_read(addr, bytes)
}
}
@ -847,10 +847,10 @@ impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> {
self.blocking_write_read(address, wr_buffer, rd_buffer)
}
fn transaction(
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::Operation<'_>],
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}

View file

@ -577,7 +577,7 @@ impl<'d, T: Instance> Twis<'d, T> {
trace!("Copying TWIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_respond_from_ram(tx_ram_buf, inten)
self.setup_respond_from_ram(&tx_ram_buf, inten)
}
Err(error) => Err(error),
}

View file

@ -52,37 +52,6 @@ impl Default for Config {
}
}
bitflags::bitflags! {
/// Error source flags
pub struct ErrorSource: u32 {
/// Buffer overrun
const OVERRUN = 0x01;
/// Parity error
const PARITY = 0x02;
/// Framing error
const FRAMING = 0x04;
/// Break condition
const BREAK = 0x08;
}
}
impl ErrorSource {
#[inline]
fn check(self) -> Result<(), Error> {
if self.contains(ErrorSource::OVERRUN) {
Err(Error::Overrun)
} else if self.contains(ErrorSource::PARITY) {
Err(Error::Parity)
} else if self.contains(ErrorSource::FRAMING) {
Err(Error::Framing)
} else if self.contains(ErrorSource::BREAK) {
Err(Error::Break)
} else {
Ok(())
}
}
}
/// UART error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -92,14 +61,6 @@ pub enum Error {
BufferTooLong,
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
BufferNotInRAM,
/// Framing Error
Framing,
/// Parity Error
Parity,
/// Buffer Overrun
Overrun,
/// Break condition
Break,
}
/// Interrupt handler.
@ -112,19 +73,12 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
let r = T::regs();
let s = T::state();
let endrx = r.events_endrx.read().bits();
let error = r.events_error.read().bits();
if endrx != 0 || error != 0 {
s.rx_waker.wake();
if endrx != 0 {
r.intenclr.write(|w| w.endrx().clear());
}
if error != 0 {
r.intenclr.write(|w| w.error().clear());
}
if r.events_endrx.read().bits() != 0 {
s.endrx_waker.wake();
r.intenclr.write(|w| w.endrx().clear());
}
if r.events_endtx.read().bits() != 0 {
s.tx_waker.wake();
s.endtx_waker.wake();
r.intenclr.write(|w| w.endtx().clear());
}
}
@ -159,7 +113,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(uarte, rxd, txd);
into_ref!(rxd, txd);
Self::new_inner(uarte, rxd.map_into(), txd.map_into(), None, None, config)
}
@ -173,7 +127,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
rts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(uarte, rxd, txd, cts, rts);
into_ref!(rxd, txd, cts, rts);
Self::new_inner(
uarte,
rxd.map_into(),
@ -185,21 +139,16 @@ impl<'d, T: Instance> Uarte<'d, T> {
}
fn new_inner(
uarte: PeripheralRef<'d, T>,
uarte: impl Peripheral<P = T> + 'd,
rxd: PeripheralRef<'d, AnyPin>,
txd: PeripheralRef<'d, AnyPin>,
cts: Option<PeripheralRef<'d, AnyPin>>,
rts: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
let r = T::regs();
into_ref!(uarte);
let hardware_flow_control = match (rts.is_some(), cts.is_some()) {
(false, false) => false,
(true, true) => true,
_ => panic!("RTS and CTS pins must be either both set or none set."),
};
configure(r, config, hardware_flow_control);
let r = T::regs();
rxd.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
@ -222,6 +171,13 @@ impl<'d, T: Instance> Uarte<'d, T> {
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
let hardware_flow_control = match (rts.is_some(), cts.is_some()) {
(false, false) => false,
(true, true) => true,
_ => panic!("RTS and CTS pins must be either both set or none set."),
};
configure(r, config, hardware_flow_control);
let s = T::state();
s.tx_rx_refcount.store(2, Ordering::Relaxed);
@ -240,14 +196,6 @@ impl<'d, T: Instance> Uarte<'d, T> {
(self.tx, self.rx)
}
/// Split the UART in reader and writer parts, by reference.
///
/// The returned halves borrow from `self`, so you can drop them and go back to using
/// the "un-split" `self`. This allows temporarily splitting the UART.
pub fn split_by_ref(&mut self) -> (&mut UarteTx<'d, T>, &mut UarteRx<'d, T>) {
(&mut self.tx, &mut self.rx)
}
/// Split the Uarte into the transmitter and receiver with idle support parts.
///
/// This is useful to concurrently transmit and receive from independent tasks.
@ -297,7 +245,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
}
}
pub(crate) fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) {
fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) {
r.config.write(|w| {
w.hwfc().bit(hardware_flow_control);
w.parity().variant(config.parity);
@ -313,14 +261,8 @@ pub(crate) fn configure(r: &RegisterBlock, config: Config, hardware_flow_control
r.events_rxstarted.reset();
r.events_txstarted.reset();
// reset all pins
r.psel.txd.write(|w| w.connect().disconnected());
r.psel.rxd.write(|w| w.connect().disconnected());
r.psel.cts.write(|w| w.connect().disconnected());
r.psel.rts.write(|w| w.connect().disconnected());
// Enable
apply_workaround_for_enable_anomaly(r);
apply_workaround_for_enable_anomaly(&r);
r.enable.write(|w| w.enable().enabled());
}
@ -332,7 +274,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(uarte, txd);
into_ref!(txd);
Self::new_inner(uarte, txd.map_into(), None, config)
}
@ -344,19 +286,19 @@ impl<'d, T: Instance> UarteTx<'d, T> {
cts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(uarte, txd, cts);
into_ref!(txd, cts);
Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config)
}
fn new_inner(
uarte: PeripheralRef<'d, T>,
uarte: impl Peripheral<P = T> + 'd,
txd: PeripheralRef<'d, AnyPin>,
cts: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
let r = T::regs();
into_ref!(uarte);
configure(r, config, cts.is_some());
let r = T::regs();
txd.set_high();
txd.conf().write(|w| w.dir().output().drive().s0s1());
@ -367,6 +309,12 @@ impl<'d, T: Instance> UarteTx<'d, T> {
}
r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
r.psel.rxd.write(|w| w.connect().disconnected());
r.psel.rts.write(|w| w.connect().disconnected());
let hardware_flow_control = cts.is_some();
configure(r, config, hardware_flow_control);
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
@ -384,7 +332,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
trace!("Copying UARTE tx buffer into RAM for DMA");
let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()];
ram_buf.copy_from_slice(buffer);
self.write_from_ram(ram_buf).await
self.write_from_ram(&ram_buf).await
}
Err(error) => Err(error),
}
@ -392,7 +340,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
/// Same as [`write`](Self::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Ok(());
}
@ -431,7 +379,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
poll_fn(|cx| {
s.tx_waker.register(cx.waker());
s.endtx_waker.register(cx.waker());
if r.events_endtx.read().bits() != 0 {
return Poll::Ready(());
}
@ -454,7 +402,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
trace!("Copying UARTE tx buffer into RAM for DMA");
let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()];
ram_buf.copy_from_slice(buffer);
self.blocking_write_from_ram(ram_buf)
self.blocking_write_from_ram(&ram_buf)
}
Err(error) => Err(error),
}
@ -462,7 +410,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
/// Same as [`write_from_ram`](Self::write_from_ram) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Ok(());
}
@ -510,7 +458,7 @@ impl<'a, T: Instance> Drop for UarteTx<'a, T> {
let s = T::state();
drop_tx_rx(r, s);
drop_tx_rx(&r, &s);
}
}
@ -522,7 +470,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
rxd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(uarte, rxd);
into_ref!(rxd);
Self::new_inner(uarte, rxd.map_into(), None, config)
}
@ -534,27 +482,19 @@ impl<'d, T: Instance> UarteRx<'d, T> {
rts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(uarte, rxd, rts);
into_ref!(rxd, rts);
Self::new_inner(uarte, rxd.map_into(), Some(rts.map_into()), config)
}
/// Check for errors and clear the error register if an error occured.
fn check_and_clear_errors(&mut self) -> Result<(), Error> {
let r = T::regs();
let err_bits = r.errorsrc.read().bits();
r.errorsrc.write(|w| unsafe { w.bits(err_bits) });
ErrorSource::from_bits_truncate(err_bits).check()
}
fn new_inner(
uarte: PeripheralRef<'d, T>,
uarte: impl Peripheral<P = T> + 'd,
rxd: PeripheralRef<'d, AnyPin>,
rts: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
let r = T::regs();
into_ref!(uarte);
configure(r, config, rts.is_some());
let r = T::regs();
rxd.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
@ -565,9 +505,15 @@ impl<'d, T: Instance> UarteRx<'d, T> {
}
r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
r.psel.txd.write(|w| w.connect().disconnected());
r.psel.cts.write(|w| w.connect().disconnected());
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
let hardware_flow_control = rts.is_some();
configure(r, config, hardware_flow_control);
let s = T::state();
s.tx_rx_refcount.store(1, Ordering::Relaxed);
@ -619,14 +565,14 @@ impl<'d, T: Instance> UarteRx<'d, T> {
UarteRxWithIdle {
rx: self,
timer,
ppi_ch1,
ppi_ch1: ppi_ch1,
_ppi_ch2: ppi_ch2,
}
}
/// Read bytes until the buffer is filled.
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Ok(());
}
if buffer.len() > EASY_DMA_SIZE {
@ -642,13 +588,8 @@ impl<'d, T: Instance> UarteRx<'d, T> {
let drop = OnDrop::new(move || {
trace!("read drop: stopping");
r.intenclr.write(|w| {
w.endrx().clear();
w.error().clear()
});
r.intenclr.write(|w| w.endrx().clear());
r.events_rxto.reset();
r.events_error.reset();
r.errorsrc.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
@ -660,26 +601,17 @@ impl<'d, T: Instance> UarteRx<'d, T> {
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.events_error.reset();
r.intenset.write(|w| {
w.endrx().set();
w.error().set()
});
r.intenset.write(|w| w.endrx().set());
compiler_fence(Ordering::SeqCst);
trace!("startrx");
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
let result = poll_fn(|cx| {
s.rx_waker.register(cx.waker());
if let Err(e) = self.check_and_clear_errors() {
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
return Poll::Ready(Err(e));
}
poll_fn(|cx| {
s.endrx_waker.register(cx.waker());
if r.events_endrx.read().bits() != 0 {
return Poll::Ready(Ok(()));
return Poll::Ready(());
}
Poll::Pending
})
@ -689,12 +621,12 @@ impl<'d, T: Instance> UarteRx<'d, T> {
r.events_rxstarted.reset();
drop.defuse();
result
Ok(())
}
/// Read bytes until the buffer is filled.
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Ok(());
}
if buffer.len() > EASY_DMA_SIZE {
@ -710,23 +642,19 @@ impl<'d, T: Instance> UarteRx<'d, T> {
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.events_error.reset();
r.intenclr.write(|w| {
w.endrx().clear();
w.error().clear()
});
r.intenclr.write(|w| w.endrx().clear());
compiler_fence(Ordering::SeqCst);
trace!("startrx");
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 && r.events_error.read().bits() == 0 {}
while r.events_endrx.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
r.events_rxstarted.reset();
self.check_and_clear_errors()
Ok(())
}
}
@ -744,7 +672,7 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
let s = T::state();
drop_tx_rx(r, s);
drop_tx_rx(&r, &s);
}
}
@ -775,7 +703,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
///
/// Returns the amount of bytes read.
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Ok(0);
}
if buffer.len() > EASY_DMA_SIZE {
@ -793,12 +721,8 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
let drop = OnDrop::new(|| {
self.timer.stop();
r.intenclr.write(|w| {
w.endrx().clear();
w.error().clear()
});
r.intenclr.write(|w| w.endrx().clear());
r.events_rxto.reset();
r.events_error.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
@ -808,27 +732,17 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.events_error.reset();
r.intenset.write(|w| {
w.endrx().set();
w.error().set()
});
r.intenset.write(|w| w.endrx().set());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
let result = poll_fn(|cx| {
s.rx_waker.register(cx.waker());
if let Err(e) = self.rx.check_and_clear_errors() {
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
return Poll::Ready(Err(e));
}
poll_fn(|cx| {
s.endrx_waker.register(cx.waker());
if r.events_endrx.read().bits() != 0 {
return Poll::Ready(Ok(()));
return Poll::Ready(());
}
Poll::Pending
})
.await;
@ -841,14 +755,14 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
drop.defuse();
result.map(|_| n)
Ok(n)
}
/// Read bytes until the buffer is filled, or the line becomes idle.
///
/// Returns the amount of bytes read.
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.is_empty() {
if buffer.len() == 0 {
return Ok(0);
}
if buffer.len() > EASY_DMA_SIZE {
@ -866,17 +780,13 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.events_error.reset();
r.intenclr.write(|w| {
w.endrx().clear();
w.error().clear()
});
r.intenclr.write(|w| w.endrx().clear());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 && r.events_error.read().bits() == 0 {}
while r.events_endrx.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
@ -884,7 +794,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
self.timer.stop();
r.events_rxstarted.reset();
self.rx.check_and_clear_errors().map(|_| n)
Ok(n)
}
}
@ -962,15 +872,15 @@ pub(crate) mod sealed {
use super::*;
pub struct State {
pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker,
pub endrx_waker: AtomicWaker,
pub endtx_waker: AtomicWaker,
pub tx_rx_refcount: AtomicU8,
}
impl State {
pub const fn new() -> Self {
Self {
rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(),
endrx_waker: AtomicWaker::new(),
endtx_waker: AtomicWaker::new(),
tx_rx_refcount: AtomicU8::new(0),
}
}

View file

@ -1,22 +1,8 @@
#![allow(dead_code)]
use core::mem;
const SRAM_LOWER: usize = 0x2000_0000;
const SRAM_UPPER: usize = 0x3000_0000;
// #![feature(const_slice_ptr_len)]
// https://github.com/rust-lang/rust/issues/71146
pub(crate) fn slice_ptr_len<T>(ptr: *const [T]) -> usize {
use core::ptr::NonNull;
let ptr = ptr.cast_mut();
if let Some(ptr) = NonNull::new(ptr) {
ptr.len()
} else {
// We know ptr is null, so we know ptr.wrapping_byte_add(1) is not null.
NonNull::new(ptr.wrapping_byte_add(1)).unwrap().len()
}
}
// TODO: replace transmutes with core::ptr::metadata once it's stable
pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) {
unsafe { mem::transmute(slice) }
@ -34,6 +20,7 @@ pub(crate) fn slice_in_ram<T>(slice: *const [T]) -> bool {
}
/// Return an error if slice is not in RAM. Skips check if slice is zero-length.
#[cfg(not(feature = "nrf51"))]
pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> {
let (_, len) = slice_ptr_parts(slice);
if len == 0 || slice_in_ram(slice) {

View file

@ -22,36 +22,33 @@ features = ["defmt", "unstable-pac", "time-driver"]
[features]
default = [ "rt" ]
## Enable the rt feature of [`rp-pac`](https://docs.rs/crates/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
## Enable the RP runtime.
rt = [ "rp-pac/rt" ]
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
## Enable defmt
defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"]
## Configure the critical section crate to use an implementation that is safe for multicore use on rp2040.
## critical section that is safe for multicore use
critical-section-impl = ["critical-section/restore-state-u8"]
## Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
## This is unstable because semver-minor (non-breaking) releases of `embassy-rp` may major-bump (breaking) the PAC version.
## This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version.
## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.
## There are no plans to make this stable.
unstable-pac = []
## Enable the timer for use with `embassy-time` with a 1MHz tick rate.
## Enable the timer for use with `embassy-time` with a 1MHz tick rate
time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000"]
## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls.
## Enable ROM function cache
rom-func-cache = []
## Enable implementations of some compiler intrinsics using functions in the rp2040 Mask ROM.
## These should be as fast or faster than the implementations in compiler-builtins. They also save code space and reduce memory contention.
## Compiler intrinsics are used automatically, you do not need to change your code to get performance improvements from this feature.
## Enable intrinsics
intrinsics = []
## Enable intrinsics based on extra ROM functions added in the v2 version of the rp2040 Mask ROM.
## This version added a lot more floating point operations - many f64 functions and a few f32 functions were added in ROM v2.
## Enable ROM v2 intrinsics
rom-v2-intrinsics = []
## Allow using QSPI pins as GPIO pins. This is mostly not what you want (because your flash is attached via QSPI pins)
## and adds code and memory overhead when this feature is enabled.
## Allow using QSPI pins as GPIO pins. This is mostly not what you want (because your flash lives there)
## and would add both code and memory overhead when enabled needlessly.
qspi-as-gpio = []
## Indicate code is running from RAM.
@ -60,24 +57,20 @@ qspi-as-gpio = []
run-from-ram = []
#! ### boot2 flash chip support
#! RP2040's internal bootloader is only able to run code from the first 256 bytes of flash.
#! A 2nd stage bootloader (boot2) is required to run larger programs from external flash.
#! Select from existing boot2 implementations via the following features. If none are selected,
#! boot2-w25q080 will be used (w25q080 is the flash chip used on the pico).
#! Each implementation uses flash commands and timings specific to a QSPI flash chip family for better performance.
## Use boot2 with support for Renesas/Dialog AT25SF128a SPI flash.
#! If none of these are enabled, w25q080 is used by default (used on the pico)
## AT25SF128a
boot2-at25sf128a = []
## Use boot2 with support for Gigadevice GD25Q64C SPI flash.
## GD25Q64cs
boot2-gd25q64cs = []
## Use boot2 that only uses generic flash commands - these are supported by all SPI flash, but are slower.
## generic-03h
boot2-generic-03h = []
## Use boot2 with support for ISSI IS25LP080 SPI flash.
## IS25LP080
boot2-is25lp080 = []
## Use boot2 that copies the entire program to RAM before booting. This uses generic flash commands to perform the copy.
## ram-memcpy
boot2-ram-memcpy = []
## Use boot2 with support for Winbond W25Q080 SPI flash.
## W25Q080
boot2-w25q080 = []
## Use boot2 with support for Winbond W25X10CL SPI flash.
## W25X10cl
boot2-w25x10cl = []
[dependencies]

View file

@ -8,7 +8,6 @@ use core::task::{Context, Poll};
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use self::sealed::Pin as _;
use crate::interrupt::InterruptExt;
use crate::pac::common::{Reg, RW};
use crate::pac::SIO;
@ -106,14 +105,14 @@ pub struct DormantWakeConfig {
}
/// GPIO input driver.
pub struct Input<'d> {
pin: Flex<'d>,
pub struct Input<'d, T: Pin> {
pin: Flex<'d, T>,
}
impl<'d> Input<'d> {
impl<'d, T: Pin> Input<'d, T> {
/// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, pull: Pull) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd, pull: Pull) -> Self {
let mut pin = Flex::new(pin);
pin.set_as_input();
pin.set_pull(pull);
@ -176,7 +175,7 @@ impl<'d> Input<'d> {
/// Configure dormant wake.
#[inline]
pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {
pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<T> {
self.pin.dormant_wake(cfg)
}
}
@ -256,12 +255,14 @@ fn IO_IRQ_QSPI() {
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct InputFuture<'d> {
pin: PeripheralRef<'d, AnyPin>,
struct InputFuture<'a, T: Pin> {
pin: PeripheralRef<'a, T>,
}
impl<'d> InputFuture<'d> {
fn new(pin: PeripheralRef<'d, AnyPin>, level: InterruptTrigger) -> Self {
impl<'d, T: Pin> InputFuture<'d, T> {
/// Create a new future wiating for input trigger.
pub fn new(pin: impl Peripheral<P = T> + 'd, level: InterruptTrigger) -> Self {
into_ref!(pin);
let pin_group = (pin.pin() % 8) as usize;
// first, clear the INTR register bits. without this INTR will still
// contain reports of previous edges, causing the IRQ to fire early
@ -304,7 +305,7 @@ impl<'d> InputFuture<'d> {
}
}
impl<'d> Future for InputFuture<'d> {
impl<'d, T: Pin> Future for InputFuture<'d, T> {
type Output = ();
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
@ -343,14 +344,14 @@ impl<'d> Future for InputFuture<'d> {
}
/// GPIO output driver.
pub struct Output<'d> {
pin: Flex<'d>,
pub struct Output<'d, T: Pin> {
pin: Flex<'d, T>,
}
impl<'d> Output<'d> {
impl<'d, T: Pin> Output<'d, T> {
/// Create GPIO output driver for a [Pin] with the provided [Level].
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level) -> Self {
let mut pin = Flex::new(pin);
match initial_output {
Level::High => pin.set_high(),
@ -417,14 +418,14 @@ impl<'d> Output<'d> {
}
/// GPIO output open-drain.
pub struct OutputOpenDrain<'d> {
pin: Flex<'d>,
pub struct OutputOpenDrain<'d, T: Pin> {
pin: Flex<'d, T>,
}
impl<'d> OutputOpenDrain<'d> {
impl<'d, T: Pin> OutputOpenDrain<'d, T> {
/// Create GPIO output driver for a [Pin] in open drain mode with the provided [Level].
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level) -> Self {
let mut pin = Flex::new(pin);
pin.set_low();
match initial_output {
@ -547,17 +548,17 @@ impl<'d> OutputOpenDrain<'d> {
/// This pin can be either an input or output pin. The output level register bit will remain
/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
/// mode.
pub struct Flex<'d> {
pin: PeripheralRef<'d, AnyPin>,
pub struct Flex<'d, T: Pin> {
pin: PeripheralRef<'d, T>,
}
impl<'d> Flex<'d> {
impl<'d, T: Pin> Flex<'d, T> {
/// Wrap the pin in a `Flex`.
///
/// The pin remains disconnected. The initial output level is unspecified, but can be changed
/// before the pin is put into output mode.
#[inline]
pub fn new(pin: impl Peripheral<P = impl Pin> + 'd) -> Self {
pub fn new(pin: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(pin);
pin.pad_ctrl().write(|w| {
@ -568,7 +569,7 @@ impl<'d> Flex<'d> {
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
});
Self { pin: pin.map_into() }
Self { pin }
}
#[inline]
@ -715,36 +716,36 @@ impl<'d> Flex<'d> {
/// Wait until the pin is high. If it is already high, return immediately.
#[inline]
pub async fn wait_for_high(&mut self) {
InputFuture::new(self.pin.reborrow(), InterruptTrigger::LevelHigh).await;
InputFuture::new(&mut self.pin, InterruptTrigger::LevelHigh).await;
}
/// Wait until the pin is low. If it is already low, return immediately.
#[inline]
pub async fn wait_for_low(&mut self) {
InputFuture::new(self.pin.reborrow(), InterruptTrigger::LevelLow).await;
InputFuture::new(&mut self.pin, InterruptTrigger::LevelLow).await;
}
/// Wait for the pin to undergo a transition from low to high.
#[inline]
pub async fn wait_for_rising_edge(&mut self) {
InputFuture::new(self.pin.reborrow(), InterruptTrigger::EdgeHigh).await;
InputFuture::new(&mut self.pin, InterruptTrigger::EdgeHigh).await;
}
/// Wait for the pin to undergo a transition from high to low.
#[inline]
pub async fn wait_for_falling_edge(&mut self) {
InputFuture::new(self.pin.reborrow(), InterruptTrigger::EdgeLow).await;
InputFuture::new(&mut self.pin, InterruptTrigger::EdgeLow).await;
}
/// Wait for the pin to undergo any transition, i.e low to high OR high to low.
#[inline]
pub async fn wait_for_any_edge(&mut self) {
InputFuture::new(self.pin.reborrow(), InterruptTrigger::AnyEdge).await;
InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await;
}
/// Configure dormant wake.
#[inline]
pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {
pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<T> {
let idx = self.pin._pin() as usize;
self.pin.io().intr(idx / 8).write(|w| {
w.set_edge_high(idx % 8, cfg.edge_high);
@ -763,7 +764,7 @@ impl<'d> Flex<'d> {
}
}
impl<'d> Drop for Flex<'d> {
impl<'d, T: Pin> Drop for Flex<'d, T> {
#[inline]
fn drop(&mut self) {
let idx = self.pin._pin() as usize;
@ -781,12 +782,12 @@ impl<'d> Drop for Flex<'d> {
}
/// Dormant wake driver.
pub struct DormantWake<'w> {
pin: PeripheralRef<'w, AnyPin>,
pub struct DormantWake<'w, T: Pin> {
pin: PeripheralRef<'w, T>,
cfg: DormantWakeConfig,
}
impl<'w> Drop for DormantWake<'w> {
impl<'w, T: Pin> Drop for DormantWake<'w, T> {
fn drop(&mut self) {
let idx = self.pin._pin() as usize;
self.pin.io().intr(idx / 8).write(|w| {
@ -815,7 +816,7 @@ pub(crate) mod sealed {
#[inline]
fn _bank(&self) -> Bank {
match self.pin_bank() >> 5 {
match self.pin_bank() & 0x20 {
#[cfg(feature = "qspi-as-gpio")]
1 => Bank::Qspi,
_ => Bank::Bank0,
@ -889,17 +890,6 @@ pub struct AnyPin {
pin_bank: u8,
}
impl AnyPin {
/// Unsafely create a new type-erased pin.
///
/// # Safety
///
/// You must ensure that youre only using one instance of this type at a time.
pub unsafe fn steal(pin_bank: u8) -> Self {
Self { pin_bank }
}
}
impl_peripheral!(AnyPin);
impl Pin for AnyPin {}
@ -980,7 +970,7 @@ mod eh02 {
use super::*;
impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Input<'d, T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
@ -992,7 +982,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::OutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Output<'d, T> {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
@ -1004,7 +994,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -1014,7 +1004,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
@ -1022,7 +1012,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::InputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for OutputOpenDrain<'d, T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
@ -1034,7 +1024,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d, T> {
type Error = Infallible;
#[inline]
@ -1048,7 +1038,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -1058,7 +1048,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for OutputOpenDrain<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
@ -1066,7 +1056,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::InputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Flex<'d, T> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
@ -1078,7 +1068,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::OutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Flex<'d, T> {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
@ -1090,7 +1080,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -1100,7 +1090,7 @@ mod eh02 {
}
}
impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> {
type Error = Infallible;
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
@ -1109,11 +1099,11 @@ mod eh02 {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for Input<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::InputPin for Input<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}
@ -1123,11 +1113,11 @@ impl<'d> embedded_hal_1::digital::InputPin for Input<'d> {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for Output<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Output<'d, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -1137,7 +1127,7 @@ impl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {
}
}
impl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_high())
}
@ -1147,11 +1137,11 @@ impl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -1161,7 +1151,7 @@ impl<'d> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d> {
}
}
impl<'d> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_high())
}
@ -1171,7 +1161,7 @@ impl<'d> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d> {
}
}
impl<'d> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}
@ -1181,11 +1171,11 @@ impl<'d> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d> {
}
}
impl<'d> embedded_hal_1::digital::ErrorType for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> {
type Error = Infallible;
}
impl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_high())
}
@ -1195,7 +1185,7 @@ impl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {
}
}
impl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -1205,7 +1195,7 @@ impl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {
}
}
impl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok((*self).is_set_high())
}
@ -1215,7 +1205,7 @@ impl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> {
}
}
impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
impl<'d, T: Pin> embedded_hal_async::digital::Wait for Flex<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
@ -1242,7 +1232,7 @@ impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
}
}
impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
impl<'d, T: Pin> embedded_hal_async::digital::Wait for Input<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
@ -1269,7 +1259,7 @@ impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
}
}
impl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {
impl<'d, T: Pin> embedded_hal_async::digital::Wait for OutputOpenDrain<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())

View file

@ -43,18 +43,6 @@ pub enum Error {
AddressReserved(u16),
}
/// I2C Config error
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
/// Max i2c speed is 1MHz
FrequencyTooHigh,
/// The sys clock is too slow to support given frequency
ClockTooSlow,
/// The sys clock is too fast to support given frequency
ClockTooFast,
}
/// I2C config.
#[non_exhaustive]
#[derive(Copy, Clone)]
@ -377,32 +365,37 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
) -> Self {
into_ref!(_peri);
assert!(config.frequency <= 1_000_000);
assert!(config.frequency > 0);
let p = T::regs();
let reset = T::reset();
crate::reset::reset(reset);
crate::reset::unreset_wait(reset);
p.ic_enable().write(|w| w.set_enable(false));
// Select controller mode & speed
p.ic_con().modify(|w| {
// Always use "fast" mode (<= 400 kHz, works fine for standard
// mode too)
w.set_speed(i2c::vals::Speed::FAST);
w.set_master_mode(true);
w.set_ic_slave_disable(true);
w.set_ic_restart_en(true);
w.set_tx_empty_ctrl(true);
});
// Set FIFO watermarks to 1 to make things simpler. This is encoded
// by a register value of 0.
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
// Configure SCL & SDA pins
set_up_i2c_pin(&scl);
set_up_i2c_pin(&sda);
let mut me = Self { phantom: PhantomData };
if let Err(e) = me.set_config_inner(&config) {
panic!("Error configuring i2c: {:?}", e);
}
me
}
fn set_config_inner(&mut self, config: &Config) -> Result<(), ConfigError> {
if config.frequency > 1_000_000 {
return Err(ConfigError::FrequencyTooHigh);
}
let p = T::regs();
p.ic_enable().write(|w| w.set_enable(false));
// Configure baudrate
// There are some subtleties to I2C timing which we are completely
@ -415,12 +408,10 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
let hcnt = period - lcnt; // and 2/5 (40%) of the period high
// Check for out-of-range divisors:
if hcnt > 0xffff || lcnt > 0xffff {
return Err(ConfigError::ClockTooFast);
}
if hcnt < 8 || lcnt < 8 {
return Err(ConfigError::ClockTooSlow);
}
assert!(hcnt <= 0xffff);
assert!(lcnt <= 0xffff);
assert!(hcnt >= 8);
assert!(lcnt >= 8);
// Per I2C-bus specification a device in standard or fast mode must
// internally provide a hold time of at least 300ns for the SDA
@ -433,19 +424,14 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
((clk_base * 3) / 10_000_000) + 1
} else {
// fast mode plus requires a clk_base > 32MHz
if clk_base <= 32_000_000 {
return Err(ConfigError::ClockTooSlow);
}
assert!(clk_base >= 32_000_000);
// sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
// 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
// fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 25_000_000) + 1
};
if sda_tx_hold_count > lcnt - 2 {
return Err(ConfigError::ClockTooSlow);
}
assert!(sda_tx_hold_count <= lcnt - 2);
p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));
p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
@ -454,9 +440,10 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
p.ic_sda_hold()
.modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
// Enable I2C block
p.ic_enable().write(|w| w.set_enable(true));
Ok(())
Self { phantom: PhantomData }
}
fn setup(addr: u16) -> Result<(), Error> {
@ -770,15 +757,6 @@ where
}
}
impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
type Config = Config;
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config_inner(config)
}
}
/// Check if address is reserved.
pub fn i2c_reserved_addr(addr: u16) -> bool {
((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0

View file

@ -21,16 +21,6 @@ pub enum Error {
Abort(AbortReason),
/// User passed in a response buffer that was 0 length
InvalidResponseBufferLength,
/// The response buffer length was too short to contain the message
///
/// The length parameter will always be the length of the buffer, and is
/// provided as a convenience for matching alongside `Command::Write`.
PartialWrite(usize),
/// The response buffer length was too short to contain the message
///
/// The length parameter will always be the length of the buffer, and is
/// provided as a convenience for matching alongside `Command::GeneralCall`.
PartialGeneralCall(usize),
}
/// Received command
@ -66,24 +56,17 @@ pub enum ReadStatus {
pub struct Config {
/// Target Address
pub addr: u16,
/// Control if the peripheral should ack to and report general calls.
pub general_call: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
addr: 0x55,
general_call: true,
}
Self { addr: 0x55 }
}
}
/// I2CSlave driver.
pub struct I2cSlave<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
pending_byte: Option<u8>,
config: Config,
}
impl<'d, T: Instance> I2cSlave<'d, T> {
@ -100,25 +83,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
assert!(!i2c_reserved_addr(config.addr));
assert!(config.addr != 0);
// Configure SCL & SDA pins
set_up_i2c_pin(&scl);
set_up_i2c_pin(&sda);
let mut ret = Self {
phantom: PhantomData,
pending_byte: None,
config,
};
ret.reset();
ret
}
/// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.
/// You can recover the bus by calling this function, but doing so will almost certainly cause
/// an i/o error in the master.
pub fn reset(&mut self) {
let p = T::regs();
let reset = T::reset();
@ -127,24 +91,12 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
p.ic_enable().write(|w| w.set_enable(false));
p.ic_sar().write(|w| w.set_ic_sar(self.config.addr));
p.ic_sar().write(|w| w.set_ic_sar(config.addr));
p.ic_con().modify(|w| {
w.set_master_mode(false);
w.set_ic_slave_disable(false);
w.set_tx_empty_ctrl(true);
w.set_rx_fifo_full_hld_ctrl(true);
// This typically makes no sense for a slave, but it is used to
// tune spike suppression, according to the datasheet.
w.set_speed(pac::i2c::vals::Speed::FAST);
// Generate stop interrupts for general calls
// This also causes stop interrupts for other devices on the bus but those will not be
// propagated up to the application.
w.set_stop_det_ifaddressed(!self.config.general_call);
});
p.ic_ack_general_call()
.write(|w| w.set_ack_gen_call(self.config.general_call));
// Set FIFO watermarks to 1 to make things simpler. This is encoded
// by a register value of 0. Rx watermark should never change, but Tx watermark will be
@ -152,6 +104,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
// Configure SCL & SDA pins
set_up_i2c_pin(&scl);
set_up_i2c_pin(&sda);
// Clear interrupts
p.ic_clr_intr().read();
@ -162,6 +118,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { phantom: PhantomData }
}
/// Calls `f` to check if we are ready or not.
@ -175,6 +133,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
future::poll_fn(|cx| {
let r = f(self);
trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0);
if r.is_pending() {
T::waker().register(cx.waker());
g(self);
@ -186,103 +146,71 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
}
#[inline(always)]
fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize {
let p = T::regs();
if let Some(pending) = self.pending_byte.take() {
buffer[*offset] = pending;
*offset += 1;
let len = p.ic_rxflr().read().rxflr() as usize;
let end = offset + len;
for i in offset..end {
buffer[i] = p.ic_data_cmd().read().dat();
}
end
}
for b in &mut buffer[*offset..] {
if !p.ic_status().read().rfne() {
break;
}
let dat = p.ic_data_cmd().read();
if *offset != 0 && dat.first_data_byte() {
// The RP2040 state machine will keep placing bytes into the
// FIFO, even if they are part of a subsequent write transaction.
//
// Unfortunately merely reading ic_data_cmd will consume that
// byte, the first byte of the next transaction, so we need
// to store it elsewhere
self.pending_byte = Some(dat.dat());
break;
}
*b = dat.dat();
*offset += 1;
#[inline(always)]
fn write_to_fifo(&mut self, buffer: &[u8]) {
let p = T::regs();
for byte in buffer {
p.ic_data_cmd().write(|w| w.set_dat(*byte));
}
}
/// Wait asynchronously for commands from an I2C master.
/// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
let p = T::regs();
p.ic_clr_intr().read();
// set rx fifo watermark to 1 byte
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
let mut len = 0;
self.wait_on(
|me| {
let stat = p.ic_raw_intr_stat().read();
trace!("ls:{:013b} len:{}", stat.0, len);
if p.ic_rxflr().read().rxflr() > 0 || me.pending_byte.is_some() {
me.drain_fifo(buffer, &mut len);
// we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise
p.ic_rx_tl().write(|w| w.set_rx_tl(11));
}
if buffer.len() == len {
if stat.gen_call() {
return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));
} else {
return Poll::Ready(Err(Error::PartialWrite(buffer.len())));
let ret = self
.wait_on(
|me| {
let stat = p.ic_raw_intr_stat().read();
if p.ic_rxflr().read().rxflr() > 0 {
len = me.drain_fifo(buffer, len);
// we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise
p.ic_rx_tl().write(|w| w.set_rx_tl(11));
}
}
trace!("len:{}, pend:{:?}", len, me.pending_byte);
if me.pending_byte.is_some() {
warn!("pending")
}
if stat.restart_det() && stat.rd_req() {
p.ic_clr_restart_det().read();
Poll::Ready(Ok(Command::WriteRead(len)))
} else if stat.gen_call() && stat.stop_det() && len > 0 {
p.ic_clr_gen_call().read();
p.ic_clr_stop_det().read();
Poll::Ready(Ok(Command::GeneralCall(len)))
} else if stat.stop_det() && len > 0 {
p.ic_clr_stop_det().read();
Poll::Ready(Ok(Command::Write(len)))
} else if stat.rd_req() {
p.ic_clr_stop_det().read();
p.ic_clr_restart_det().read();
p.ic_clr_gen_call().read();
Poll::Ready(Ok(Command::Read))
} else if stat.stop_det() {
// clear stuck stop bit
// This can happen if the SDA/SCL pullups are enabled after calling this func
p.ic_clr_stop_det().read();
Poll::Pending
} else {
Poll::Pending
}
},
|_me| {
p.ic_intr_mask().write(|w| {
w.set_m_stop_det(true);
w.set_m_restart_det(true);
w.set_m_gen_call(true);
w.set_m_rd_req(true);
w.set_m_rx_full(true);
});
},
)
.await
if stat.restart_det() && stat.rd_req() {
Poll::Ready(Ok(Command::WriteRead(len)))
} else if stat.gen_call() && stat.stop_det() && len > 0 {
Poll::Ready(Ok(Command::GeneralCall(len)))
} else if stat.stop_det() {
Poll::Ready(Ok(Command::Write(len)))
} else if stat.rd_req() {
Poll::Ready(Ok(Command::Read))
} else {
Poll::Pending
}
},
|_me| {
p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true);
w.set_m_restart_det(true);
w.set_m_gen_call(true);
w.set_m_rd_req(true);
w.set_m_rx_full(true);
});
},
)
.await;
p.ic_clr_intr().read();
ret
}
/// Respond to an I2C master READ command, asynchronously.
@ -295,61 +223,54 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
let mut chunks = buffer.chunks(FIFO_SIZE as usize);
self.wait_on(
|me| {
let stat = p.ic_raw_intr_stat().read();
trace!("rs:{:013b}", stat.0);
if stat.tx_abrt() {
let ret = self
.wait_on(
|me| {
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
p.ic_clr_intr().read();
return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
} else {
return Poll::Ready(Err(abort_reason));
}
}
}
if let Some(chunk) = chunks.next() {
for byte in chunk {
p.ic_clr_rd_req().read();
p.ic_data_cmd().write(|w| w.set_dat(*byte));
}
if let Some(chunk) = chunks.next() {
me.write_to_fifo(chunk);
Poll::Pending
} else {
if stat.rx_done() {
p.ic_clr_rx_done().read();
Poll::Ready(Ok(ReadStatus::Done))
} else if stat.rd_req() && stat.tx_empty() {
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
} else {
Poll::Pending
} else {
let stat = p.ic_raw_intr_stat().read();
if stat.rx_done() && stat.stop_det() {
Poll::Ready(Ok(ReadStatus::Done))
} else if stat.rd_req() {
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
} else {
Poll::Pending
}
}
}
},
|_me| {
p.ic_intr_mask().write(|w| {
w.set_m_rx_done(true);
w.set_m_tx_empty(true);
w.set_m_tx_abrt(true);
})
},
)
.await
},
|_me| {
p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true);
w.set_m_rx_done(true);
w.set_m_tx_empty(true);
w.set_m_tx_abrt(true);
})
},
)
.await;
p.ic_clr_intr().read();
ret
}
/// Respond to reads with the fill byte until the controller stops asking
pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> {
// Send fill bytes a full fifo at a time, to reduce interrupt noise.
// This does mean we'll almost certainly abort the write, but since these are fill bytes,
// we don't care.
let buff = [fill; FIFO_SIZE as usize];
loop {
match self.respond_to_read(&buff).await {
match self.respond_to_read(&[fill]).await {
Ok(ReadStatus::NeedMoreBytes) => (),
Ok(ReadStatus::LeftoverBytes(_)) => break Ok(()),
Ok(_) => break Ok(()),
Err(e) => break Err(e),
}
@ -371,7 +292,14 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
#[inline(always)]
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
let p = T::regs();
let abort_reason = p.ic_tx_abrt_source().read();
let mut abort_reason = p.ic_tx_abrt_source().read();
// Mask off fifo flush count
let tx_flush_cnt = abort_reason.tx_flush_cnt();
abort_reason.set_tx_flush_cnt(0);
// Mask off master_dis
abort_reason.set_abrt_master_dis(false);
if abort_reason.0 != 0 {
// Note clearing the abort flag also clears the reason, and this
@ -386,8 +314,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
AbortReason::NoAcknowledge
} else if abort_reason.arb_lost() {
AbortReason::ArbitrationLoss
} else if abort_reason.tx_flush_cnt() > 0 {
AbortReason::TxNotEmpty(abort_reason.tx_flush_cnt())
} else if abort_reason.abrt_slvflush_txfifo() {
AbortReason::TxNotEmpty(tx_flush_cnt)
} else {
AbortReason::Other(abort_reason.0)
};

View file

@ -118,17 +118,6 @@ pub enum Error {
Framing,
}
/// Read To Break error
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ReadToBreakError {
/// Read this many bytes, but never received a line break.
MissingBreak(usize),
/// Other, standard issue with the serial request
Other(Error),
}
/// Internal DMA state of UART RX.
pub struct DmaState {
rx_err_waker: AtomicWaker,
@ -285,17 +274,14 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
/// Read from UART RX blocking execution until done.
pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> {
while !buffer.is_empty() {
let received = self.drain_fifo(buffer).map_err(|(_i, e)| e)?;
while buffer.len() > 0 {
let received = self.drain_fifo(buffer)?;
buffer = &mut buffer[received..];
}
Ok(())
}
/// Returns Ok(len) if no errors occurred. Returns Err((len, err)) if an error was
/// encountered. in both cases, `len` is the number of *good* bytes copied into
/// `buffer`.
fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> {
fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
let r = T::regs();
for (i, b) in buffer.iter_mut().enumerate() {
if r.uartfr().read().rxfe() {
@ -305,13 +291,13 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
let dr = r.uartdr().read();
if dr.oe() {
return Err((i, Error::Overrun));
return Err(Error::Overrun);
} else if dr.be() {
return Err((i, Error::Break));
return Err(Error::Break);
} else if dr.pe() {
return Err((i, Error::Parity));
return Err(Error::Parity);
} else if dr.fe() {
return Err((i, Error::Framing));
return Err(Error::Framing);
} else {
*b = dr.data();
}
@ -403,7 +389,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
} {
Ok(len) if len < buffer.len() => &mut buffer[len..],
Ok(_) => return Ok(()),
Err((_i, e)) => return Err(e),
Err(e) => return Err(e),
};
// start a dma transfer. if errors have happened in the interim some error
@ -440,25 +426,13 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
.await;
let errors = match transfer_result {
Either::First(()) => {
// We're here because the DMA finished, BUT if an error occurred on the LAST
// byte, then we may still need to grab the error state!
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
}
Either::Second(e) => {
// We're here because we errored, which means this is the error that
// was problematic.
e
}
Either::First(()) => return Ok(()),
Either::Second(e) => e,
};
// If we got no error, just return at this point
if errors.0 == 0 {
return Ok(());
}
// If we DID get an error, we need to figure out which one it was.
if errors.oeris() {
} else if errors.oeris() {
return Err(Error::Overrun);
} else if errors.beris() {
return Err(Error::Break);
@ -469,173 +443,6 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
}
unreachable!("unrecognized rx error");
}
/// Read from the UART, waiting for a line break.
///
/// We read until one of the following occurs:
///
/// * We read `buffer.len()` bytes without a line break
/// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`
/// * We read `n` bytes then a line break occurs
/// * returns `Ok(n)`
/// * We encounter some error OTHER than a line break
/// * returns `Err(ReadToBreakError::Other(error))`
///
/// **NOTE**: you MUST provide a buffer one byte larger than your largest expected
/// message to reliably detect the framing on one single call to `read_to_break()`.
///
/// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer:
/// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`
/// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break
/// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer:
/// * The first call to `read_to_break()` will return `Ok(20)`.
/// * The next call to `read_to_break()` will work as expected
pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> {
// clear error flags before we drain the fifo. errors that have accumulated
// in the flags will also be present in the fifo.
T::dma_state().rx_errs.store(0, Ordering::Relaxed);
T::regs().uarticr().write(|w| {
w.set_oeic(true);
w.set_beic(true);
w.set_peic(true);
w.set_feic(true);
});
// then drain the fifo. we need to read at most 32 bytes. errors that apply
// to fifo bytes will be reported directly.
let sbuffer = match {
let limit = buffer.len().min(32);
self.drain_fifo(&mut buffer[0..limit])
} {
// Drained fifo, still some room left!
Ok(len) if len < buffer.len() => &mut buffer[len..],
// Drained (some/all of the fifo), no room left
Ok(len) => return Err(ReadToBreakError::MissingBreak(len)),
// We got a break WHILE draining the FIFO, return what we did get before the break
Err((i, Error::Break)) => return Ok(i),
// Some other error, just return the error
Err((_i, e)) => return Err(ReadToBreakError::Other(e)),
};
// start a dma transfer. if errors have happened in the interim some error
// interrupt flags will have been raised, and those will be picked up immediately
// by the interrupt handler.
let mut ch = self.rx_dma.as_mut().unwrap();
T::regs().uartimsc().write_set(|w| {
w.set_oeim(true);
w.set_beim(true);
w.set_peim(true);
w.set_feim(true);
});
T::regs().uartdmacr().write_set(|reg| {
reg.set_rxdmae(true);
reg.set_dmaonerr(true);
});
let transfer = unsafe {
// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
crate::dma::read(&mut ch, T::regs().uartdr().as_ptr() as *const _, sbuffer, T::RX_DREQ)
};
// wait for either the transfer to complete or an error to happen.
let transfer_result = select(
transfer,
poll_fn(|cx| {
T::dma_state().rx_err_waker.register(cx.waker());
match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) {
0 => Poll::Pending,
e => Poll::Ready(Uartris(e as u32)),
}
}),
)
.await;
// Figure out our error state
let errors = match transfer_result {
Either::First(()) => {
// We're here because the DMA finished, BUT if an error occurred on the LAST
// byte, then we may still need to grab the error state!
Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32)
}
Either::Second(e) => {
// We're here because we errored, which means this is the error that
// was problematic.
e
}
};
if errors.0 == 0 {
// No errors? That means we filled the buffer without a line break.
// For THIS function, that's a problem.
return Err(ReadToBreakError::MissingBreak(buffer.len()));
} else if errors.beris() {
// We got a Line Break! By this point, we've finished/aborted the DMA
// transaction, which means that we need to figure out where it left off
// by looking at the write_addr.
//
// First, we do a sanity check to make sure the write value is within the
// range of DMA we just did.
let sval = buffer.as_ptr() as usize;
let eval = sval + buffer.len();
// This is the address where the DMA would write to next
let next_addr = ch.regs().write_addr().read() as usize;
// If we DON'T end up inside the range, something has gone really wrong.
// Note that it's okay that `eval` is one past the end of the slice, as
// this is where the write pointer will end up at the end of a full
// transfer.
if (next_addr < sval) || (next_addr > eval) {
unreachable!("UART DMA reported invalid `write_addr`");
}
let regs = T::regs();
let all_full = next_addr == eval;
// NOTE: This is off label usage of RSR! See the issue below for
// why I am not checking if there is an "extra" FIFO byte, and why
// I am checking RSR directly (it seems to report the status of the LAST
// POPPED value, rather than the NEXT TO POP value like the datasheet
// suggests!)
//
// issue: https://github.com/raspberrypi/pico-feedback/issues/367
let last_was_break = regs.uartrsr().read().be();
return match (all_full, last_was_break) {
(true, true) | (false, _) => {
// We got less than the full amount + a break, or the full amount
// and the last byte was a break. Subtract the break off by adding one to sval.
Ok(next_addr.saturating_sub(1 + sval))
}
(true, false) => {
// We finished the whole DMA, and the last DMA'd byte was NOT a break
// character. This is an error.
//
// NOTE: we COULD potentially return Ok(buffer.len()) here, since we
// know a line break occured at SOME POINT after the DMA completed.
//
// However, we have no way of knowing if there was extra data BEFORE
// that line break, so instead return an Err to signal to the caller
// that there are "leftovers", and they'll catch the actual line break
// on the next call.
//
// Doing it like this also avoids racyness: now whether you finished
// the full read BEFORE the line break occurred or AFTER the line break
// occurs, you still get `MissingBreak(buffer.len())` instead of sometimes
// getting `Ok(buffer.len())` if you were "late enough" to observe the
// line break.
Err(ReadToBreakError::MissingBreak(buffer.len()))
}
};
} else if errors.oeris() {
return Err(ReadToBreakError::Other(Error::Overrun));
} else if errors.peris() {
return Err(ReadToBreakError::Other(Error::Parity));
} else if errors.feris() {
return Err(ReadToBreakError::Other(Error::Framing));
}
unreachable!("unrecognized rx error");
}
}
impl<'d, T: Instance> Uart<'d, T, Blocking> {
@ -936,13 +743,6 @@ impl<'d, T: Instance> Uart<'d, T, Async> {
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.read(buffer).await
}
/// Read until the buffer is full or a line break occurs.
///
/// See [`UartRx::read_to_break()`] for more details
pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<usize, ReadToBreakError> {
self.rx.read_to_break(buf).await
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, M> {

View file

@ -414,11 +414,12 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
}
fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) {
todo!();
warn!("Call to unsupported function 'endpoint_set_stalled'");
}
fn endpoint_is_stalled(&mut self, _ep_addr: EndpointAddress) -> bool {
todo!();
warn!("Call to unsupported function 'endpoint_is_stalled'");
return false;
}
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {

View file

@ -3,11 +3,6 @@ name = "embassy-stm32-wpan"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Async STM32 WPAN stack for embedded devices in Rust."
keywords = ["embedded", "async", "stm32", "ble", "wpan"]
categories = ["embedded", "hardware-support", "no-std", "asynchronous"]
repository = "https://github.com/embassy-rs/embassy"
documentation = "https://docs.embassy.dev/embassy-stm32-wpan"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/"
@ -34,7 +29,7 @@ aligned = "0.4.1"
bit_field = "0.10.2"
stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
stm32wb-hci = { version = "0.17.0", optional = true }
stm32wb-hci = { git = "https://github.com/Dirbaio/stm32wb-hci", rev = "0aff47e009c30c5fc5d520672625173d75f7505c", optional = true }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
bitflags = { version = "2.3.3", optional = true }
@ -44,8 +39,6 @@ defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embas
ble = ["dep:stm32wb-hci"]
mac = ["dep:bitflags", "dep:embassy-net-driver" ]
extended = []
stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ]
stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ]
stm32wb30ce = [ "embassy-stm32/stm32wb30ce" ]

View file

@ -1,13 +0,0 @@
# embassy-stm32-wpan
Async WPAN (short range wireless) on STM32WB families.
## Features
- Rust interface to the WPAN stack running on the STM32WB co-processor .
- Controller trait implementation for the [stm32wb-hci](https://crates.io/crates/stm32wb-hci) crate.
- Embassy-net driver implementation for 802.15.4 MAC.
## Examples
See the [stm32wb examples](https://github.com/embassy-rs/embassy/tree/main/examples/stm32wb).

View file

@ -18,22 +18,9 @@ fn main() {
// stm32wb tl_mbox link sections
let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string();
let in_file;
if env::var_os("CARGO_FEATURE_EXTENDED").is_some() {
if env::vars()
.map(|(a, _)| a)
.any(|x| x.starts_with("CARGO_FEATURE_STM32WB1"))
{
in_file = "tl_mbox_extended_wb1.x.in";
} else {
in_file = "tl_mbox_extended_wbx5.x.in";
}
} else {
in_file = "tl_mbox.x.in";
}
fs::write(out_file, fs::read_to_string(in_file).unwrap()).unwrap();
fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap();
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rerun-if-changed={}", in_file);
println!("cargo:rerun-if-changed=tl_mbox.x.in");
}
enum GetOneError {

View file

@ -1,7 +1,5 @@
#![no_std]
#![allow(async_fn_in_trait)]
#![doc = include_str!("../README.md")]
// #![warn(missing_docs)]
// This must go FIRST so that all the other modules see its macros.
mod fmt;

View file

@ -1,16 +0,0 @@
MEMORY
{
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 4K
RAMB_SHARED (xrw) : ORIGIN = 0x20030028, LENGTH = 4K
}
/*
* Scatter the mailbox interface memory sections in shared memory
*/
SECTIONS
{
TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED
MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAMB_SHARED
MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAMB_SHARED
}

View file

@ -1,16 +0,0 @@
MEMORY
{
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 2K
RAMB_SHARED (xrw) : ORIGIN = 0x20038000, LENGTH = 10K
}
/*
* Scatter the mailbox interface memory sections in shared memory
*/
SECTIONS
{
TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED
MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAMB_SHARED
MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAMB_SHARED
}

View file

@ -55,12 +55,10 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
embedded-hal-async = { version = "1.0" }
embedded-hal-nb = { version = "1.0" }
embedded-can = "0.4"
embedded-storage = "0.3.1"
embedded-storage-async = { version = "0.4.1" }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
cortex-m-rt = ">=0.6.15,<0.8"
@ -69,9 +67,10 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
rand_core = "0.6.3"
sdio-host = "0.5.0"
critical-section = "1.1"
#stm32-metapac = { version = "15" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5" }
stm32-metapac = { version = "15" }
#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-656ecf6714fa34fdfb3b3e2f2cd034bffed3f303" }
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
stm32-fmc = "0.3.0"
cfg-if = "1.0.0"
@ -81,20 +80,14 @@ chrono = { version = "^0.4", default-features = false, optional = true}
bit_field = "0.10.2"
document-features = "0.2.7"
static_assertions = { version = "1.1" }
volatile-register = { version = "0.2.1" }
bitflags = "2.4.2"
[dev-dependencies]
critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5", default-features = false, features = ["metadata"]}
stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-656ecf6714fa34fdfb3b3e2f2cd034bffed3f303", default-features = false, features = ["metadata"]}
[features]
@ -104,7 +97,7 @@ default = ["rt"]
rt = ["stm32-metapac/rt"]
## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async/defmt-03", "embassy-usb-driver/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"]
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async/defmt-03", "embassy-usb-driver/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"]
exti = []
low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ]
@ -130,8 +123,6 @@ _time-driver = ["dep:embassy-time-driver", "time"]
## Use any time driver
time-driver-any = ["_time-driver"]
## Use TIM1 as time driver
time-driver-tim1 = ["_time-driver"]
## Use TIM2 as time driver
time-driver-tim2 = ["_time-driver"]
## Use TIM3 as time driver
@ -140,24 +131,14 @@ time-driver-tim3 = ["_time-driver"]
time-driver-tim4 = ["_time-driver"]
## Use TIM5 as time driver
time-driver-tim5 = ["_time-driver"]
## Use TIM8 as time driver
time-driver-tim8 = ["_time-driver"]
## Use TIM9 as time driver
time-driver-tim9 = ["_time-driver"]
## Use TIM11 as time driver
time-driver-tim11 = ["_time-driver"]
## Use TIM12 as time driver
time-driver-tim12 = ["_time-driver"]
## Use TIM15 as time driver
time-driver-tim15 = ["_time-driver"]
## Use TIM20 as time driver
time-driver-tim20 = ["_time-driver"]
## Use TIM21 as time driver
time-driver-tim21 = ["_time-driver"]
## Use TIM22 as time driver
time-driver-tim22 = ["_time-driver"]
## Use TIM23 as time driver
time-driver-tim23 = ["_time-driver"]
## Use TIM24 as time driver
time-driver-tim24 = ["_time-driver"]
#! ## Analog Switch Pins (Pxy_C) on STM32H7 series

View file

@ -5,9 +5,8 @@ use std::{env, fs};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use stm32_metapac::metadata::{
MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, METADATA,
};
use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet};
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA};
fn main() {
let target = env::var("TARGET").unwrap();
@ -184,33 +183,34 @@ fn main() {
let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
None => "",
Some("tim1") => "TIM1",
Some("tim2") => "TIM2",
Some("tim3") => "TIM3",
Some("tim4") => "TIM4",
Some("tim5") => "TIM5",
Some("tim8") => "TIM8",
Some("tim9") => "TIM9",
Some("tim11") => "TIM11",
Some("tim12") => "TIM12",
Some("tim15") => "TIM15",
Some("tim20") => "TIM20",
Some("tim21") => "TIM21",
Some("tim22") => "TIM22",
Some("tim23") => "TIM23",
Some("tim24") => "TIM24",
Some("any") => {
// Order of TIM candidators:
// 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
// 2. In same catagory: larger TIM number first
[
"TIM22", "TIM21", "TIM12", "TIM9", // 2CH
"TIM15", // 2CH_CMP
"TIM19", "TIM4", "TIM3", // GP16
"TIM24", "TIM23", "TIM5", "TIM2", // GP32
"TIM20", "TIM8", "TIM1", //ADV
]
.iter()
.find(|tim| singletons.contains(&tim.to_string())).expect("time-driver-any requested, but the chip doesn't have TIM1, TIM2, TIM3, TIM4, TIM5, TIM8, TIM9, TIM12, TIM15, TIM20, TIM21, TIM22, TIM23 or TIM24.")
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(&"TIM9".to_string()) {
"TIM9"
} else if singletons.contains(&"TIM11".to_string()) {
"TIM11"
} 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, TIM9, TIM11, TIM12 or TIM15.")
}
}
_ => panic!("unknown time_driver {:?}", time_driver),
};
@ -355,6 +355,50 @@ fn main() {
g.extend(quote! { pub mod flash_regions { #flash_regions } });
// ========
// Generate DMA IRQs.
let mut dma_irqs: BTreeMap<&str, Vec<(&str, &str, &str)>> = BTreeMap::new();
for p in METADATA.peripherals {
if let Some(r) = &p.registers {
if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" {
if p.name == "BDMA1" {
// BDMA1 in H7 doesn't use DMAMUX, which breaks
continue;
}
for irq in p.interrupts {
dma_irqs
.entry(irq.interrupt)
.or_default()
.push((r.kind, p.name, irq.signal));
}
}
}
}
let dma_irqs: TokenStream = dma_irqs
.iter()
.map(|(irq, channels)| {
let irq = format_ident!("{}", irq);
let xdma = format_ident!("{}", channels[0].0);
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
quote! {
#[cfg(feature = "rt")]
#[crate::interrupt]
unsafe fn #irq () {
#(
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
)*
}
}
})
.collect();
g.extend(dma_irqs);
// ========
// Extract the rcc registers
let rcc_registers = METADATA
@ -364,142 +408,43 @@ fn main() {
.find(|r| r.kind == "rcc")
.unwrap();
// ========
// Generate rcc fieldset and enum maps
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect();
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect();
rcc_blocks
.iter()
.filter_map(|b| match &b.inner {
BlockItemInner::Register(register) => register.fieldset.map(|f| (b.name, f)),
_ => None,
})
.filter_map(|(b, f)| {
rcc_fieldsets.get(f).map(|f| {
(
b,
f.fields
.iter()
.filter_map(|f| {
let enumm = f.enumm?;
let enumm = rcc_enums.get(enumm)?;
Some((f.name, *enumm))
})
.collect(),
)
})
})
.collect()
};
// ========
// Generate RccPeripheral impls
// count how many times each xxENR field is used, to enable refcounting if used more than once.
let mut rcc_field_count: HashMap<_, usize> = HashMap::new();
for p in METADATA.peripherals {
if let Some(rcc) = &p.rcc {
let en = rcc.enable.as_ref().unwrap();
*rcc_field_count.entry((en.register, en.field)).or_insert(0) += 1;
}
}
struct ClockGen<'a> {
rcc_registers: &'a PeripheralRegisters,
chained_muxes: HashMap<&'a str, &'a PeripheralRccRegister>,
force_refcount: HashSet<&'a str>,
refcount_statics: BTreeSet<Ident>,
clock_names: BTreeSet<String>,
muxes: BTreeSet<(Ident, Ident, Ident)>,
}
let mut clock_gen = ClockGen {
rcc_registers,
chained_muxes: HashMap::new(),
force_refcount: HashSet::from(["usart"]),
refcount_statics: BTreeSet::new(),
clock_names: BTreeSet::new(),
muxes: BTreeSet::new(),
};
if chip_name.starts_with("stm32h5") {
clock_gen.chained_muxes.insert(
"PER",
&PeripheralRccRegister {
register: "CCIPR5",
field: "PERSEL",
},
);
}
if chip_name.starts_with("stm32h7") {
clock_gen.chained_muxes.insert(
"PER",
&PeripheralRccRegister {
register: "D1CCIPR",
field: "PERSEL",
},
);
}
if chip_name.starts_with("stm32u5") {
clock_gen.chained_muxes.insert(
"ICLK",
&PeripheralRccRegister {
register: "CCIPR1",
field: "ICLKSEL",
},
);
}
if chip_name.starts_with("stm32wb") && !chip_name.starts_with("stm32wba") {
clock_gen.chained_muxes.insert(
"CLK48",
&PeripheralRccRegister {
register: "CCIPR",
field: "CLK48SEL",
},
);
}
if chip_name.starts_with("stm32f7") {
clock_gen.chained_muxes.insert(
"CLK48",
&PeripheralRccRegister {
register: "DCKCFGR2",
field: "CLK48SEL",
},
);
}
if chip_name.starts_with("stm32f4") && !chip_name.starts_with("stm32f410") {
clock_gen.chained_muxes.insert(
"CLK48",
&PeripheralRccRegister {
register: "DCKCFGR",
field: "CLK48SEL",
},
);
}
impl<'a> ClockGen<'a> {
fn gen_clock(&mut self, name: &str) -> TokenStream {
let clock_name = format_ident!("{}", name.to_ascii_lowercase());
self.clock_names.insert(name.to_ascii_lowercase());
quote!( unsafe { crate::rcc::get_freqs().#clock_name.unwrap() } )
}
fn gen_mux(&mut self, mux: &PeripheralRccRegister) -> TokenStream {
let ir = &self.rcc_registers.ir;
let fieldset_name = mux.register.to_ascii_lowercase();
let fieldset = ir
.fieldsets
.iter()
.find(|i| i.name.eq_ignore_ascii_case(&fieldset_name))
.unwrap();
let field_name = mux.field.to_ascii_lowercase();
let field = fieldset.fields.iter().find(|i| i.name == field_name).unwrap();
let enum_name = field.enumm.unwrap();
let enumm = ir.enums.iter().find(|i| i.name == enum_name).unwrap();
let fieldset_name = format_ident!("{}", fieldset_name);
let field_name = format_ident!("{}", field_name);
let enum_name = format_ident!("{}", enum_name);
self.muxes
.insert((fieldset_name.clone(), field_name.clone(), enum_name.clone()));
let mut match_arms = TokenStream::new();
for v in enumm.variants.iter().filter(|v| v.name != "DISABLE") {
let variant_name = format_ident!("{}", v.name);
let expr = if let Some(mux) = self.chained_muxes.get(&v.name) {
self.gen_mux(mux)
} else {
self.gen_clock(&v.name)
};
match_arms.extend(quote! {
crate::pac::rcc::vals::#enum_name::#variant_name => #expr,
});
}
quote! {
match crate::pac::RCC.#fieldset_name().read().#field_name() {
#match_arms
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
}
}
let refcounted_peripherals = HashSet::from(["usart", "adc"]);
let mut refcount_statics = BTreeSet::new();
for p in METADATA.peripherals {
if !singletons.contains(&p.name.to_string()) {
@ -532,16 +477,15 @@ fn main() {
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
let pname = format_ident!("{}", p.name);
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
let clk = format_ident!("{}", rcc.clock);
let en_reg = format_ident!("{}", en.register);
let set_en_field = format_ident!("set_{}", en.field);
let refcount =
clock_gen.force_refcount.contains(ptype) || *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1;
let (before_enable, before_disable) = if refcount {
let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) {
let refcount_static =
format_ident!("{}_{}", en.register.to_ascii_uppercase(), en.field.to_ascii_uppercase());
clock_gen.refcount_statics.insert(refcount_static.clone());
refcount_statics.insert(refcount_static.clone());
(
quote! {
@ -561,13 +505,68 @@ fn main() {
(TokenStream::new(), TokenStream::new())
};
let clock_frequency = match &rcc.kernel_clock {
PeripheralRccKernelClock::Mux(mux) => clock_gen.gen_mux(mux),
PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(clock),
let mux_supported = HashSet::from(["c0", "h5", "h50", "h7", "h7ab", "h7rm0433", "g0", "g4", "l4"])
.contains(rcc_registers.version);
let mux_for = |mux: Option<&'static PeripheralRccRegister>| {
// restrict mux implementation to supported versions
if !mux_supported {
return None;
}
let mux = mux?;
let fieldset = rcc_enum_map.get(mux.register)?;
let enumm = fieldset.get(mux.field)?;
Some((mux, *enumm))
};
// A refcount leak can result if the same field is shared by peripherals with different stop modes
// This condition should be checked in stm32-data
let clock_frequency = match mux_for(rcc.mux.as_ref()) {
Some((mux, rcc_enumm)) => {
let fieldset_name = format_ident!("{}", mux.register);
let field_name = format_ident!("{}", mux.field);
let enum_name = format_ident!("{}", rcc_enumm.name);
let match_arms: TokenStream = rcc_enumm
.variants
.iter()
.filter(|v| v.name != "DISABLE")
.map(|v| {
let variant_name = format_ident!("{}", v.name);
let clock_name = format_ident!("{}", v.name.to_ascii_lowercase());
if v.name.starts_with("HCLK") || v.name.starts_with("PCLK") || v.name == "SYS" {
quote! {
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name },
}
} else {
quote! {
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
}
}
})
.collect();
quote! {
use crate::pac::rcc::vals::#enum_name;
#[allow(unreachable_patterns)]
match crate::pac::RCC.#fieldset_name().read().#field_name() {
#match_arms
_ => unreachable!(),
}
}
}
None => quote! {
unsafe { crate::rcc::get_freqs().#clk }
},
};
/*
A refcount leak can result if the same field is shared by peripherals with different stop modes
This condition should be checked in stm32-data
*/
let stop_refcount = match rcc.stop_mode {
StopMode::Standby => None,
StopMode::Stop2 => Some(quote! { REFCOUNT_STOP2 }),
@ -612,110 +611,7 @@ fn main() {
}
}
let struct_fields: Vec<_> = clock_gen
.muxes
.iter()
.map(|(_fieldset, fieldname, enum_name)| {
quote! {
pub #fieldname: #enum_name
}
})
.collect();
let mut inits = TokenStream::new();
for fieldset in clock_gen
.muxes
.iter()
.map(|(f, _, _)| f)
.collect::<BTreeSet<_>>()
.into_iter()
{
let setters: Vec<_> = clock_gen
.muxes
.iter()
.filter(|(f, _, _)| f == fieldset)
.map(|(_, fieldname, _)| {
let setter = format_ident!("set_{}", fieldname);
quote! {
w.#setter(self.#fieldname);
}
})
.collect();
inits.extend(quote! {
crate::pac::RCC.#fieldset().modify(|w| {
#(#setters)*
});
})
}
let enum_names: BTreeSet<_> = clock_gen.muxes.iter().map(|(_, _, enum_name)| enum_name).collect();
g.extend(quote! {
pub mod mux {
#(pub use crate::pac::rcc::vals::#enum_names as #enum_names; )*
#[derive(Clone, Copy)]
pub struct ClockMux {
#( #struct_fields, )*
}
impl ClockMux {
pub(crate) const fn default() -> Self {
// safety: zero value is valid for all PAC enums.
unsafe { ::core::mem::zeroed() }
}
}
impl Default for ClockMux {
fn default() -> Self {
Self::default()
}
}
impl ClockMux {
pub(crate) fn init(&self) {
#inits
}
}
}
});
// Generate RCC
clock_gen.clock_names.insert("sys".to_string());
clock_gen.clock_names.insert("rtc".to_string());
let clock_idents: Vec<_> = clock_gen.clock_names.iter().map(|n| format_ident!("{}", n)).collect();
g.extend(quote! {
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clocks {
#(
pub #clock_idents: Option<crate::time::Hertz>,
)*
}
});
let clocks_macro = quote!(
macro_rules! set_clocks {
($($(#[$m:meta])* $k:ident: $v:expr,)*) => {
{
#[allow(unused)]
struct Temp {
$($(#[$m])* $k: Option<crate::time::Hertz>,)*
}
let all = Temp {
$($(#[$m])* $k: $v,)*
};
crate::rcc::set_freqs(crate::rcc::Clocks {
#( #clock_idents: all.#clock_idents, )*
});
}
};
}
);
let refcount_mod: TokenStream = clock_gen
.refcount_statics
let refcount_mod: TokenStream = refcount_statics
.iter()
.map(|refcount_static| {
quote! {
@ -763,7 +659,7 @@ fn main() {
#[rustfmt::skip]
let signals: HashMap<_, _> = [
// (kind, signal) => trait
// (kind, signal) => trait
(("usart", "TX"), quote!(crate::usart::TxPin)),
(("usart", "RX"), quote!(crate::usart::RxPin)),
(("usart", "CTS"), quote!(crate::usart::CtsPin)),
@ -833,20 +729,13 @@ fn main() {
(("can", "TX"), quote!(crate::can::TxPin)),
(("can", "RX"), quote!(crate::can::RxPin)),
(("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)),
(("eth", "RX_CLK"), quote!(crate::eth::RXClkPin)),
(("eth", "TX_CLK"), quote!(crate::eth::TXClkPin)),
(("eth", "MDIO"), quote!(crate::eth::MDIOPin)),
(("eth", "MDC"), quote!(crate::eth::MDCPin)),
(("eth", "CRS_DV"), quote!(crate::eth::CRSPin)),
(("eth", "RX_DV"), quote!(crate::eth::RXDVPin)),
(("eth", "RXD0"), quote!(crate::eth::RXD0Pin)),
(("eth", "RXD1"), quote!(crate::eth::RXD1Pin)),
(("eth", "RXD2"), quote!(crate::eth::RXD2Pin)),
(("eth", "RXD3"), quote!(crate::eth::RXD3Pin)),
(("eth", "TXD0"), quote!(crate::eth::TXD0Pin)),
(("eth", "TXD1"), quote!(crate::eth::TXD1Pin)),
(("eth", "TXD2"), quote!(crate::eth::TXD2Pin)),
(("eth", "TXD3"), quote!(crate::eth::TXD3Pin)),
(("eth", "TX_EN"), quote!(crate::eth::TXEnPin)),
(("fmc", "A0"), quote!(crate::fmc::A0Pin)),
(("fmc", "A1"), quote!(crate::fmc::A1Pin)),
@ -996,7 +885,7 @@ fn main() {
(("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)),
(("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)),
(("quadspi", "CLK"), quote!(crate::qspi::SckPin)),
].into();
].into();
for p in METADATA.peripherals {
if let Some(regs) = &p.registers {
@ -1047,9 +936,9 @@ fn main() {
} else if pin.signal.starts_with("INN") {
// TODO handle in the future when embassy supports differential measurements
None
} else if pin.signal.starts_with("IN") && pin.signal.ends_with('b') {
} else if pin.signal.starts_with("IN") && pin.signal.ends_with("b") {
// we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63
let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix('b').unwrap();
let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix("b").unwrap();
Some(32u8 + signal.parse::<u8>().unwrap())
} else if pin.signal.starts_with("IN") {
Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap())
@ -1058,7 +947,7 @@ fn main() {
};
if let Some(ch) = ch {
g.extend(quote! {
impl_adc_pin!( #peri, #pin_name, #ch);
impl_adc_pin!( #peri, #pin_name, #ch);
})
}
}
@ -1090,7 +979,7 @@ fn main() {
let ch: u8 = pin.signal.strip_prefix("OUT").unwrap().parse().unwrap();
g.extend(quote! {
impl_dac_pin!( #peri, #pin_name, #ch);
impl_dac_pin!( #peri, #pin_name, #ch);
})
}
}
@ -1120,13 +1009,6 @@ fn main() {
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
(("timer", "UP"), quote!(crate::timer::UpDma)),
(("hash", "IN"), quote!(crate::hash::Dma)),
(("cryp", "IN"), quote!(crate::cryp::DmaIn)),
(("cryp", "OUT"), quote!(crate::cryp::DmaOut)),
(("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
(("timer", "CH4"), quote!(crate::timer::Ch4Dma)),
]
.into();
@ -1142,6 +1024,16 @@ fn main() {
}
if let Some(tr) = signals.get(&(regs.kind, ch.signal)) {
// TIM6 of stm32f334 is special, DMA channel for TIM6 depending on SYSCFG state
if chip_name.starts_with("stm32f334") && p.name == "TIM6" {
continue;
}
// TIM6 of stm32f378 is special, DMA channel for TIM6 depending on SYSCFG state
if chip_name.starts_with("stm32f378") && p.name == "TIM6" {
continue;
}
let peri = format_ident!("{}", p.name);
let channel = if let Some(channel) = &ch.channel {
@ -1290,6 +1182,7 @@ fn main() {
let mut interrupts_table: Vec<Vec<String>> = Vec::new();
let mut peripherals_table: Vec<Vec<String>> = Vec::new();
let mut pins_table: Vec<Vec<String>> = Vec::new();
let mut dma_channels_table: Vec<Vec<String>> = Vec::new();
let mut adc_common_table: Vec<Vec<String>> = Vec::new();
/*
@ -1299,7 +1192,7 @@ fn main() {
ADC3 and higher are assigned to the adc34 clock in the table
The adc3_common cfg directive is added if ADC3_COMMON exists
*/
let has_adc3 = METADATA.peripherals.iter().any(|p| p.name == "ADC3_COMMON");
let has_adc3 = METADATA.peripherals.iter().find(|p| p.name == "ADC3_COMMON").is_some();
let set_adc345 = HashSet::from(["ADC3", "ADC4", "ADC5"]);
for m in METADATA
@ -1383,108 +1276,51 @@ fn main() {
}
}
let mut dmas = TokenStream::new();
let has_dmamux = METADATA
.peripherals
.iter()
.flat_map(|p| &p.registers)
.any(|p| p.kind == "dmamux");
for (ch_idx, ch) in METADATA.dma_channels.iter().enumerate() {
// Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it.
if has_dmamux && ch.dmamux.is_none() {
continue;
}
let name = format_ident!("{}", ch.name);
let idx = ch_idx as u8;
g.extend(quote!(dma_channel_impl!(#name, #idx);));
let dma = format_ident!("{}", ch.dma);
let ch_num = ch.channel as usize;
let mut dma_channel_count: usize = 0;
let mut bdma_channel_count: usize = 0;
let mut gpdma_channel_count: usize = 0;
for ch in METADATA.dma_channels {
let mut row = Vec::new();
let dma_peri = METADATA.peripherals.iter().find(|p| p.name == ch.dma).unwrap();
let bi = dma_peri.registers.as_ref().unwrap();
let dma_info = match bi.kind {
"dma" => quote!(crate::dma::DmaInfo::Dma(crate::pac::#dma)),
"bdma" => quote!(crate::dma::DmaInfo::Bdma(crate::pac::#dma)),
"gpdma" => quote!(crate::pac::#dma),
let num;
match bi.kind {
"dma" => {
num = dma_channel_count;
dma_channel_count += 1;
}
"bdma" => {
num = bdma_channel_count;
bdma_channel_count += 1;
}
"gpdma" => {
num = gpdma_channel_count;
gpdma_channel_count += 1;
}
_ => panic!("bad dma channel kind {}", bi.kind),
};
let dmamux = match &ch.dmamux {
Some(dmamux) => {
let dmamux = format_ident!("{}", dmamux);
let num = ch.dmamux_channel.unwrap() as usize;
g.extend(quote!(dmamux_channel_impl!(#name, #dmamux);));
quote! {
dmamux: crate::dma::DmamuxInfo {
mux: crate::pac::#dmamux,
num: #num,
},
}
}
None => quote!(),
};
dmas.extend(quote! {
crate::dma::ChannelInfo {
dma: #dma_info,
num: #ch_num,
#dmamux
},
});
}
// ========
// Generate DMA IRQs.
let mut dma_irqs: BTreeMap<&str, Vec<String>> = BTreeMap::new();
for p in METADATA.peripherals {
if let Some(r) = &p.registers {
if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" {
for irq in p.interrupts {
let ch_name = format!("{}_{}", p.name, irq.signal);
let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap();
// Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it.
if has_dmamux && ch.dmamux.is_none() {
continue;
}
dma_irqs.entry(irq.interrupt).or_default().push(ch_name);
}
}
}
row.push(ch.name.to_string());
row.push(ch.dma.to_string());
row.push(bi.kind.to_string());
row.push(ch.channel.to_string());
row.push(num.to_string());
if let Some(dmamux) = &ch.dmamux {
let dmamux_channel = ch.dmamux_channel.unwrap();
row.push(format!("{{dmamux: {}, dmamux_channel: {}}}", dmamux, dmamux_channel));
} else {
row.push("{}".to_string());
}
dma_channels_table.push(row);
}
let dma_irqs: TokenStream = dma_irqs
.iter()
.map(|(irq, channels)| {
let irq = format_ident!("{}", irq);
let channels = channels.iter().map(|c| format_ident!("{}", c));
quote! {
#[cfg(feature = "rt")]
#[crate::interrupt]
unsafe fn #irq () {
#(
<crate::peripherals::#channels as crate::dma::sealed::ChannelInterrupt>::on_irq();
)*
}
}
})
.collect();
g.extend(dma_irqs);
g.extend(quote! {
pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas];
pub(crate) const DMA_CHANNEL_COUNT: usize = #dma_channel_count;
pub(crate) const BDMA_CHANNEL_COUNT: usize = #bdma_channel_count;
pub(crate) const GPDMA_CHANNEL_COUNT: usize = #gpdma_channel_count;
});
for irq in METADATA.interrupts {
@ -1495,7 +1331,7 @@ fn main() {
}
}
let mut m = clocks_macro.to_string();
let mut m = String::new();
// DO NOT ADD more macros like these.
// These turned to be a bad idea!
@ -1504,6 +1340,7 @@ fn main() {
make_table(&mut m, "foreach_interrupt", &interrupts_table);
make_table(&mut m, "foreach_peripheral", &peripherals_table);
make_table(&mut m, "foreach_pin", &pins_table);
make_table(&mut m, "foreach_dma_channel", &dma_channels_table);
make_table(&mut m, "foreach_adc", &adc_common_table);
let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
@ -1539,7 +1376,6 @@ fn main() {
// =======
// ADC3_COMMON is present
#[allow(clippy::print_literal)]
if has_adc3 {
println!("cargo:rustc-cfg={}", "adc3_common");
}

View file

@ -6,6 +6,7 @@ use embassy_hal_internal::into_ref;
use embedded_hal_02::blocking::delay::DelayUs;
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
use crate::rcc::get_freqs;
use crate::time::Hertz;
use crate::{interrupt, Peripheral};
@ -74,24 +75,24 @@ impl<'d, T: Instance> Adc<'d, T> {
Self {
adc,
sample_time: SampleTime::from_bits(0),
sample_time: Default::default(),
}
}
fn freq() -> Hertz {
T::frequency()
unsafe { get_freqs() }.adc.unwrap()
}
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
match us * Self::freq().0 / 1_000_000 {
0..=1 => SampleTime::CYCLES1_5,
2..=7 => SampleTime::CYCLES7_5,
8..=13 => SampleTime::CYCLES13_5,
14..=28 => SampleTime::CYCLES28_5,
29..=41 => SampleTime::CYCLES41_5,
42..=55 => SampleTime::CYCLES55_5,
56..=71 => SampleTime::CYCLES71_5,
_ => SampleTime::CYCLES239_5,
0..=1 => SampleTime::Cycles1_5,
2..=7 => SampleTime::Cycles7_5,
8..=13 => SampleTime::Cycles13_5,
14..=28 => SampleTime::Cycles28_5,
29..=41 => SampleTime::Cycles41_5,
42..=55 => SampleTime::Cycles55_5,
56..=71 => SampleTime::Cycles71_5,
_ => SampleTime::Cycles239_5,
}
}

View file

@ -97,23 +97,23 @@ impl<'d, T: Instance> Adc<'d, T> {
Self {
adc,
sample_time: SampleTime::from_bits(0),
sample_time: Default::default(),
}
}
fn freq() -> Hertz {
<T as crate::rcc::sealed::RccPeripheral>::frequency()
<T as crate::adc::sealed::Instance>::frequency()
}
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
match us * Self::freq().0 / 1_000_000 {
0..=1 => SampleTime::CYCLES1_5,
2..=4 => SampleTime::CYCLES4_5,
5..=7 => SampleTime::CYCLES7_5,
8..=19 => SampleTime::CYCLES19_5,
20..=61 => SampleTime::CYCLES61_5,
62..=181 => SampleTime::CYCLES181_5,
_ => SampleTime::CYCLES601_5,
0..=1 => SampleTime::Cycles1_5,
2..=4 => SampleTime::Cycles4_5,
5..=7 => SampleTime::Cycles7_5,
8..=19 => SampleTime::Cycles19_5,
20..=61 => SampleTime::Cycles61_5,
62..=181 => SampleTime::Cycles181_5,
_ => SampleTime::Cycles601_5,
}
}

View file

@ -107,12 +107,12 @@ impl Calibration {
/// Returns a calibrated voltage value as in microvolts (uV)
pub fn cal_uv(&self, raw: u16, resolution: super::Resolution) -> u32 {
(self.vdda_uv() / super::resolution_to_max_count(resolution)) * raw as u32
(self.vdda_uv() / resolution.to_max_count()) * raw as u32
}
/// Returns a calibrated voltage value as an f32
pub fn cal_f32(&self, raw: u16, resolution: super::Resolution) -> f32 {
raw as f32 * self.vdda_f32() / super::resolution_to_max_count(resolution) as f32
raw as f32 * self.vdda_f32() / resolution.to_max_count() as f32
}
}
@ -175,7 +175,12 @@ impl<'d, T: Instance> Adc<'d, T> {
}
pub fn resolution(&self) -> Resolution {
T::regs().cr1().read().res()
match T::regs().cr1().read().res() {
crate::pac::adc::vals::Res::TWELVEBIT => Resolution::TwelveBit,
crate::pac::adc::vals::Res::TENBIT => Resolution::TenBit,
crate::pac::adc::vals::Res::EIGHTBIT => Resolution::EightBit,
crate::pac::adc::vals::Res::SIXBIT => Resolution::SixBit,
}
}
pub fn enable_vref(&self) -> Vref<T> {
@ -354,23 +359,23 @@ impl<'d, T: Instance> Adc<'d, T> {
fn get_res_clks(res: Resolution) -> u32 {
match res {
Resolution::BITS12 => 12,
Resolution::BITS10 => 11,
Resolution::BITS8 => 9,
Resolution::BITS6 => 7,
Resolution::TwelveBit => 12,
Resolution::TenBit => 11,
Resolution::EightBit => 9,
Resolution::SixBit => 7,
}
}
fn get_sample_time_clks(sample_time: SampleTime) -> u32 {
match sample_time {
SampleTime::CYCLES4 => 4,
SampleTime::CYCLES9 => 9,
SampleTime::CYCLES16 => 16,
SampleTime::CYCLES24 => 24,
SampleTime::CYCLES48 => 48,
SampleTime::CYCLES96 => 96,
SampleTime::CYCLES192 => 192,
SampleTime::CYCLES384 => 384,
SampleTime::Cycles4 => 4,
SampleTime::Cycles9 => 9,
SampleTime::Cycles16 => 16,
SampleTime::Cycles24 => 24,
SampleTime::Cycles48 => 48,
SampleTime::Cycles96 => 96,
SampleTime::Cycles192 => 192,
SampleTime::Cycles384 => 384,
}
}
@ -379,14 +384,14 @@ impl<'d, T: Instance> Adc<'d, T> {
let us_clks = us * Self::freq().0 / 1_000_000;
let clks = us_clks.saturating_sub(res_clks);
match clks {
0..=4 => SampleTime::CYCLES4,
5..=9 => SampleTime::CYCLES9,
10..=16 => SampleTime::CYCLES16,
17..=24 => SampleTime::CYCLES24,
25..=48 => SampleTime::CYCLES48,
49..=96 => SampleTime::CYCLES96,
97..=192 => SampleTime::CYCLES192,
193.. => SampleTime::CYCLES384,
0..=4 => SampleTime::Cycles4,
5..=9 => SampleTime::Cycles9,
10..=16 => SampleTime::Cycles16,
17..=24 => SampleTime::Cycles24,
25..=48 => SampleTime::Cycles48,
49..=96 => SampleTime::Cycles96,
97..=192 => SampleTime::Cycles192,
193.. => SampleTime::Cycles384,
}
}

View file

@ -8,19 +8,23 @@
#[cfg_attr(adc_f3, path = "f3.rs")]
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
#[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_l0, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.rs")]
#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
#[cfg_attr(adc_v4, path = "v4.rs")]
mod _version;
#[cfg(not(any(adc_f1, adc_f3_v2)))]
mod resolution;
mod sample_time;
#[allow(unused)]
#[cfg(not(adc_f3_v2))]
pub use _version::*;
#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))]
pub use resolution::Resolution;
#[cfg(not(adc_f3_v2))]
pub use sample_time::SampleTime;
#[cfg(not(any(adc_f1, adc_f3_v2)))]
pub use crate::pac::adc::vals::Res as Resolution;
pub use crate::pac::adc::vals::SampleTime;
use crate::peripherals;
/// Analog to Digital driver.
@ -32,15 +36,15 @@ pub struct Adc<'d, T: Instance> {
}
pub(crate) mod sealed {
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
pub struct State {
pub waker: AtomicWaker,
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
impl State {
pub const fn new() -> Self {
Self {
@ -55,14 +59,16 @@ pub(crate) mod sealed {
pub trait Instance: InterruptableInstance {
fn regs() -> crate::pac::adc::Adc;
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
#[cfg(adc_f3)]
fn frequency() -> crate::time::Hertz;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
fn state() -> &'static State;
}
pub trait AdcPin<T: Instance> {
#[cfg(any(adc_v1, adc_l0, adc_v2))]
#[cfg(any(adc_v1, adc_v2))]
fn set_as_analog(&mut self) {}
fn channel(&self) -> u8;
@ -74,10 +80,10 @@ pub(crate) mod sealed {
}
/// ADC instance.
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))]
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
/// ADC instance.
#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))]
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
/// ADC pin.
@ -92,12 +98,17 @@ foreach_adc!(
crate::pac::$inst
}
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon {
return crate::pac::$common_inst
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
#[cfg(adc_f3)]
fn frequency() -> crate::time::Hertz {
unsafe { crate::rcc::get_freqs() }.$clock.unwrap()
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
fn state() -> &'static sealed::State {
static STATE: sealed::State = sealed::State::new();
&STATE
@ -121,7 +132,7 @@ macro_rules! impl_adc_pin {
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
impl crate::adc::sealed::AdcPin<peripherals::$inst> for crate::peripherals::$pin {
#[cfg(any(adc_v1, adc_l0, adc_v2))]
#[cfg(any(adc_v1, adc_v2))]
fn set_as_analog(&mut self) {
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
}
@ -132,27 +143,3 @@ macro_rules! impl_adc_pin {
}
};
}
/// Get the maximum reading value for this resolution.
///
/// This is `2**n - 1`.
#[cfg(not(any(adc_f1, adc_f3_v2)))]
pub const fn resolution_to_max_count(res: Resolution) -> u32 {
match res {
#[cfg(adc_v4)]
Resolution::BITS16 => (1 << 16) - 1,
#[cfg(adc_v4)]
Resolution::BITS14 => (1 << 14) - 1,
#[cfg(adc_v4)]
Resolution::BITS14V => (1 << 14) - 1,
#[cfg(adc_v4)]
Resolution::BITS12V => (1 << 12) - 1,
Resolution::BITS12 => (1 << 12) - 1,
Resolution::BITS10 => (1 << 10) - 1,
Resolution::BITS8 => (1 << 8) - 1,
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))]
Resolution::BITS6 => (1 << 6) - 1,
#[allow(unreachable_patterns)]
_ => core::unreachable!(),
}
}

View file

@ -0,0 +1,72 @@
/// ADC resolution
#[allow(missing_docs)]
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Resolution {
TwelveBit,
TenBit,
EightBit,
SixBit,
}
/// ADC resolution
#[allow(missing_docs)]
#[cfg(adc_v4)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Resolution {
SixteenBit,
FourteenBit,
TwelveBit,
TenBit,
EightBit,
}
impl Default for Resolution {
fn default() -> Self {
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
{
Self::TwelveBit
}
#[cfg(adc_v4)]
{
Self::SixteenBit
}
}
}
impl From<Resolution> for crate::pac::adc::vals::Res {
fn from(res: Resolution) -> crate::pac::adc::vals::Res {
match res {
#[cfg(adc_v4)]
Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT,
#[cfg(adc_v4)]
Resolution::FourteenBit => crate::pac::adc::vals::Res::FOURTEENBITV,
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
}
}
}
impl Resolution {
/// Get the maximum reading value for this resolution.
///
/// This is `2**n - 1`.
pub fn to_max_count(&self) -> u32 {
match self {
#[cfg(adc_v4)]
Resolution::SixteenBit => (1 << 16) - 1,
#[cfg(adc_v4)]
Resolution::FourteenBit => (1 << 14) - 1,
Resolution::TwelveBit => (1 << 12) - 1,
Resolution::TenBit => (1 << 10) - 1,
Resolution::EightBit => (1 << 8) - 1,
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
Resolution::SixBit => (1 << 6) - 1,
}
}
}

Some files were not shown because too many files have changed in this diff Show more