Compare commits
No commits in common. "main" and "update-heapless" have entirely different histories.
main
...
update-hea
@ -7,23 +7,14 @@ set -euo pipefail
|
|||||||
export RUSTUP_HOME=/ci/cache/rustup
|
export RUSTUP_HOME=/ci/cache/rustup
|
||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
|
||||||
|
|
||||||
# needed for "dumb HTTP" transport support
|
# needed for "dumb HTTP" transport support
|
||||||
# used when pointing stm32-metapac to a CI-built one.
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
# Restore lockfiles
|
|
||||||
if [ -f /ci/cache/lockfiles.tar ]; then
|
|
||||||
echo Restoring lockfiles...
|
|
||||||
tar xf /ci/cache/lockfiles.tar
|
|
||||||
fi
|
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
./ci-nightly.sh
|
sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
|
||||||
|
|
||||||
# Save lockfiles
|
./ci_stable.sh
|
||||||
echo Saving lockfiles...
|
|
||||||
find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+
|
|
10
.github/ci/build.sh
vendored
10
.github/ci/build.sh
vendored
@ -18,17 +18,7 @@ fi
|
|||||||
# used when pointing stm32-metapac to a CI-built one.
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
# Restore lockfiles
|
|
||||||
if [ -f /ci/cache/lockfiles.tar ]; then
|
|
||||||
echo Restoring lockfiles...
|
|
||||||
tar xf /ci/cache/lockfiles.tar
|
|
||||||
fi
|
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
./ci.sh
|
./ci.sh
|
||||||
|
|
||||||
# Save lockfiles
|
|
||||||
echo Saving lockfiles...
|
|
||||||
find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+
|
|
||||||
|
1
.github/ci/doc.sh
vendored
1
.github/ci/doc.sh
vendored
@ -22,6 +22,7 @@ docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/g
|
|||||||
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
||||||
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
||||||
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
||||||
|
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
|
||||||
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||||
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
|
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
|
||||||
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
||||||
|
12
.github/ci/rustfmt.sh
vendored
12
.github/ci/rustfmt.sh
vendored
@ -1,12 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
## on push branch~=gh-readonly-queue/main/.*
|
|
||||||
## on pull_request
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
export RUSTUP_HOME=/ci/cache/rustup
|
|
||||||
export CARGO_HOME=/ci/cache/cargo
|
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
|
||||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
|
||||||
|
|
||||||
find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021
|
|
13
.github/ci/test-nightly.sh
vendored
13
.github/ci/test-nightly.sh
vendored
@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
## on push branch~=gh-readonly-queue/main/.*
|
|
||||||
## on pull_request
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
export RUSTUP_HOME=/ci/cache/rustup
|
|
||||||
export CARGO_HOME=/ci/cache/cargo
|
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
|
||||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
|
||||||
|
|
||||||
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml
|
|
||||||
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly
|
|
22
.github/ci/test.sh
vendored
22
.github/ci/test.sh
vendored
@ -8,21 +8,29 @@ export RUSTUP_HOME=/ci/cache/rustup
|
|||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
|
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
|
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features ed25519-dalek
|
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly
|
||||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features ed25519-salty
|
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-dalek
|
||||||
|
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-salty
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nrf52840,time-driver-rtc1,gpiote
|
cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1,gpiote
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver
|
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features nightly,time-driver
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
|
||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti
|
||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml
|
cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml
|
||||||
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -3,9 +3,6 @@
|
|||||||
"[toml]": {
|
"[toml]": {
|
||||||
"editor.formatOnSave": false
|
"editor.formatOnSave": false
|
||||||
},
|
},
|
||||||
"[markdown]": {
|
|
||||||
"editor.formatOnSave": false
|
|
||||||
},
|
|
||||||
"rust-analyzer.check.allTargets": false,
|
"rust-analyzer.check.allTargets": false,
|
||||||
"rust-analyzer.check.noDefaultFeatures": true,
|
"rust-analyzer.check.noDefaultFeatures": true,
|
||||||
"rust-analyzer.cargo.noDefaultFeatures": true,
|
"rust-analyzer.cargo.noDefaultFeatures": true,
|
||||||
@ -15,10 +12,14 @@
|
|||||||
//"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
|
//"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
|
||||||
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
|
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
|
||||||
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
||||||
|
"rust-analyzer.cargo.features": [
|
||||||
|
// Uncomment if the example has a "nightly" feature.
|
||||||
|
"nightly",
|
||||||
|
],
|
||||||
"rust-analyzer.linkedProjects": [
|
"rust-analyzer.linkedProjects": [
|
||||||
// Uncomment ONE line for the chip you want to work on.
|
// Uncomment ONE line for the chip you want to work on.
|
||||||
// This makes rust-analyzer work on the example crate and all its dependencies.
|
// This makes rust-analyzer work on the example crate and all its dependencies.
|
||||||
"examples/stm32l4/Cargo.toml",
|
"examples/nrf52840/Cargo.toml",
|
||||||
// "examples/nrf52840-rtic/Cargo.toml",
|
// "examples/nrf52840-rtic/Cargo.toml",
|
||||||
// "examples/nrf5340/Cargo.toml",
|
// "examples/nrf5340/Cargo.toml",
|
||||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||||
|
@ -36,7 +36,7 @@ The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> cr
|
|||||||
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
|
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
|
||||||
|
|
||||||
- **LoRa** -
|
- **LoRa** -
|
||||||
<a href="hthttps://github.com/lora-rs/lora-rs">The lora-rs project</a> provides an async LoRa and LoRaWAN stack that works well on Embassy.
|
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
|
||||||
|
|
||||||
- **USB** -
|
- **USB** -
|
||||||
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
export RUSTFLAGS=-Dwarnings
|
|
||||||
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
|
||||||
if [[ -z "${CARGO_TARGET_DIR}" ]]; then
|
|
||||||
export CARGO_TARGET_DIR=target_ci
|
|
||||||
fi
|
|
||||||
|
|
||||||
cargo batch \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
|
||||||
--- 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 \
|
|
||||||
|
|
214
ci.sh
214
ci.sh
@ -15,112 +15,122 @@ 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"
|
BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --out-dir out/examples/std"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021
|
||||||
|
|
||||||
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 nightly \
|
||||||
--- 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 nightly,log \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
--- 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 thumbv6m-none-eabi --features nightly,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 nightly,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 nightly,arch-cortex-m,integrated-timers \
|
||||||
--- 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 nightly,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 nightly,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 \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,integrated-timers \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,integrated-timers \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \
|
--- 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 embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,igmp,medium-ethernet \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly,dhcpv4-hostname \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,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 \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \
|
||||||
--- 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,nightly \
|
||||||
--- 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-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,nightly \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
--- 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,nightly \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time-driver-rtc1,reset-pin-as-gpio \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52820,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time-driver-rtc1,nfc-pins-as-gpio \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52832,gpiote,time-driver-rtc1,reset-pin-as-gpio \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52833,gpiote,time-driver-rtc1,unstable-traits,nfc-pins-as-gpio \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf9160-s,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf9160-ns,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-app-s,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-app-ns,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-net,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,log,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52840,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,defmt,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52840,log,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52840,defmt,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features log \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features intrinsics \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits,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 thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,unstable-traits,time \
|
||||||
--- 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,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 thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,time \
|
||||||
--- 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,nightly,defmt,time-driver-any,unstable-traits,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,nightly,defmt,exti,time \
|
||||||
--- 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 thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits,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 thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time \
|
||||||
--- 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 thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f412zg,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f413vh,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f415zg,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f417zg,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f423zh,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f412zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f427zi,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f415zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f437zi,log,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f417zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f439zi,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f423zh,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446ze,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f427zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f469zi,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f479zi,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f437zi,log,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f730i8,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f439zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f446ze,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h735zg,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f469zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 nightly,stm32f479zi,defmt,exti,time-driver-any,unstable-traits,embedded-sdmmc,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 nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits,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 nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,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 nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,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 nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb15cc,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l041f6,defmt,exti,time-driver-any,time \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,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 thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits,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 thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits,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 thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits,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 nightly,stm32l073cz,defmt,exti,time-driver-any,unstable-traits,low-power,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 nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits,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 thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits,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 thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits,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 thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits,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 thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits,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 thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits,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 thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits,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 thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits,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 thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits,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 nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits,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 embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits,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 ''\
|
||||||
--- 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 'log' \
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
||||||
@ -128,16 +138,17 @@ cargo batch \
|
|||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \
|
||||||
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \
|
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \
|
||||||
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \
|
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \
|
||||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,nightly \
|
||||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,nightly \
|
||||||
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
|
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
||||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4,nightly \
|
||||||
--- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
|
--- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
|
||||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
|
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
|
||||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
|
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
|
||||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \
|
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \
|
||||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \
|
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \
|
||||||
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
|
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
|
||||||
|
--- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \
|
||||||
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
|
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
|
||||||
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
|
--- 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/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
|
||||||
@ -170,16 +181,13 @@ cargo batch \
|
|||||||
--- 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/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/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-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
|
--- 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 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/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/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/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-eabihf \
|
|
||||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
--- 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 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 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --out-dir out/tests/stm32f446re \
|
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
||||||
@ -210,8 +218,6 @@ cargo batch \
|
|||||||
rm out/tests/stm32wb55rg/wpan_mac
|
rm out/tests/stm32wb55rg/wpan_mac
|
||||||
rm out/tests/stm32wb55rg/wpan_ble
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
|
||||||
# not in CI yet.
|
|
||||||
rm -rf out/tests/stm32f446re
|
|
||||||
|
|
||||||
# unstable, I think it's running out of RAM?
|
# unstable, I think it's running out of RAM?
|
||||||
rm out/tests/stm32f207zg/eth
|
rm out/tests/stm32f207zg/eth
|
||||||
|
77
ci_stable.sh
Executable file
77
ci_stable.sh
Executable file
@ -0,0 +1,77 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export RUSTFLAGS=-Dwarnings
|
||||||
|
export DEFMT_LOG=trace
|
||||||
|
|
||||||
|
cargo batch \
|
||||||
|
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||||
|
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||||
|
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||||
|
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||||
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||||
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||||
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,log,gpiote,time-driver-rtc1 \
|
||||||
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,defmt,gpiote,time-driver-rtc1,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- 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 stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,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 stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits,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 thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits,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 thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \
|
||||||
|
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
@ -1,17 +0,0 @@
|
|||||||
# cyw43-pio
|
|
||||||
|
|
||||||
RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. The PIO driver offloads SPI communication with the WiFi chip and improves throughput.
|
|
||||||
|
|
||||||
## Minimum supported Rust version (MSRV)
|
|
||||||
|
|
||||||
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This work is licensed under either of
|
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
||||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
|
||||||
|
|
||||||
at your option.
|
|
@ -1,19 +1,17 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
use cyw43::SpiBusCyw43;
|
use cyw43::SpiBusCyw43;
|
||||||
use embassy_rp::dma::Channel;
|
use embassy_rp::dma::Channel;
|
||||||
use embassy_rp::gpio::{Drive, Level, Output, Pin, 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::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::{Peripheral, PeripheralRef};
|
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
||||||
use fixed::FixedU32;
|
use fixed::FixedU32;
|
||||||
use pio_proc::pio_asm;
|
use pio_proc::pio_asm;
|
||||||
|
|
||||||
/// SPI comms driven by PIO.
|
|
||||||
pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> {
|
pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> {
|
||||||
cs: Output<'d, CS>,
|
cs: Output<'d, CS>,
|
||||||
sm: StateMachine<'d, PIO, SM>,
|
sm: StateMachine<'d, PIO, SM>,
|
||||||
@ -28,7 +26,6 @@ where
|
|||||||
CS: Pin,
|
CS: Pin,
|
||||||
PIO: Instance,
|
PIO: Instance,
|
||||||
{
|
{
|
||||||
/// Create a new instance of PioSpi.
|
|
||||||
pub fn new<DIO, CLK>(
|
pub fn new<DIO, CLK>(
|
||||||
common: &mut Common<'d, PIO>,
|
common: &mut Common<'d, PIO>,
|
||||||
mut sm: StateMachine<'d, PIO, SM>,
|
mut sm: StateMachine<'d, PIO, SM>,
|
||||||
@ -147,7 +144,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write data to peripheral and return status.
|
|
||||||
pub async fn write(&mut self, write: &[u32]) -> u32 {
|
pub async fn write(&mut self, write: &[u32]) -> u32 {
|
||||||
self.sm.set_enable(false);
|
self.sm.set_enable(false);
|
||||||
let write_bits = write.len() * 32 - 1;
|
let write_bits = write.len() * 32 - 1;
|
||||||
@ -157,10 +153,10 @@ where
|
|||||||
defmt::trace!("write={} read={}", write_bits, read_bits);
|
defmt::trace!("write={} read={}", write_bits, read_bits);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
instr::set_x(&mut self.sm, write_bits as u32);
|
pio_instr_util::set_x(&mut self.sm, write_bits as u32);
|
||||||
instr::set_y(&mut self.sm, read_bits as u32);
|
pio_instr_util::set_y(&mut self.sm, read_bits as u32);
|
||||||
instr::set_pindir(&mut self.sm, 0b1);
|
pio_instr_util::set_pindir(&mut self.sm, 0b1);
|
||||||
instr::exec_jmp(&mut self.sm, self.wrap_target);
|
pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sm.set_enable(true);
|
self.sm.set_enable(true);
|
||||||
@ -175,7 +171,6 @@ where
|
|||||||
status
|
status
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send command and read response into buffer.
|
|
||||||
pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 {
|
pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 {
|
||||||
self.sm.set_enable(false);
|
self.sm.set_enable(false);
|
||||||
let write_bits = 31;
|
let write_bits = 31;
|
||||||
@ -185,10 +180,10 @@ where
|
|||||||
defmt::trace!("write={} read={}", write_bits, read_bits);
|
defmt::trace!("write={} read={}", write_bits, read_bits);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
instr::set_y(&mut self.sm, read_bits as u32);
|
pio_instr_util::set_y(&mut self.sm, read_bits as u32);
|
||||||
instr::set_x(&mut self.sm, write_bits as u32);
|
pio_instr_util::set_x(&mut self.sm, write_bits as u32);
|
||||||
instr::set_pindir(&mut self.sm, 0b1);
|
pio_instr_util::set_pindir(&mut self.sm, 0b1);
|
||||||
instr::exec_jmp(&mut self.sm, self.wrap_target);
|
pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// self.cs.set_low();
|
// self.cs.set_low();
|
||||||
|
@ -11,8 +11,8 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.2", path = "../embassy-time"}
|
embassy-time = { version = "0.1.5", path = "../embassy-time"}
|
||||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ cortex-m = "0.7.6"
|
|||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
||||||
|
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.3" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" }
|
||||||
num_enum = { version = "0.5.7", default-features = false }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
|
@ -45,10 +45,6 @@ nc 192.168.0.250 1234
|
|||||||
```
|
```
|
||||||
Send it some data, you should see it echoed back and printed in the firmware's logs.
|
Send it some data, you should see it echoed back and printed in the firmware's logs.
|
||||||
|
|
||||||
## Minimum supported Rust version (MSRV)
|
|
||||||
|
|
||||||
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This work is licensed under either of
|
This work is licensed under either of
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
use core::iter::zip;
|
|
||||||
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
@ -12,23 +11,11 @@ use crate::ioctl::{IoctlState, IoctlType};
|
|||||||
use crate::structs::*;
|
use crate::structs::*;
|
||||||
use crate::{countries, events, PowerManagementMode};
|
use crate::{countries, events, PowerManagementMode};
|
||||||
|
|
||||||
/// Control errors.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
/// Status code.
|
|
||||||
pub status: u32,
|
pub status: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multicast errors.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum AddMulticastAddressError {
|
|
||||||
/// Not a multicast address.
|
|
||||||
NotMulticast,
|
|
||||||
/// No free address slots.
|
|
||||||
NoFreeSlots,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Control driver.
|
|
||||||
pub struct Control<'a> {
|
pub struct Control<'a> {
|
||||||
state_ch: ch::StateRunner<'a>,
|
state_ch: ch::StateRunner<'a>,
|
||||||
events: &'a Events,
|
events: &'a Events,
|
||||||
@ -44,7 +31,6 @@ impl<'a> Control<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize WiFi controller.
|
|
||||||
pub async fn init(&mut self, clm: &[u8]) {
|
pub async fn init(&mut self, clm: &[u8]) {
|
||||||
const CHUNK_SIZE: usize = 1024;
|
const CHUNK_SIZE: usize = 1024;
|
||||||
|
|
||||||
@ -161,7 +147,6 @@ impl<'a> Control<'a> {
|
|||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set power management mode.
|
|
||||||
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
||||||
// power save mode
|
// power save mode
|
||||||
let mode_num = mode.mode();
|
let mode_num = mode.mode();
|
||||||
@ -174,7 +159,6 @@ impl<'a> Control<'a> {
|
|||||||
self.ioctl_set_u32(86, 0, mode_num).await;
|
self.ioctl_set_u32(86, 0, mode_num).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Join an unprotected network with the provided ssid.
|
|
||||||
pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> {
|
pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> {
|
||||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||||
|
|
||||||
@ -192,7 +176,6 @@ impl<'a> Control<'a> {
|
|||||||
self.wait_for_join(i).await
|
self.wait_for_join(i).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Join an protected network with the provided ssid and passphrase.
|
|
||||||
pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
|
pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
|
||||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||||
|
|
||||||
@ -260,19 +243,16 @@ impl<'a> Control<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set GPIO pin on WiFi chip.
|
|
||||||
pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) {
|
pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) {
|
||||||
assert!(gpio_n < 3);
|
assert!(gpio_n < 3);
|
||||||
self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 })
|
self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 })
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start open access point.
|
|
||||||
pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) {
|
pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) {
|
||||||
self.start_ap(ssid, "", Security::OPEN, channel).await;
|
self.start_ap(ssid, "", Security::OPEN, channel).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start WPA2 protected access point.
|
|
||||||
pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) {
|
pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) {
|
||||||
self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await;
|
self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await;
|
||||||
}
|
}
|
||||||
@ -336,54 +316,6 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
|
self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add specified address to the list of hardware addresses the device
|
|
||||||
/// listens on. The address must be a Group address (I/G bit set). Up
|
|
||||||
/// to 10 addresses are supported by the firmware. Returns the number of
|
|
||||||
/// address slots filled after adding, or an error.
|
|
||||||
pub async fn add_multicast_address(&mut self, address: [u8; 6]) -> Result<usize, AddMulticastAddressError> {
|
|
||||||
// The firmware seems to ignore non-multicast addresses, so let's
|
|
||||||
// prevent the user from adding them and wasting space.
|
|
||||||
if address[0] & 0x01 != 1 {
|
|
||||||
return Err(AddMulticastAddressError::NotMulticast);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
self.get_iovar("mcast_list", &mut buf).await;
|
|
||||||
|
|
||||||
let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;
|
|
||||||
let (used, free) = buf[4..].split_at_mut(n * 6);
|
|
||||||
|
|
||||||
if used.chunks(6).any(|a| a == address) {
|
|
||||||
return Ok(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if free.len() < 6 {
|
|
||||||
return Err(AddMulticastAddressError::NoFreeSlots);
|
|
||||||
}
|
|
||||||
|
|
||||||
free[..6].copy_from_slice(&address);
|
|
||||||
let n = n + 1;
|
|
||||||
buf[..4].copy_from_slice(&(n as u32).to_le_bytes());
|
|
||||||
|
|
||||||
self.set_iovar_v::<80>("mcast_list", &buf).await;
|
|
||||||
Ok(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the list of configured multicast hardware addresses.
|
|
||||||
pub async fn list_mulistcast_addresses(&mut self, result: &mut [[u8; 6]; 10]) -> usize {
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
self.get_iovar("mcast_list", &mut buf).await;
|
|
||||||
|
|
||||||
let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;
|
|
||||||
let used = &buf[4..][..n * 6];
|
|
||||||
|
|
||||||
for (addr, output) in zip(used.chunks(6), result.iter_mut()) {
|
|
||||||
output.copy_from_slice(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
n
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
|
async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
|
||||||
let mut buf = [0; 8];
|
let mut buf = [0; 8];
|
||||||
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
||||||
@ -507,14 +439,13 @@ impl<'a> Control<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WiFi network scanner.
|
|
||||||
pub struct Scanner<'a> {
|
pub struct Scanner<'a> {
|
||||||
subscriber: EventSubscriber<'a>,
|
subscriber: EventSubscriber<'a>,
|
||||||
events: &'a Events,
|
events: &'a Events,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner<'_> {
|
impl Scanner<'_> {
|
||||||
/// Wait for the next found network.
|
/// wait for the next found network
|
||||||
pub async fn next(&mut self) -> Option<BssInfo> {
|
pub async fn next(&mut self) -> Option<BssInfo> {
|
||||||
let event = self.subscriber.next_message_pure().await;
|
let event = self.subscriber.next_message_pure().await;
|
||||||
if event.header.status != EStatus::PARTIAL {
|
if event.header.status != EStatus::PARTIAL {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![allow(async_fn_in_trait)]
|
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
#![doc = include_str!("../README.md")]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
@ -28,7 +27,7 @@ use ioctl::IoctlState;
|
|||||||
|
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, Scanner};
|
pub use crate::control::{Control, Error as ControlError, Scanner};
|
||||||
pub use crate::runner::Runner;
|
pub use crate::runner::Runner;
|
||||||
pub use crate::structs::BssInfo;
|
pub use crate::structs::BssInfo;
|
||||||
|
|
||||||
@ -104,7 +103,6 @@ const CHIP: Chip = Chip {
|
|||||||
chanspec_ctl_sb_mask: 0x0700,
|
chanspec_ctl_sb_mask: 0x0700,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Driver state.
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
ioctl_state: IoctlState,
|
ioctl_state: IoctlState,
|
||||||
ch: ch::State<MTU, 4, 4>,
|
ch: ch::State<MTU, 4, 4>,
|
||||||
@ -112,7 +110,6 @@ pub struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
/// Create new driver state holder.
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ioctl_state: IoctlState::new(),
|
ioctl_state: IoctlState::new(),
|
||||||
@ -122,7 +119,6 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Power management modes.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum PowerManagementMode {
|
pub enum PowerManagementMode {
|
||||||
/// Custom, officially unsupported mode. Use at your own risk.
|
/// Custom, officially unsupported mode. Use at your own risk.
|
||||||
@ -208,13 +204,8 @@ impl PowerManagementMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Embassy-net driver.
|
|
||||||
pub type NetDriver<'a> = ch::Device<'a, MTU>;
|
pub type NetDriver<'a> = ch::Device<'a, MTU>;
|
||||||
|
|
||||||
/// Create a new instance of the CYW43 driver.
|
|
||||||
///
|
|
||||||
/// Returns a handle to the network device, control handle and a runner for driving the low level
|
|
||||||
/// stack.
|
|
||||||
pub async fn new<'a, PWR, SPI>(
|
pub async fn new<'a, PWR, SPI>(
|
||||||
state: &'a mut State,
|
state: &'a mut State,
|
||||||
pwr: PWR,
|
pwr: PWR,
|
||||||
|
@ -1,48 +1,54 @@
|
|||||||
pub static NVRAM: &'static [u8] = b"
|
macro_rules! nvram {
|
||||||
NVRAMRev=$Rev$\x00\
|
($($s:literal,)*) => {
|
||||||
manfid=0x2d0\x00\
|
concat_bytes!($($s, b"\x00",)* b"\x00\x00")
|
||||||
prodid=0x0727\x00\
|
};
|
||||||
vendid=0x14e4\x00\
|
}
|
||||||
devid=0x43e2\x00\
|
|
||||||
boardtype=0x0887\x00\
|
pub static NVRAM: &'static [u8] = &*nvram!(
|
||||||
boardrev=0x1100\x00\
|
b"NVRAMRev=$Rev$",
|
||||||
boardnum=22\x00\
|
b"manfid=0x2d0",
|
||||||
macaddr=00:A0:50:b5:59:5e\x00\
|
b"prodid=0x0727",
|
||||||
sromrev=11\x00\
|
b"vendid=0x14e4",
|
||||||
boardflags=0x00404001\x00\
|
b"devid=0x43e2",
|
||||||
boardflags3=0x04000000\x00\
|
b"boardtype=0x0887",
|
||||||
xtalfreq=37400\x00\
|
b"boardrev=0x1100",
|
||||||
nocrc=1\x00\
|
b"boardnum=22",
|
||||||
ag0=255\x00\
|
b"macaddr=00:A0:50:b5:59:5e",
|
||||||
aa2g=1\x00\
|
b"sromrev=11",
|
||||||
ccode=ALL\x00\
|
b"boardflags=0x00404001",
|
||||||
pa0itssit=0x20\x00\
|
b"boardflags3=0x04000000",
|
||||||
extpagain2g=0\x00\
|
b"xtalfreq=37400",
|
||||||
pa2ga0=-168,6649,-778\x00\
|
b"nocrc=1",
|
||||||
AvVmid_c0=0x0,0xc8\x00\
|
b"ag0=255",
|
||||||
cckpwroffset0=5\x00\
|
b"aa2g=1",
|
||||||
maxp2ga0=84\x00\
|
b"ccode=ALL",
|
||||||
txpwrbckof=6\x00\
|
b"pa0itssit=0x20",
|
||||||
cckbw202gpo=0\x00\
|
b"extpagain2g=0",
|
||||||
legofdmbw202gpo=0x66111111\x00\
|
b"pa2ga0=-168,6649,-778",
|
||||||
mcsbw202gpo=0x77711111\x00\
|
b"AvVmid_c0=0x0,0xc8",
|
||||||
propbw202gpo=0xdd\x00\
|
b"cckpwroffset0=5",
|
||||||
ofdmdigfilttype=18\x00\
|
b"maxp2ga0=84",
|
||||||
ofdmdigfilttypebe=18\x00\
|
b"txpwrbckof=6",
|
||||||
papdmode=1\x00\
|
b"cckbw202gpo=0",
|
||||||
papdvalidtest=1\x00\
|
b"legofdmbw202gpo=0x66111111",
|
||||||
pacalidx2g=45\x00\
|
b"mcsbw202gpo=0x77711111",
|
||||||
papdepsoffset=-30\x00\
|
b"propbw202gpo=0xdd",
|
||||||
papdendidx=58\x00\
|
b"ofdmdigfilttype=18",
|
||||||
ltecxmux=0\x00\
|
b"ofdmdigfilttypebe=18",
|
||||||
ltecxpadnum=0x0102\x00\
|
b"papdmode=1",
|
||||||
ltecxfnsel=0x44\x00\
|
b"papdvalidtest=1",
|
||||||
ltecxgcigpio=0x01\x00\
|
b"pacalidx2g=45",
|
||||||
il0macaddr=00:90:4c:c5:12:38\x00\
|
b"papdepsoffset=-30",
|
||||||
wl0id=0x431b\x00\
|
b"papdendidx=58",
|
||||||
deadman_to=0xffffffff\x00\
|
b"ltecxmux=0",
|
||||||
muxenab=0x100\x00\
|
b"ltecxpadnum=0x0102",
|
||||||
spurconfig=0x3\x00\
|
b"ltecxfnsel=0x44",
|
||||||
glitch_based_crsmin=1\x00\
|
b"ltecxgcigpio=0x01",
|
||||||
btc_mode=1\x00\
|
b"il0macaddr=00:90:4c:c5:12:38",
|
||||||
\x00";
|
b"wl0id=0x431b",
|
||||||
|
b"deadman_to=0xffffffff",
|
||||||
|
b"muxenab=0x100",
|
||||||
|
b"spurconfig=0x3",
|
||||||
|
b"glitch_based_crsmin=1",
|
||||||
|
b"btc_mode=1",
|
||||||
|
);
|
||||||
|
@ -34,7 +34,6 @@ impl Default for LogState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Driver communicating with the WiFi chip.
|
|
||||||
pub struct Runner<'a, PWR, SPI> {
|
pub struct Runner<'a, PWR, SPI> {
|
||||||
ch: ch::Runner<'a, MTU>,
|
ch: ch::Runner<'a, MTU>,
|
||||||
bus: Bus<PWR, SPI>,
|
bus: Bus<PWR, SPI>,
|
||||||
@ -223,7 +222,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the
|
|
||||||
pub async fn run(mut self) -> ! {
|
pub async fn run(mut self) -> ! {
|
||||||
let mut buf = [0; 512];
|
let mut buf = [0; 512];
|
||||||
loop {
|
loop {
|
||||||
|
@ -4,16 +4,13 @@ use crate::fmt::Bytes;
|
|||||||
macro_rules! impl_bytes {
|
macro_rules! impl_bytes {
|
||||||
($t:ident) => {
|
($t:ident) => {
|
||||||
impl $t {
|
impl $t {
|
||||||
/// Bytes consumed by this type.
|
|
||||||
pub const SIZE: usize = core::mem::size_of::<Self>();
|
pub const SIZE: usize = core::mem::size_of::<Self>();
|
||||||
|
|
||||||
/// Convert to byte array.
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
|
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
|
||||||
unsafe { core::mem::transmute(*self) }
|
unsafe { core::mem::transmute(*self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create from byte array.
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {
|
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {
|
||||||
let alignment = core::mem::align_of::<Self>();
|
let alignment = core::mem::align_of::<Self>();
|
||||||
@ -26,7 +23,6 @@ macro_rules! impl_bytes {
|
|||||||
unsafe { core::mem::transmute(bytes) }
|
unsafe { core::mem::transmute(bytes) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create from mutable byte array.
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {
|
pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {
|
||||||
let alignment = core::mem::align_of::<Self>();
|
let alignment = core::mem::align_of::<Self>();
|
||||||
@ -208,7 +204,6 @@ pub struct EthernetHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EthernetHeader {
|
impl EthernetHeader {
|
||||||
/// Swap endianness.
|
|
||||||
pub fn byteswap(&mut self) {
|
pub fn byteswap(&mut self) {
|
||||||
self.ether_type = self.ether_type.to_be();
|
self.ether_type = self.ether_type.to_be();
|
||||||
}
|
}
|
||||||
@ -477,26 +472,19 @@ impl ScanResults {
|
|||||||
#[repr(C, packed(2))]
|
#[repr(C, packed(2))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct BssInfo {
|
pub struct BssInfo {
|
||||||
/// Version.
|
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
/// Length.
|
|
||||||
pub length: u32,
|
pub length: u32,
|
||||||
/// BSSID.
|
|
||||||
pub bssid: [u8; 6],
|
pub bssid: [u8; 6],
|
||||||
/// Beacon period.
|
|
||||||
pub beacon_period: u16,
|
pub beacon_period: u16,
|
||||||
/// Capability.
|
|
||||||
pub capability: u16,
|
pub capability: u16,
|
||||||
/// SSID length.
|
|
||||||
pub ssid_len: u8,
|
pub ssid_len: u8,
|
||||||
/// SSID.
|
|
||||||
pub ssid: [u8; 32],
|
pub ssid: [u8; 32],
|
||||||
// there will be more stuff here
|
// there will be more stuff here
|
||||||
}
|
}
|
||||||
impl_bytes!(BssInfo);
|
impl_bytes!(BssInfo);
|
||||||
|
|
||||||
impl BssInfo {
|
impl BssInfo {
|
||||||
pub(crate) fn parse(packet: &mut [u8]) -> Option<&mut Self> {
|
pub fn parse(packet: &mut [u8]) -> Option<&mut Self> {
|
||||||
if packet.len() < BssInfo::SIZE {
|
if packet.len() < BssInfo::SIZE {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ version = "0.1.0"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-executor = { version = "0.4.0", path = "../../../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||||
embassy-time = { version = "0.2.0", path = "../../../../../embassy-time", features = ["defmt"] }
|
embassy-time = { version = "0.1.4", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
|
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
|
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
|
||||||
embassy-executor = { version = "0.4.0", features = ["arch-cortex-m", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
|
||||||
|
|
||||||
defmt = "0.3.0"
|
defmt = "0.3.0"
|
||||||
defmt-rtt = "0.3.0"
|
defmt-rtt = "0.3.0"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_stm32::exti::ExtiInput;
|
use embassy_stm32::exti::ExtiInput;
|
||||||
|
@ -9,7 +9,7 @@ use {defmt_rtt as _, panic_probe as _};
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
let mut led = Output::new(p.PB14, Level::High, Speed::VeryHigh);
|
let mut led = Output::new(p.PB14, Level::High, Speed::VeryHigh);
|
||||||
let mut button = Input::new(p.PC13, Pull::Up);
|
let button = Input::new(p.PC13, Pull::Up);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_low() {
|
if button.is_low() {
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
* xref:getting_started.adoc[Getting started]
|
* xref:getting_started.adoc[Getting started]
|
||||||
** xref:basic_application.adoc[Basic application]
|
** xref:basic_application.adoc[Basic application]
|
||||||
** xref:project_structure.adoc[Project Structure]
|
|
||||||
** xref:new_project.adoc[Starting a new Embassy project]
|
|
||||||
** xref:best_practices.adoc[Best Practices]
|
|
||||||
* xref:layer_by_layer.adoc[Bare metal to async]
|
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||||
* xref:runtime.adoc[Executor]
|
* xref:runtime.adoc[Executor]
|
||||||
* xref:delaying_a_task.adoc[Delaying a Task]
|
|
||||||
* xref:hal.adoc[HAL]
|
* xref:hal.adoc[HAL]
|
||||||
** xref:nrf.adoc[nRF]
|
** xref:nrf.adoc[nRF]
|
||||||
** xref:stm32.adoc[STM32]
|
** xref:stm32.adoc[STM32]
|
||||||
* xref:bootloader.adoc[Bootloader]
|
* xref:bootloader.adoc[Bootloader]
|
||||||
|
|
||||||
* xref:examples.adoc[Examples]
|
* xref:examples.adoc[Examples]
|
||||||
* xref:developer.adoc[Developer Docs]
|
* xref:developer.adoc[Developer]
|
||||||
** xref:developer_stm32.adoc[Developer Docs: STM32]
|
** xref:developer_stm32.adoc[Developer: STM32]
|
||||||
* xref:embassy_in_the_wild.adoc[Embassy in the wild]
|
|
||||||
* xref:faq.adoc[Frequently Asked Questions]
|
|
||||||
|
@ -6,24 +6,13 @@ So you've got one of the xref:examples.adoc[examples] running, but what now? Let
|
|||||||
|
|
||||||
The full example can be found link:https://github.com/embassy-rs/embassy/tree/master/docs/modules/ROOT/examples/basic[here].
|
The full example can be found link:https://github.com/embassy-rs/embassy/tree/master/docs/modules/ROOT/examples/basic[here].
|
||||||
|
|
||||||
NOTE: If you’re using VS Code and rust-analyzer to view and edit the examples, you may need to make some changes to `.vscode/settings.json` to tell it which project we’re working on. Follow the instructions commented in that file to get rust-analyzer working correctly.
|
|
||||||
|
|
||||||
=== Bare metal
|
|
||||||
|
|
||||||
The first thing you’ll notice are two attributes at the top of the file. These tells the compiler that program has no access to std, and that there is no main function (because it is not run by an OS).
|
|
||||||
|
|
||||||
[source,rust]
|
|
||||||
----
|
|
||||||
include::example$basic/src/main.rs[lines="1..2"]
|
|
||||||
----
|
|
||||||
|
|
||||||
=== Rust Nightly
|
=== Rust Nightly
|
||||||
|
|
||||||
The next declaration is a Rust Unstable feature, which means that Embassy requires Rust Nightly:
|
The first thing you'll notice is a few declarations stating that Embassy requires some nightly features:
|
||||||
|
|
||||||
[source,rust]
|
[source,rust]
|
||||||
----
|
----
|
||||||
include::example$basic/src/main.rs[lines="3"]
|
include::example$basic/src/main.rs[lines="1..3"]
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Dealing with errors
|
=== Dealing with errors
|
||||||
@ -50,9 +39,9 @@ NOTE: Notice that there is no busy waiting going on in this task. It is using th
|
|||||||
|
|
||||||
=== Main
|
=== Main
|
||||||
|
|
||||||
The main entry point of an Embassy application is defined using the `#[embassy_executor::main]` macro. The entry point is passed a `Spawner`, which it can use to spawn other tasks.
|
The main entry point of an Embassy application is defined using the `#[embassy_executor::main]` macro. The entry point is also required to take a `Spawner` and a `Peripherals` argument.
|
||||||
|
|
||||||
We then initialize the HAL with a default config, which gives us a `Peripherals` struct we can use to access the MCU’s various peripherals. In this case, we want to configure one of the pins as a GPIO output driving the LED:
|
The `Spawner` is the way the main application spawns other tasks. The `Peripherals` type comes from the HAL and holds all peripherals that the application may use. In this case, we want to configure one of the pins as a GPIO output driving the LED:
|
||||||
|
|
||||||
[source,rust]
|
[source,rust]
|
||||||
----
|
----
|
||||||
@ -62,6 +51,7 @@ 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:
|
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:
|
||||||
|
|
||||||
. Creates an Embassy Executor
|
. Creates an Embassy Executor
|
||||||
|
. Initializes the microcontroller HAL to get the `Peripherals`
|
||||||
. Defines a main task for the entry point
|
. Defines a main task for the entry point
|
||||||
. Runs the executor spawning the main task
|
. Runs the executor spawning the main task
|
||||||
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
= Best Practices
|
|
||||||
|
|
||||||
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 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]
|
|
||||||
----
|
|
||||||
fn process_buffer(mut buf: [u8; 1024]) -> [u8; 1024] {
|
|
||||||
// do stuff and return new buffer
|
|
||||||
for elem in buf.iter_mut() {
|
|
||||||
*elem = 0;
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() -> () {
|
|
||||||
let buf = [1u8; 1024];
|
|
||||||
let buf_new = process_buffer(buf);
|
|
||||||
// do stuff with buf_new
|
|
||||||
()
|
|
||||||
}
|
|
||||||
----
|
|
||||||
When calling `process_buffer` in your program, a copy of the buffer you pass to the function will be created,
|
|
||||||
consuming another 1024 bytes.
|
|
||||||
After the processing, another 1024 byte buffer will be placed on the stack to be returned to the caller.
|
|
||||||
(You can check the assembly, there will be two memcopy operations, e.g., `bl __aeabi_memcpy` when compiling for a Cortex-M processor.)
|
|
||||||
|
|
||||||
*Possible Solution:*
|
|
||||||
|
|
||||||
Pass the data by reference and not by value on both, the way in and the way out.
|
|
||||||
For example, you could return a slice of the input buffer as the output.
|
|
||||||
Requiring the lifetime of the input slice and the output slice to be the same, the memory safetly of this procedure will be enforced by the compiler.
|
|
||||||
|
|
||||||
[,rust]
|
|
||||||
----
|
|
||||||
fn process_buffer<'a>(buf: &'a mut [u8]) -> &'a mut[u8] {
|
|
||||||
for elem in buf.iter_mut() {
|
|
||||||
*elem = 0;
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() -> () {
|
|
||||||
let mut buf = [1u8; 1024];
|
|
||||||
let buf_new = process_buffer(&mut buf);
|
|
||||||
// do stuff with buf_new
|
|
||||||
()
|
|
||||||
}
|
|
||||||
----
|
|
@ -45,8 +45,6 @@ The BOOTLOADER_STATE partition must be big enough to store one word per page in
|
|||||||
|
|
||||||
The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
|
The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
|
||||||
|
|
||||||
NOTE: The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application.
|
|
||||||
|
|
||||||
=== FirmwareUpdater
|
=== FirmwareUpdater
|
||||||
|
|
||||||
The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call.
|
The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call.
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
= Delaying a Task
|
|
||||||
|
|
||||||
In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure
|
|
||||||
that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides an abstraction
|
|
||||||
to delay the current task for a specified interval of time.
|
|
||||||
|
|
||||||
Timing is serviced by the `embassy::time::Timer` struct, which provides two timing methods.
|
|
||||||
|
|
||||||
`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
|
|
||||||
`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
|
|
||||||
|
|
||||||
An example of a delay is provided as follows:
|
|
||||||
|
|
||||||
[,rust]
|
|
||||||
----
|
|
||||||
use embassy::executor::{task, Executor};
|
|
||||||
use embassy::time::{Duration, Timer};
|
|
||||||
|
|
||||||
#[task]
|
|
||||||
/// Task that ticks periodically
|
|
||||||
async fn tick_periodic() -> ! {
|
|
||||||
loop {
|
|
||||||
rprintln!("tick!");
|
|
||||||
// async sleep primitive, suspends the task for 500ms.
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
@ -1,9 +0,0 @@
|
|||||||
= Embassy in the wild!
|
|
||||||
|
|
||||||
Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/modules/ROOT/pages/embassy_in_the_wild.adoc[add more]!
|
|
||||||
|
|
||||||
* link:https://github.com/cbruiz/printhor/[Printhor: The highly reliable but not necessarily functional 3D printer firmware]
|
|
||||||
** Targets some STM32 MCUs
|
|
||||||
* 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
|
|
@ -1,155 +0,0 @@
|
|||||||
= Frequently Asked Questions
|
|
||||||
|
|
||||||
These are a list of unsorted, commonly asked questions and answers.
|
|
||||||
|
|
||||||
Please feel free to add items to link:https://github.com/embassy-rs/embassy/edit/main/docs/modules/ROOT/pages/faq.adoc[this page], especially if someone in the chat answered a question for you!
|
|
||||||
|
|
||||||
== How to deploy to RP2040 without a debugging probe.
|
|
||||||
|
|
||||||
Install link:https://github.com/JoNil/elf2uf2-rs[elf2uf2-rs] for converting the generated elf binary into a uf2 file.
|
|
||||||
|
|
||||||
Configure the runner to use this tool, add this to `.cargo/config.toml`:
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
|
||||||
runner = "elf2uf2-rs --deploy --serial --verbose"
|
|
||||||
----
|
|
||||||
|
|
||||||
The command-line parameters `--deploy` will detect your device and upload the binary, `--serial` starts a serial connection. See the documentation for more info.
|
|
||||||
|
|
||||||
== Missing main macro
|
|
||||||
|
|
||||||
If you see an error like this:
|
|
||||||
|
|
||||||
[source,rust]
|
|
||||||
----
|
|
||||||
#[embassy_executor::main]
|
|
||||||
| ^^^^ could not find `main` in `embassy_executor`
|
|
||||||
----
|
|
||||||
|
|
||||||
You are likely missing some features of 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].
|
|
||||||
|
|
||||||
== Why is my binary so big?
|
|
||||||
|
|
||||||
The first step to managing your binary size is to set up your link:https://doc.rust-lang.org/cargo/reference/profiles.html[profiles].
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[profile.release]
|
|
||||||
debug = false
|
|
||||||
lto = true
|
|
||||||
opt-level = "s"
|
|
||||||
incremental = false
|
|
||||||
codegen-units = 1
|
|
||||||
----
|
|
||||||
|
|
||||||
All of these flags are elaborated on in the Rust Book page linked above.
|
|
||||||
|
|
||||||
=== My binary is still big... filled with `std::fmt` stuff!
|
|
||||||
|
|
||||||
This means your code is sufficiently complex that `panic!` invocation's formatting requirements could not be optimized out, despite your usage of `panic-halt` or `panic-reset`.
|
|
||||||
|
|
||||||
You can remedy this by adding the following to your `.cargo/config.toml`:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[unstable]
|
|
||||||
build-std = ["core"]
|
|
||||||
build-std-features = ["panic_immediate_abort"]
|
|
||||||
----
|
|
||||||
|
|
||||||
This replaces all panics with a `UDF` (undefined) instruction.
|
|
||||||
|
|
||||||
Depending on your chipset, this will exhibit different behavior.
|
|
||||||
|
|
||||||
Refer to the spec for your chipset, but for `thumbv6m`, it results in a hardfault. Which can be configured like so:
|
|
||||||
|
|
||||||
[source,rust]
|
|
||||||
----
|
|
||||||
#[exception]
|
|
||||||
unsafe fn HardFault(_frame: &ExceptionFrame) -> ! {
|
|
||||||
SCB::sys_reset() // <- you could do something other than reset
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Refer to cortex-m's link:https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.exception.html[exception handling] for more info.
|
|
||||||
|
|
||||||
== `embassy-time` throws linker errors
|
|
||||||
|
|
||||||
If you see linker error like this:
|
|
||||||
|
|
||||||
[source,text]
|
|
||||||
----
|
|
||||||
= note: rust-lld: error: undefined symbol: _embassy_time_now
|
|
||||||
>>> referenced by driver.rs:127 (src/driver.rs:127)
|
|
||||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::now::hefb1f99d6e069842) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
|
||||||
|
|
||||||
rust-lld: error: undefined symbol: _embassy_time_allocate_alarm
|
|
||||||
>>> referenced by driver.rs:134 (src/driver.rs:134)
|
|
||||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::allocate_alarm::hf5145b6bd46706b2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
|
||||||
|
|
||||||
rust-lld: error: undefined symbol: _embassy_time_set_alarm_callback
|
|
||||||
>>> referenced by driver.rs:139 (src/driver.rs:139)
|
|
||||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm_callback::h24f92388d96eafd2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
|
||||||
|
|
||||||
rust-lld: error: undefined symbol: _embassy_time_set_alarm
|
|
||||||
>>> referenced by driver.rs:144 (src/driver.rs:144)
|
|
||||||
>>> embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm::h530a5b1f444a6d5b) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib
|
|
||||||
----
|
|
||||||
|
|
||||||
You probably need to enable a time driver for your HAL (not in `embassy-time`!). For example with `embassy-stm32`, you might need to enable `time-driver-any`:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[dependencies.embassy-stm32]
|
|
||||||
version = "0.1.0"
|
|
||||||
features = [
|
|
||||||
# ...
|
|
||||||
"time-driver-any", # Add this line!
|
|
||||||
# ...
|
|
||||||
]
|
|
||||||
----
|
|
||||||
|
|
||||||
== 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']`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[patch.crates-io]
|
|
||||||
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!
|
|
||||||
|
|
||||||
== How can I optimize the speed of my embassy-stm32 program?
|
|
||||||
|
|
||||||
* Make sure RCC is set up to go as fast as possible
|
|
||||||
* Make sure link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html[flash cache] is enabled
|
|
||||||
* build with `--release`
|
|
||||||
* Set the following keys for the release profile in your `Cargo.toml`:
|
|
||||||
** `opt-level = "s"`
|
|
||||||
** `lto = "fat"`
|
|
||||||
* Set the following keys in the `[unstable]` section of your `.cargo/config.toml`
|
|
||||||
** `build-std = ["core"]`
|
|
||||||
** `build-std-features = ["panic_immediate_abort"]`
|
|
||||||
* Enable feature `embassy-time/generic-queue`, disable feature `embassy-executor/integrated-timers`
|
|
||||||
* When using `InterruptExecutor`:
|
|
||||||
** disable `executor-thread`
|
|
||||||
** make `main`` spawn everything, then enable link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html#method.set_sleeponexit[SCB.SLEEPONEXIT] and `loop { cortex_m::asm::wfi() }`
|
|
||||||
** *Note:* If you need 2 priority levels, using 2 interrupt executors is better than 1 thread executor + 1 interrupt executor.
|
|
@ -9,9 +9,7 @@ If you don't have any supported board, don't worry: you can also run embassy on
|
|||||||
|
|
||||||
== Getting a board with examples
|
== Getting a board with examples
|
||||||
|
|
||||||
Embassy supports many microcontroller families, but the quickest way to get started is by using a board which Embassy has existing example code for.
|
Embassy supports many microcontroller families, but the easiest ways to get started is if you have one of the more common development kits.
|
||||||
|
|
||||||
This list is non-exhaustive. If your board isn’t included here, check the link:https://github.com/embassy-rs/embassy/tree/main/examples[examples folder] to see if example code has been written for it.
|
|
||||||
|
|
||||||
=== nRF kits
|
=== nRF kits
|
||||||
|
|
||||||
@ -38,7 +36,7 @@ This list is non-exhaustive. If your board isn’t included here, check the link
|
|||||||
|
|
||||||
== Running an example
|
== Running an example
|
||||||
|
|
||||||
First you need to clone the link:https://github.com/embassy-rs/embassy[github repository];
|
First you need to clone the [github repository];
|
||||||
|
|
||||||
[source, bash]
|
[source, bash]
|
||||||
----
|
----
|
||||||
@ -46,80 +44,17 @@ git clone https://github.com/embassy-rs/embassy.git
|
|||||||
cd embassy
|
cd embassy
|
||||||
----
|
----
|
||||||
|
|
||||||
Once you have a copy of the repository, find examples folder for your board and, and build an example program. `blinky` is a good choice as all it does is blink an LED – the embedded world’s equivalent of “Hello World”.
|
You can run an example by opening a terminal and entering the following commands:
|
||||||
|
|
||||||
[source, bash]
|
[source, bash]
|
||||||
----
|
----
|
||||||
cd examples/nrf52840
|
cd examples/nrf52840
|
||||||
cargo build --bin blinky --release
|
|
||||||
----
|
|
||||||
|
|
||||||
Once you’ve confirmed you can build the example, connect your computer to your board with a debug probe and run it on hardware:
|
|
||||||
|
|
||||||
[source, bash]
|
|
||||||
----
|
|
||||||
cargo run --bin blinky --release
|
cargo run --bin blinky --release
|
||||||
----
|
----
|
||||||
|
|
||||||
If everything worked correctly, you should see a blinking LED on your board, and debug output similar to this on your computer:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 1m 56s
|
|
||||||
Running `probe-run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky`
|
|
||||||
(HOST) INFO flashing program (71.36 KiB)
|
|
||||||
(HOST) INFO success!
|
|
||||||
────────────────────────────────────────────────────────────────────────────────
|
|
||||||
0 INFO Hello World!
|
|
||||||
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:18
|
|
||||||
1 INFO high
|
|
||||||
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:23
|
|
||||||
2 INFO low
|
|
||||||
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27
|
|
||||||
3 INFO high
|
|
||||||
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:23
|
|
||||||
4 INFO low
|
|
||||||
└─ 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, there’s 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 it’s programming, so you’ll have to edit this file if you want to run examples on other chips.
|
|
||||||
|
|
||||||
=== It didn’t work!
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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:
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
0.000000 INFO Hello World!
|
|
||||||
└─ <invalid location: defmt frame-index: 14>
|
|
||||||
0.000000 DEBUG rcc: Clocks { sys: Hertz(80000000), apb1: Hertz(80000000), apb1_tim: Hertz(80000000), apb2: Hertz(80000000), apb2_tim: Hertz(80000000), ahb1: Hertz(80000000), ahb2: Hertz(80000000), ahb3: Hertz(80000000) }
|
|
||||||
└─ <invalid location: defmt frame-index: 124>
|
|
||||||
0.000061 TRACE allocating type=Interrupt mps=8 interval_ms=255, dir=In
|
|
||||||
└─ <invalid location: defmt frame-index: 68>
|
|
||||||
0.000091 TRACE index=1
|
|
||||||
└─ <invalid location: defmt frame-index: 72>
|
|
||||||
----
|
|
||||||
|
|
||||||
To get rid of the frame-index error add the following to your `Cargo.toml`:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[profile.release]
|
|
||||||
debug = 2
|
|
||||||
----
|
|
||||||
|
|
||||||
If you’re 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?
|
== What's next?
|
||||||
|
|
||||||
Congratulations, you have your first Embassy application running! Here are some suggestions for where to go from here:
|
Congratulations, you have your first Embassy application running! Here are some alternatives on where to go from here:
|
||||||
|
|
||||||
* Read more about the xref:runtime.adoc[executor].
|
* Read more about the xref:runtime.adoc[executor].
|
||||||
* Read more about the xref:hal.adoc[HAL].
|
* Read more about the xref:hal.adoc[HAL].
|
||||||
|
@ -4,56 +4,34 @@ Embassy is a project to make async/await a first-class option for embedded devel
|
|||||||
|
|
||||||
== What is async?
|
== What is async?
|
||||||
|
|
||||||
When handling I/O, software must call functions that block program execution until the I/O operation completes. When running inside of an OS such as Linux, such functions generally transfer control to the kernel so that another task (known as a “thread”) can be executed if available, or the CPU can be put to sleep until another task is ready.
|
When handling I/O, software must call functions that block program execution until the I/O operation completes. When running inside of an OS such as Linux, such functions generally transfer control to the kernel so that another task, known as a thread, can be executed if available, or the CPU can be put to sleep until another such task is ready to perform more work. Because an OS cannot presume that threads will behave cooperatively, threads are relatively resource-intensive, and may be forcibly interrupted they do not transfer control back to the kernel within an allotted time. But if tasks could be presumed to behave cooperatively, or at least not maliciously, it would be possible to create tasks that appear to be almost free when compared to a traditional OS thread. In Rust, these lightweight tasks, known as 'coroutines' or 'goroutines' in other languages, are implemented with async.
|
||||||
|
|
||||||
Because an OS cannot presume that threads will behave cooperatively, threads are relatively resource-intensive, and may be forcibly interrupted they do not transfer control back to the kernel within an allotted time. If tasks could be presumed to behave cooperatively, or at least not maliciously, it would be possible to create tasks that appear to be almost free when compared to a traditional OS thread.
|
Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
||||||
|
|
||||||
In other programming languages, these lightweight tasks are known as “coroutines” or ”goroutines”. In Rust, they are implemented with async. Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute.
|
|
||||||
|
|
||||||
Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
|
||||||
|
|
||||||
== What is Embassy?
|
== What is Embassy?
|
||||||
|
|
||||||
The Embassy project consists of several crates that you can use together or independently:
|
The Embassy project consists of several crates that you can use together or independently:
|
||||||
|
|
||||||
=== Executor
|
* **Executor** - The link:https://docs.embassy.dev/embassy-executor/[embassy-executor] is an async/await executor that generally executes a fixed number of tasks, allocated at startup, though more can be added later. The HAL is an API that you can use to access peripherals, such as USART, UART, I2C, SPI, CAN, and USB. Embassy provides implementations of both async and blocking APIs where it makes sense. DMA (Direct Memory Access) is an example where async is a good fit, whereas GPIO states are a better fit for a blocking API. The executor may also provide a system timer that you can use for both async and blocking delays. For less than one microsecond, blocking delays should be used because the cost of context-switching is too high and the executor will be unable to provide accurate timing.
|
||||||
The link:https://docs.embassy.dev/embassy-executor/[embassy-executor] is an async/await executor that generally executes a fixed number of tasks, allocated at startup, though more can be added later. The executor may also provide a system timer that you can use for both async and blocking delays. For less than one microsecond, blocking delays should be used because the cost of context-switching is too high and the executor will be unable to provide accurate timing.
|
|
||||||
|
|
||||||
=== Hardware Abstraction Layers
|
|
||||||
HALs implement safe Rust API which let you use peripherals such as USART, UART, I2C, SPI, CAN, and USB without having to directly manipulate registers.
|
|
||||||
|
|
||||||
Embassy provides implementations of both async and blocking APIs where it makes sense. DMA (Direct Memory Access) is an example where async is a good fit, whereas GPIO states are a better fit for a blocking API.
|
|
||||||
|
|
||||||
The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
|
||||||
|
|
||||||
* link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
|
||||||
* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
|
||||||
* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller.
|
|
||||||
* link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips.
|
|
||||||
|
|
||||||
|
* **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||||
|
** link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
||||||
|
** link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
||||||
|
** link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller.
|
||||||
|
** link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips.
|
||||||
|
+
|
||||||
NOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There are no dependency on the executor within the HALs. You can even use them without async,
|
NOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There are no dependency on the executor within the HALs. You can even use them without async,
|
||||||
as they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits.
|
as they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits.
|
||||||
|
|
||||||
=== Networking
|
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.
|
||||||
The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.
|
|
||||||
|
|
||||||
=== Bluetooth
|
* **Bluetooth** - The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||||
The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
|
||||||
|
|
||||||
=== LoRa
|
* **LoRa** - link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
|
||||||
link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
|
|
||||||
|
|
||||||
=== USB
|
* **USB** - link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||||
link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
|
||||||
|
|
||||||
=== Bootloader and DFU
|
* **Bootloader and DFU** - link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
||||||
link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
|
||||||
|
|
||||||
== What is DMA?
|
|
||||||
|
|
||||||
For most I/O in embedded devices, the peripheral doesn't directly support the transmission of multiple bits at once, with CAN being a notable exception. Instead, the MCU must write each byte, one at a time, and then wait until the peripheral is ready to send the next. For high I/O rates, this can pose a problem if the MCU must devote an increasing portion of its time handling each byte. The solution to this problem is to use the Direct Memory Access controller.
|
|
||||||
|
|
||||||
The Direct Memory Access controller (DMA) is a controller that is present in MCUs that Embassy supports, including stm32 and nrf. The DMA allows the MCU to set up a transfer, either send or receive, and then wait for the transfer to complete. With DMA, once started, no MCU intervention is required until the transfer is complete, meaning that the MCU can perform other computation, or set up other I/O while the transfer is in progress. For high I/O rates, DMA can cut the time that the MCU spends handling I/O by over half. However, because DMA is more complex to set-up, it is less widely used in the embedded community. Embassy aims to change that by making DMA the first choice rather than the last. Using Embassy, there's no additional tuning required once I/O rates increase because your application is already set-up to handle them.
|
|
||||||
|
|
||||||
== Resources
|
== Resources
|
||||||
|
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
= Starting a new Embassy project
|
|
||||||
|
|
||||||
Once you’ve 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 you’re targeting.
|
|
||||||
|
|
||||||
As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes.
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
[source,bash]
|
|
||||||
----
|
|
||||||
cargo new stm32g474-example
|
|
||||||
cd stm32g474-example
|
|
||||||
----
|
|
||||||
|
|
||||||
to create an empty rust project:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
stm32g474-example
|
|
||||||
├── Cargo.toml
|
|
||||||
└── src
|
|
||||||
└── main.rs
|
|
||||||
----
|
|
||||||
|
|
||||||
Looking in link:https://github.com/embassy-rs/embassy/tree/main/examples[the Embassy examples], we can see there’s a `stm32g4` folder. Find `src/blinky.rs` and copy its contents into our `src/main.rs`.
|
|
||||||
|
|
||||||
== .cargo/config.toml
|
|
||||||
|
|
||||||
Currently, we’d need to provide cargo with a target triple every time we run `cargo build` or `cargo run`. Let’s spare ourselves that work by copying `.cargo/config.toml` from `examples/stm32g4` into our project.
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
stm32g474-example
|
|
||||||
├── .cargo
|
|
||||||
│ └── config.toml
|
|
||||||
├── Cargo.toml
|
|
||||||
└── src
|
|
||||||
└── main.rs
|
|
||||||
----
|
|
||||||
|
|
||||||
In addition to a target triple, `.cargo/config.toml` contains a `runner` key which allows us to conveniently run our project on hardware with `cargo run` via probe-rs. In order for this to work, we need to provide the correct chip ID. We can do this by checking `probe-rs chip list`:
|
|
||||||
|
|
||||||
[source,bash]
|
|
||||||
----
|
|
||||||
$ probe-rs chip list | grep -i stm32g474re
|
|
||||||
STM32G474RETx
|
|
||||||
----
|
|
||||||
|
|
||||||
and copying `STM32G474RETx` into `.cargo/config.toml` as so:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
|
||||||
# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
|
|
||||||
runner = "probe-rs run --chip STM32G474RETx"
|
|
||||||
----
|
|
||||||
|
|
||||||
== Cargo.toml
|
|
||||||
|
|
||||||
Now that cargo knows what target to compile for (and probe-rs knows what chip to run it on), we’re ready to add some dependencies.
|
|
||||||
|
|
||||||
Looking in `examples/stm32g4/Cargo.toml`, we can see that the examples require a number of embassy crates. For blinky, we’ll only need three of them: `embassy-stm32`, `embassy-executor` and `embassy-time`.
|
|
||||||
|
|
||||||
At the time of writing, the latest version of embassy isn‘t available on crates.io, so we need to install it straight from the git repository. The recommended way of doing so is as follows:
|
|
||||||
|
|
||||||
* Copy the required `embassy-*` lines from the example `Cargo.toml`
|
|
||||||
* Make any necessary changes to `features`, e.g. requiring the `stm32g474re` feature of `embassy-stm32`
|
|
||||||
* Remove the `path = ""` keys in the `embassy-*` entries
|
|
||||||
* Create a `[patch.crates-io]` section, with entries for each embassy crate we need. These should all contain identical values: a link to the git repository, and a reference to the commit we’re checking out. Assuming you want the latest commit, you can find it by running `git ls-remote https://github.com/embassy-rs/embassy.git HEAD`
|
|
||||||
|
|
||||||
NOTE: When using this method, it’s necessary that the `version` keys in `[dependencies]` match up with the versions defined in each crate’s `Cargo.toml` in the specificed `rev` under `[patch.crates.io]`. This means that when updating, you have to a pick a new revision, change everything in `[patch.crates.io]` to match it, and then correct any versions under `[dependencies]` which have changed. Hopefully this will no longer be necessary once embassy is released on crates.io!
|
|
||||||
|
|
||||||
At the time of writing, this method produces the following results:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[dependencies]
|
|
||||||
embassy-stm32 = {version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"]}
|
|
||||||
embassy-executor = { version = "0.3.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
|
||||||
embassy-time = { version = "0.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "7703f47c1ecac029f603033b7977d9a2becef48c" }
|
|
||||||
embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "7703f47c1ecac029f603033b7977d9a2becef48c" }
|
|
||||||
embassy-stm32 = { git = "https://github.com/embassy-rs/embassy", rev = "7703f47c1ecac029f603033b7977d9a2becef48c" }
|
|
||||||
----
|
|
||||||
|
|
||||||
There are a few other dependencies we need to build the project, but fortunately they’re much simpler to install. Copy their lines from the example `Cargo.toml` to the the `[dependencies]` section in the new `Cargo.toml`:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
defmt = "0.3.5"
|
|
||||||
defmt-rtt = "0.4.0"
|
|
||||||
cortex-m = {version = "0.7.7", features = ["critical-section-single-core"]}
|
|
||||||
cortex-m-rt = "0.7.3"
|
|
||||||
panic-probe = "0.3.1"
|
|
||||||
----
|
|
||||||
|
|
||||||
These are the bare minimum dependencies required to run `blinky.rs`, but it’s worth taking a look at the other dependencies specified in the example `Cargo.toml`, and noting what features are required for use with embassy – for example `futures = { version = "0.3.17", default-features = false, features = ["async-await"] }`.
|
|
||||||
|
|
||||||
Finally, copy the `[profile.release]` section from the example `Cargo.toml` into ours.
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[profile.release]
|
|
||||||
debug = 2
|
|
||||||
----
|
|
||||||
|
|
||||||
== rust-toolchain.toml
|
|
||||||
|
|
||||||
Before we can build our project, we need to add an additional file to tell cargo to use the nightly toolchain. Copy the `rust-toolchain.toml` from the embassy repo to ours, and trim the list of targets down to only the target triple relevent for our project — in this case, `thumbv7em-none-eabi`:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
stm32g474-example
|
|
||||||
├── .cargo
|
|
||||||
│ └── config.toml
|
|
||||||
├── Cargo.toml
|
|
||||||
├── rust-toolchain.toml
|
|
||||||
└── src
|
|
||||||
└── main.rs
|
|
||||||
----
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
# Before upgrading check that everything is available on all tier1 targets here:
|
|
||||||
# https://rust-lang.github.io/rustup-components-history
|
|
||||||
[toolchain]
|
|
||||||
channel = "nightly-2023-11-01"
|
|
||||||
components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ]
|
|
||||||
targets = ["thumbv7em-none-eabi"]
|
|
||||||
----
|
|
||||||
|
|
||||||
== build.rs
|
|
||||||
|
|
||||||
In order to produce a working binary for our target, cargo requires a custom build script. Copy `build.rs` from the example to our project:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
stm32g474-example
|
|
||||||
├── build.rs
|
|
||||||
├── .cargo
|
|
||||||
│ └── config.toml
|
|
||||||
├── Cargo.toml
|
|
||||||
├── rust-toolchain.toml
|
|
||||||
└── src
|
|
||||||
└── main.rs
|
|
||||||
----
|
|
||||||
|
|
||||||
== Building and running
|
|
||||||
|
|
||||||
At this point, we‘re finally ready to build and run our project! Connect your board via a debug probe and run:
|
|
||||||
|
|
||||||
[source,bash]
|
|
||||||
----
|
|
||||||
cargo run --release
|
|
||||||
----
|
|
||||||
|
|
||||||
should result in a blinking LED (if there’s one attached to the pin in `src/main.rs` – change it if not!) and the following output:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
Compiling stm32g474-example v0.1.0 (/home/you/stm32g474-example)
|
|
||||||
Finished release [optimized + debuginfo] target(s) in 0.22s
|
|
||||||
Running `probe-rs run --chip STM32G474RETx target/thumbv7em-none-eabi/release/stm32g474-example`
|
|
||||||
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
|
|
||||||
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
|
|
||||||
0.000000 INFO Hello World!
|
|
||||||
└─ 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
|
|
||||||
0.300201 INFO low
|
|
||||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
|
|
||||||
----
|
|
@ -1,80 +0,0 @@
|
|||||||
= Project Structure
|
|
||||||
|
|
||||||
There are many ways to configure embassy and its components for your exact application. The link:https://github.com/embassy-rs/embassy/tree/main/examples[examples] directory for each chipset demonstrates how your project structure should look. Let's break it down:
|
|
||||||
|
|
||||||
The toplevel file structure of your project should look like this:
|
|
||||||
[source,plain]
|
|
||||||
----
|
|
||||||
{} = Maybe
|
|
||||||
|
|
||||||
my-project
|
|
||||||
|- .cargo
|
|
||||||
| |- config.toml
|
|
||||||
|- src
|
|
||||||
| |- main.rs
|
|
||||||
|- build.rs
|
|
||||||
|- Cargo.toml
|
|
||||||
|- {memory.x}
|
|
||||||
|- rust-toolchain.toml
|
|
||||||
----
|
|
||||||
|
|
||||||
=== .cargo/config.toml
|
|
||||||
|
|
||||||
This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device.
|
|
||||||
|
|
||||||
Here is a minimal example:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[target.thumbv6m-none-eabi] # <-change for your platform
|
|
||||||
runner = 'probe-rs run --chip STM32F031K6Tx' # <- change for your chip
|
|
||||||
|
|
||||||
[build]
|
|
||||||
target = "thumbv6m-none-eabi" # <-change for your platform
|
|
||||||
|
|
||||||
[env]
|
|
||||||
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 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.
|
|
||||||
|
|
||||||
TODO: someone should exhaustively describe every feature for every component!
|
|
||||||
|
|
||||||
=== memory.x
|
|
||||||
|
|
||||||
This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x.
|
|
||||||
|
|
||||||
Here is an example for using S140 with an nRF52840:
|
|
||||||
|
|
||||||
[source,x]
|
|
||||||
----
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
|
||||||
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
|
|
||||||
FLASH : ORIGIN = 0x00027000, LENGTH = 868K
|
|
||||||
RAM : ORIGIN = 0x20020000, LENGTH = 128K
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
=== rust-toolchain.toml
|
|
||||||
|
|
||||||
This file configures the rust version and configuration to use.
|
|
||||||
|
|
||||||
A minimal example:
|
|
||||||
|
|
||||||
[source,toml]
|
|
||||||
----
|
|
||||||
[toolchain]
|
|
||||||
channel = "nightly-2023-08-19" # <- as of writing, this is the exact rust version embassy uses
|
|
||||||
components = [ "rust-src", "rustfmt" ] # <- optionally add "llvm-tools-preview" for some extra features like "cargo size"
|
|
||||||
targets = [
|
|
||||||
"thumbv6m-none-eabi" # <-change for your platform
|
|
||||||
]
|
|
||||||
----
|
|
@ -27,7 +27,7 @@ If you use the `#[embassy_executor::main]` macro in your application, it creates
|
|||||||
|
|
||||||
Interrupts are a common way for peripherals to signal completion of some operation and fits well with the async execution model. The following diagram describes a typical application flow where (1) a task is polled and is attempting to make progress. The task then (2) instructs the peripheral to perform some operation, and awaits. After some time has passed, (3) an interrupt is raised, marking the completion of the operation.
|
Interrupts are a common way for peripherals to signal completion of some operation and fits well with the async execution model. The following diagram describes a typical application flow where (1) a task is polled and is attempting to make progress. The task then (2) instructs the peripheral to perform some operation, and awaits. After some time has passed, (3) an interrupt is raised, marking the completion of the operation.
|
||||||
|
|
||||||
The peripheral HAL then (4) ensures that interrupt signals are routed to the peripheral and updating the peripheral state with the results of the operation. The executor is then (5) notified that the task should be polled, which it will do.
|
The peripheral HAL then (4) ensures that interrupt signals are routed to to the peripheral and updating the peripheral state with the results of the operation. The executor is then (5) notified that the task should be polled, which it will do.
|
||||||
|
|
||||||
image::embassy_irq.png[Interrupt handling]
|
image::embassy_irq.png[Interrupt handling]
|
||||||
|
|
||||||
|
@ -26,26 +26,31 @@ features = ["defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
digest = "0.10"
|
digest = "0.10"
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
ed25519-dalek = { version = "2", default_features = false, features = ["digest"], optional = true }
|
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
salty = { version = "0.3", optional = true }
|
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
||||||
signature = { version = "2.0", default-features = false }
|
signature = { version = "1.6.4", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
rand = "0.8"
|
rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version
|
||||||
futures = { version = "0.3", features = ["executor"] }
|
futures = { version = "0.3", features = ["executor"] }
|
||||||
sha1 = "0.10.5"
|
sha1 = "0.10.5"
|
||||||
critical-section = { version = "1.1.1", features = ["std"] }
|
critical-section = { version = "1.1.1", features = ["std"] }
|
||||||
ed25519-dalek = { version = "2", default_features = false, features = ["std", "rand_core", "digest"] }
|
|
||||||
|
[dev-dependencies.ed25519-dalek]
|
||||||
|
default_features = false
|
||||||
|
features = ["rand", "std", "u32_backend"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
|
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
|
||||||
ed25519-salty = ["dep:salty", "_verify"]
|
ed25519-salty = ["dep:salty", "_verify"]
|
||||||
|
|
||||||
|
nightly = ["dep:embedded-storage-async", "embassy-embedded-hal/nightly"]
|
||||||
|
|
||||||
#Internal features
|
#Internal features
|
||||||
_verify = []
|
_verify = []
|
||||||
|
@ -8,24 +8,6 @@ The bootloader can be used either as a library or be flashed directly with the d
|
|||||||
|
|
||||||
By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
|
By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader instance or via linker scripts:
|
|
||||||
|
|
||||||
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.
|
|
||||||
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. The minimum size required for this partition is the size of your application.
|
|
||||||
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition.
|
|
||||||
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped.
|
|
||||||
|
|
||||||
For any partition, the following preconditions are required:
|
|
||||||
|
|
||||||
* Partitions must be aligned on the page size.
|
|
||||||
* Partitions must be a multiple of the page size.
|
|
||||||
|
|
||||||
The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application.
|
|
||||||
|
|
||||||
For more details on the bootloader, see [the documentation](https://embassy.dev/book/dev/bootloader.html).
|
|
||||||
|
|
||||||
## Hardware support
|
## Hardware support
|
||||||
|
|
||||||
The bootloader supports different hardware in separate crates:
|
The bootloader supports different hardware in separate crates:
|
||||||
@ -34,7 +16,6 @@ The bootloader supports different hardware in separate crates:
|
|||||||
* `embassy-boot-rp` - for the RP2040 microcontrollers.
|
* `embassy-boot-rp` - for the RP2040 microcontrollers.
|
||||||
* `embassy-boot-stm32` - for the STM32 microcontrollers.
|
* `embassy-boot-stm32` - for the STM32 microcontrollers.
|
||||||
|
|
||||||
|
|
||||||
## Minimum supported Rust version (MSRV)
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
`embassy-boot` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
`embassy-boot` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||||
|
@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
|
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
|
||||||
|
|
||||||
use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||||
|
|
||||||
/// Errors returned by bootloader
|
/// Errors returned by bootloader
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
@ -135,44 +135,51 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
|||||||
/// The provided aligned_buf argument must satisfy any alignment requirements
|
/// The provided aligned_buf argument must satisfy any alignment requirements
|
||||||
/// given by the partition flashes. All flash operations will use this buffer.
|
/// given by the partition flashes. All flash operations will use this buffer.
|
||||||
///
|
///
|
||||||
/// ## SWAPPING
|
/// SWAPPING
|
||||||
///
|
///
|
||||||
/// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.
|
/// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.
|
||||||
/// The swap index contains the copy progress, as to allow continuation of the copy process on
|
/// The swap index contains the copy progress, as to allow continuation of the copy process on
|
||||||
/// power failure. The index counter is represented within 1 or more pages (depending on total
|
/// power failure. The index counter is represented within 1 or more pages (depending on total
|
||||||
/// flash size), where a page X is considered swapped if index at location (`X + WRITE_SIZE`)
|
/// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE)
|
||||||
/// contains a zero value. This ensures that index updates can be performed atomically and
|
/// contains a zero value. This ensures that index updates can be performed atomically and
|
||||||
/// avoid a situation where the wrong index value is set (page write size is "atomic").
|
/// avoid a situation where the wrong index value is set (page write size is "atomic").
|
||||||
///
|
///
|
||||||
///
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|------------|--------|--------|--------|--------|
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Active | 0 | 1 | 2 | 3 | - |
|
/// | Active | 0 | 1 | 2 | 3 | - |
|
||||||
/// | DFU | 0 | 3 | 2 | 1 | X |
|
/// | DFU | 0 | 3 | 2 | 1 | X |
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
/// The algorithm starts by copying 'backwards', and after the first step, the layout is
|
/// The algorithm starts by copying 'backwards', and after the first step, the layout is
|
||||||
/// as follows:
|
/// as follows:
|
||||||
///
|
///
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|------------|--------|--------|--------|--------|
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Active | 1 | 1 | 2 | 1 | - |
|
/// | Active | 1 | 1 | 2 | 1 | - |
|
||||||
/// | DFU | 1 | 3 | 2 | 1 | 3 |
|
/// | DFU | 1 | 3 | 2 | 1 | 3 |
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
/// The next iteration performs the same steps
|
/// The next iteration performs the same steps
|
||||||
///
|
///
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|------------|--------|--------|--------|--------|
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Active | 2 | 1 | 2 | 1 | - |
|
/// | Active | 2 | 1 | 2 | 1 | - |
|
||||||
/// | DFU | 2 | 3 | 2 | 2 | 3 |
|
/// | DFU | 2 | 3 | 2 | 2 | 3 |
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
/// And again until we're done
|
/// And again until we're done
|
||||||
///
|
///
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|------------|--------|--------|--------|--------|
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
/// | Active | 3 | 3 | 2 | 1 | - |
|
/// | Active | 3 | 3 | 2 | 1 | - |
|
||||||
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
||||||
|
/// +-----------+------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
/// ## REVERTING
|
/// REVERTING
|
||||||
///
|
///
|
||||||
/// The reverting algorithm uses the swap index to discover that images were swapped, but that
|
/// The reverting algorithm uses the swap index to discover that images were swapped, but that
|
||||||
/// the application failed to mark the boot successful. In this case, the revert algorithm will
|
/// the application failed to mark the boot successful. In this case, the revert algorithm will
|
||||||
@ -183,21 +190,28 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
|||||||
///
|
///
|
||||||
/// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.
|
/// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.
|
||||||
///
|
///
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|--------------|--------|--------|--------|--------|
|
//*/
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
/// | Active | 3 | 1 | 2 | 1 | - |
|
/// | Active | 3 | 1 | 2 | 1 | - |
|
||||||
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|--------------|--------|--------|--------|--------|
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
/// | Active | 3 | 1 | 2 | 1 | - |
|
/// | Active | 3 | 1 | 2 | 1 | - |
|
||||||
/// | DFU | 3 | 3 | 2 | 2 | 3 |
|
/// | DFU | 3 | 3 | 2 | 2 | 3 |
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||||
/// |-----------|--------------|--------|--------|--------|--------|
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
/// | Active | 3 | 1 | 2 | 3 | - |
|
/// | Active | 3 | 1 | 2 | 3 | - |
|
||||||
/// | DFU | 3 | 3 | 2 | 1 | 3 |
|
/// | DFU | 3 | 3 | 2 | 1 | 3 |
|
||||||
|
/// +-----------+--------------+--------+--------+--------+--------+
|
||||||
///
|
///
|
||||||
pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
|
pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
|
||||||
// Ensure we have enough progress pages to store copy progress
|
// Ensure we have enough progress pages to store copy progress
|
||||||
@ -210,7 +224,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
|||||||
assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);
|
assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);
|
||||||
assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);
|
assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);
|
||||||
|
|
||||||
// Ensure our partitions are able to handle boot operations
|
|
||||||
assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE);
|
assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE);
|
||||||
|
|
||||||
// Copy contents from partition N to active
|
// Copy contents from partition N to active
|
||||||
@ -371,8 +384,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
|||||||
|
|
||||||
if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
|
if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
|
||||||
Ok(State::Swap)
|
Ok(State::Swap)
|
||||||
} else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) {
|
|
||||||
Ok(State::DfuDetach)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(State::Boot)
|
Ok(State::Boot)
|
||||||
}
|
}
|
||||||
@ -387,7 +398,6 @@ fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
|
|||||||
) {
|
) {
|
||||||
assert_eq!(active.capacity() as u32 % page_size, 0);
|
assert_eq!(active.capacity() as u32 % page_size, 0);
|
||||||
assert_eq!(dfu.capacity() as u32 % page_size, 0);
|
assert_eq!(dfu.capacity() as u32 % page_size, 0);
|
||||||
// DFU partition has to be bigger than ACTIVE partition to handle swap algorithm
|
|
||||||
assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);
|
assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);
|
||||||
assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);
|
assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use digest::typenum::U64;
|
use digest::typenum::U64;
|
||||||
use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
|
use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
|
||||||
use ed25519_dalek::Digest;
|
use ed25519_dalek::Digest as _;
|
||||||
|
|
||||||
pub struct Sha512(ed25519_dalek::Sha512);
|
pub struct Sha512(ed25519_dalek::Sha512);
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ impl Default for Sha512 {
|
|||||||
|
|
||||||
impl Update for Sha512 {
|
impl Update for Sha512 {
|
||||||
fn update(&mut self, data: &[u8]) {
|
fn update(&mut self, data: &[u8]) {
|
||||||
Digest::update(&mut self.0, data)
|
self.0.update(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|||||||
use embedded_storage_async::nor_flash::NorFlash;
|
use embedded_storage_async::nor_flash::NorFlash;
|
||||||
|
|
||||||
use super::FirmwareUpdaterConfig;
|
use super::FirmwareUpdaterConfig;
|
||||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||||
|
|
||||||
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
@ -79,8 +79,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
#[cfg(feature = "_verify")]
|
#[cfg(feature = "_verify")]
|
||||||
pub async fn verify_and_mark_updated(
|
pub async fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8; 32],
|
_public_key: &[u8],
|
||||||
_signature: &[u8; 64],
|
_signature: &[u8],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
@ -89,14 +89,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[cfg(feature = "ed25519-dalek")]
|
||||||
{
|
{
|
||||||
use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};
|
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
|
||||||
|
|
||||||
use crate::digest_adapters::ed25519_dalek::Sha512;
|
use crate::digest_adapters::ed25519_dalek::Sha512;
|
||||||
|
|
||||||
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
||||||
|
|
||||||
let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||||
let signature = Signature::from_bytes(_signature);
|
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
@ -106,6 +106,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "ed25519-salty")]
|
#[cfg(feature = "ed25519-salty")]
|
||||||
{
|
{
|
||||||
|
use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
|
||||||
use salty::{PublicKey, Signature};
|
use salty::{PublicKey, Signature};
|
||||||
|
|
||||||
use crate::digest_adapters::salty::Sha512;
|
use crate::digest_adapters::salty::Sha512;
|
||||||
@ -114,8 +115,10 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
FirmwareUpdaterError::Signature(signature::Error::default())
|
FirmwareUpdaterError::Signature(signature::Error::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;
|
let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
|
||||||
let signature = Signature::try_from(_signature).map_err(into_signature_error)?;
|
let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
|
||||||
|
let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
|
||||||
|
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
@ -158,12 +161,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
self.state.mark_updated().await
|
self.state.mark_updated().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark to trigger USB DFU on next boot.
|
|
||||||
pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
self.state.verify_booted().await?;
|
|
||||||
self.state.mark_dfu().await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.state.mark_booted().await
|
self.state.mark_booted().await
|
||||||
@ -210,16 +207,6 @@ pub struct FirmwareState<'d, STATE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||||
/// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
|
||||||
/// and written to.
|
|
||||||
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
|
||||||
Self::new(config.state, aligned)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -260,11 +247,6 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
|||||||
self.set_magic(SWAP_MAGIC).await
|
self.set_magic(SWAP_MAGIC).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark to trigger USB DFU on next boot.
|
|
||||||
pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
self.set_magic(DFU_DETACH_MAGIC).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.set_magic(BOOT_MAGIC).await
|
self.set_magic(BOOT_MAGIC).await
|
||||||
|
@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|||||||
use embedded_storage::nor_flash::NorFlash;
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
use super::FirmwareUpdaterConfig;
|
use super::FirmwareUpdaterConfig;
|
||||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||||
|
|
||||||
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
@ -86,8 +86,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
#[cfg(feature = "_verify")]
|
#[cfg(feature = "_verify")]
|
||||||
pub fn verify_and_mark_updated(
|
pub fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8; 32],
|
_public_key: &[u8],
|
||||||
_signature: &[u8; 64],
|
_signature: &[u8],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
@ -96,14 +96,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[cfg(feature = "ed25519-dalek")]
|
||||||
{
|
{
|
||||||
use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};
|
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
|
||||||
|
|
||||||
use crate::digest_adapters::ed25519_dalek::Sha512;
|
use crate::digest_adapters::ed25519_dalek::Sha512;
|
||||||
|
|
||||||
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
||||||
|
|
||||||
let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||||
let signature = Signature::from_bytes(_signature);
|
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
@ -113,6 +113,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "ed25519-salty")]
|
#[cfg(feature = "ed25519-salty")]
|
||||||
{
|
{
|
||||||
|
use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
|
||||||
use salty::{PublicKey, Signature};
|
use salty::{PublicKey, Signature};
|
||||||
|
|
||||||
use crate::digest_adapters::salty::Sha512;
|
use crate::digest_adapters::salty::Sha512;
|
||||||
@ -121,8 +122,10 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
FirmwareUpdaterError::Signature(signature::Error::default())
|
FirmwareUpdaterError::Signature(signature::Error::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;
|
let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
|
||||||
let signature = Signature::try_from(_signature).map_err(into_signature_error)?;
|
let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
|
||||||
|
let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
|
||||||
|
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
@ -165,12 +168,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
self.state.mark_updated()
|
self.state.mark_updated()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark to trigger USB DFU device on next boot.
|
|
||||||
pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
self.state.verify_booted()?;
|
|
||||||
self.state.mark_dfu()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.state.mark_booted()
|
self.state.mark_booted()
|
||||||
@ -184,12 +181,10 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
///
|
///
|
||||||
/// Failing to meet alignment and size requirements may result in a panic.
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||||
//assert!(data.len() >= DFU::ERASE_SIZE);
|
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||||
self.state.verify_booted()?;
|
self.state.verify_booted()?;
|
||||||
|
|
||||||
if offset == 0 {
|
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
|
||||||
self.dfu.erase(0, self.dfu.capacity() as u32)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dfu.write(offset as u32, data)?;
|
self.dfu.write(offset as u32, data)?;
|
||||||
|
|
||||||
@ -218,16 +213,6 @@ pub struct BlockingFirmwareState<'d, STATE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||||
/// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
|
||||||
/// and written to.
|
|
||||||
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
|
||||||
Self::new(config.state, aligned)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -241,7 +226,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
|||||||
|
|
||||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||||
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach {
|
if self.get_state()? == State::Boot {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(FirmwareUpdaterError::BadState)
|
Err(FirmwareUpdaterError::BadState)
|
||||||
@ -258,8 +243,6 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
|||||||
|
|
||||||
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||||
Ok(State::Swap)
|
Ok(State::Swap)
|
||||||
} else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) {
|
|
||||||
Ok(State::DfuDetach)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(State::Boot)
|
Ok(State::Boot)
|
||||||
}
|
}
|
||||||
@ -270,11 +253,6 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
|||||||
self.set_magic(SWAP_MAGIC)
|
self.set_magic(SWAP_MAGIC)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark to trigger USB DFU on next boot.
|
|
||||||
pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
self.set_magic(DFU_DETACH_MAGIC)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.set_magic(BOOT_MAGIC)
|
self.set_magic(BOOT_MAGIC)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
#[cfg(feature = "nightly")]
|
||||||
mod asynch;
|
mod asynch;
|
||||||
mod blocking;
|
mod blocking;
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub use asynch::{FirmwareState, FirmwareUpdater};
|
pub use asynch::{FirmwareState, FirmwareUpdater};
|
||||||
pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};
|
pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};
|
||||||
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(async_fn_in_trait)]
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
@ -17,13 +18,13 @@ mod test_flash;
|
|||||||
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
||||||
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
||||||
pub use firmware_updater::{
|
pub use firmware_updater::{
|
||||||
BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig,
|
BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError,
|
||||||
FirmwareUpdaterError,
|
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use firmware_updater::{FirmwareState, FirmwareUpdater};
|
||||||
|
|
||||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
||||||
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
||||||
pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;
|
|
||||||
|
|
||||||
/// The state of the bootloader after running prepare.
|
/// The state of the bootloader after running prepare.
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
@ -33,8 +34,6 @@ pub enum State {
|
|||||||
Boot,
|
Boot,
|
||||||
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
|
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
|
||||||
Swap,
|
Swap,
|
||||||
/// Application has received a request to reboot into DFU mode to apply an update.
|
|
||||||
DfuDetach,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
|
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
|
||||||
@ -58,6 +57,7 @@ mod tests {
|
|||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
|
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
|
|
||||||
@ -65,7 +65,9 @@ mod tests {
|
|||||||
use crate::boot_loader::BootLoaderConfig;
|
use crate::boot_loader::BootLoaderConfig;
|
||||||
use crate::firmware_updater::FirmwareUpdaterConfig;
|
use crate::firmware_updater::FirmwareUpdaterConfig;
|
||||||
use crate::mem_flash::MemFlash;
|
use crate::mem_flash::MemFlash;
|
||||||
use crate::test_flash::{AsyncTestFlash, BlockingTestFlash};
|
#[cfg(feature = "nightly")]
|
||||||
|
use crate::test_flash::AsyncTestFlash;
|
||||||
|
use crate::test_flash::BlockingTestFlash;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
@ -103,7 +105,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "_verify"))]
|
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||||
fn test_swap_state() {
|
fn test_swap_state() {
|
||||||
const FIRMWARE_SIZE: usize = 57344;
|
const FIRMWARE_SIZE: usize = 57344;
|
||||||
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
||||||
@ -181,7 +183,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "_verify"))]
|
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||||
fn test_swap_state_active_page_biggest() {
|
fn test_swap_state_active_page_biggest() {
|
||||||
const FIRMWARE_SIZE: usize = 12288;
|
const FIRMWARE_SIZE: usize = 12288;
|
||||||
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
||||||
@ -226,7 +228,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "_verify"))]
|
#[cfg(all(feature = "nightly", not(feature = "_verify")))]
|
||||||
fn test_swap_state_dfu_page_biggest() {
|
fn test_swap_state_dfu_page_biggest() {
|
||||||
const FIRMWARE_SIZE: usize = 12288;
|
const FIRMWARE_SIZE: usize = 12288;
|
||||||
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
let flash = AsyncTestFlash::new(BootLoaderConfig {
|
||||||
@ -270,24 +272,26 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "_verify")]
|
#[cfg(all(feature = "nightly", feature = "_verify"))]
|
||||||
fn test_verify() {
|
fn test_verify() {
|
||||||
// The following key setup is based on:
|
// The following key setup is based on:
|
||||||
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
||||||
|
|
||||||
use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey};
|
use ed25519_dalek::Keypair;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
let mut csprng = OsRng {};
|
let mut csprng = OsRng {};
|
||||||
let keypair = SigningKey::generate(&mut csprng);
|
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||||
|
|
||||||
|
use ed25519_dalek::{Digest, Sha512, Signature, Signer};
|
||||||
let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
|
let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
|
||||||
let mut digest = Sha512::new();
|
let mut digest = Sha512::new();
|
||||||
digest.update(&firmware);
|
digest.update(&firmware);
|
||||||
let message = digest.finalize();
|
let message = digest.finalize();
|
||||||
let signature: Signature = keypair.sign(&message);
|
let signature: Signature = keypair.sign(&message);
|
||||||
|
|
||||||
let public_key = keypair.verifying_key();
|
use ed25519_dalek::PublicKey;
|
||||||
|
let public_key: PublicKey = keypair.public;
|
||||||
|
|
||||||
// Setup flash
|
// Setup flash
|
||||||
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use core::ops::{Bound, Range, RangeBounds};
|
use core::ops::{Bound, Range, RangeBounds};
|
||||||
|
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||||
|
|
||||||
pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
|
pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
|
||||||
@ -140,6 +141,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFla
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
|
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
|
||||||
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
||||||
{
|
{
|
||||||
@ -154,6 +156,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
|
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
|
||||||
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
||||||
{
|
{
|
||||||
|
@ -51,6 +51,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE>
|
impl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE>
|
||||||
where
|
where
|
||||||
ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash,
|
ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
#[cfg(feature = "nightly")]
|
||||||
mod asynch;
|
mod asynch;
|
||||||
mod blocking;
|
mod blocking;
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub(crate) use asynch::AsyncTestFlash;
|
pub(crate) use asynch::AsyncTestFlash;
|
||||||
pub(crate) use blocking::BlockingTestFlash;
|
pub(crate) use blocking::BlockingTestFlash;
|
||||||
|
@ -4,12 +4,6 @@ name = "embassy-boot-nrf"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Bootloader lib for nRF chips"
|
description = "Bootloader lib for nRF chips"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
|
||||||
categories = [
|
|
||||||
"embedded",
|
|
||||||
"no-std",
|
|
||||||
"asynchronous",
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"
|
||||||
@ -22,16 +16,16 @@ target = "thumbv7em-none-eabi"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-nrf = { path = "../../embassy-nrf" }
|
embassy-nrf = { path = "../../embassy-nrf" }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
cortex-m-rt = { version = "0.7" }
|
cortex-m-rt = { version = "0.7" }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
||||||
nrf-softdevice-mbr = { version = "0.2.0", optional = true }
|
nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
defmt = [
|
defmt = [
|
||||||
@ -42,3 +36,8 @@ defmt = [
|
|||||||
softdevice = [
|
softdevice = [
|
||||||
"nrf-softdevice-mbr",
|
"nrf-softdevice-mbr",
|
||||||
]
|
]
|
||||||
|
nightly = [
|
||||||
|
"dep:embedded-storage-async",
|
||||||
|
"embassy-boot/nightly",
|
||||||
|
"embassy-nrf/nightly"
|
||||||
|
]
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
pub use embassy_boot::{
|
pub use embassy_boot::{
|
||||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig,
|
||||||
FirmwareUpdaterConfig,
|
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
use embassy_nrf::nvmc::PAGE_SIZE;
|
use embassy_nrf::nvmc::PAGE_SIZE;
|
||||||
use embassy_nrf::peripherals::WDT;
|
use embassy_nrf::peripherals::WDT;
|
||||||
use embassy_nrf::wdt;
|
use embassy_nrf::wdt;
|
||||||
|
@ -4,12 +4,6 @@ name = "embassy-boot-rp"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Bootloader lib for RP2040 chips"
|
description = "Bootloader lib for RP2040 chips"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
|
||||||
categories = [
|
|
||||||
"embedded",
|
|
||||||
"no-std",
|
|
||||||
"asynchronous",
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/"
|
||||||
@ -23,15 +17,15 @@ defmt = { version = "0.3", optional = true }
|
|||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
|
||||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
embassy-time = { path = "../../embassy-time" }
|
embassy-time = { path = "../../embassy-time" }
|
||||||
|
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
cortex-m-rt = { version = "0.7" }
|
cortex-m-rt = { version = "0.7" }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -46,6 +40,12 @@ log = [
|
|||||||
"embassy-rp/log",
|
"embassy-rp/log",
|
||||||
]
|
]
|
||||||
debug = ["defmt-rtt"]
|
debug = ["defmt-rtt"]
|
||||||
|
nightly = [
|
||||||
|
"dep:embedded-storage-async",
|
||||||
|
"embassy-boot/nightly",
|
||||||
|
"embassy-rp/nightly",
|
||||||
|
"embassy-time/nightly"
|
||||||
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
debug = 2
|
debug = 2
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
pub use embassy_boot::{
|
pub use embassy_boot::{
|
||||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
|
||||||
FirmwareUpdaterConfig, State,
|
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
|
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
|
||||||
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
||||||
use embassy_rp::watchdog::Watchdog;
|
use embassy_rp::watchdog::Watchdog;
|
||||||
|
@ -4,12 +4,6 @@ name = "embassy-boot-stm32"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Bootloader lib for STM32 chips"
|
description = "Bootloader lib for STM32 chips"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
|
||||||
categories = [
|
|
||||||
"embedded",
|
|
||||||
"no-std",
|
|
||||||
"asynchronous",
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"
|
||||||
@ -24,19 +18,32 @@ defmt = { version = "0.3", optional = true }
|
|||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
|
||||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
cortex-m-rt = { version = "0.7" }
|
cortex-m-rt = { version = "0.7" }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
defmt = ["dep:defmt", "embassy-boot/defmt", "embassy-stm32/defmt"]
|
defmt = [
|
||||||
log = ["dep:log", "embassy-boot/log", "embassy-stm32/log"]
|
"dep:defmt",
|
||||||
|
"embassy-boot/defmt",
|
||||||
|
"embassy-stm32/defmt",
|
||||||
|
]
|
||||||
|
log = [
|
||||||
|
"dep:log",
|
||||||
|
"embassy-boot/log",
|
||||||
|
"embassy-stm32/log",
|
||||||
|
]
|
||||||
debug = ["defmt-rtt"]
|
debug = ["defmt-rtt"]
|
||||||
|
nightly = [
|
||||||
|
"dep:embedded-storage-async",
|
||||||
|
"embassy-boot/nightly",
|
||||||
|
"embassy-stm32/nightly"
|
||||||
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
debug = 2
|
debug = 2
|
||||||
|
@ -4,16 +4,14 @@
|
|||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
pub use embassy_boot::{
|
pub use embassy_boot::{
|
||||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
|
||||||
FirmwareUpdaterConfig, State,
|
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
use embedded_storage::nor_flash::NorFlash;
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
/// A bootloader for STM32 devices.
|
/// A bootloader for STM32 devices.
|
||||||
pub struct BootLoader {
|
pub struct BootLoader;
|
||||||
/// The reported state of the bootloader after preparing for boot
|
|
||||||
pub state: State,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BootLoader {
|
impl BootLoader {
|
||||||
/// 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
|
||||||
@ -22,8 +20,8 @@ impl BootLoader {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||||
let mut boot = embassy_boot::BootLoader::new(config);
|
let mut boot = embassy_boot::BootLoader::new(config);
|
||||||
let state = boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
||||||
Self { state }
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boots the application.
|
/// Boots the application.
|
||||||
|
@ -8,25 +8,27 @@ license = "MIT OR Apache-2.0"
|
|||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/"
|
||||||
features = ["std"]
|
features = ["nightly", "std"]
|
||||||
target = "x86_64-unknown-linux-gnu"
|
target = "x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = []
|
std = []
|
||||||
|
# Enable nightly-only features
|
||||||
|
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
||||||
time = ["dep:embassy-time"]
|
time = ["dep:embassy-time"]
|
||||||
default = ["time"]
|
default = ["time"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
||||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.2", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
|
|
||||||
let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
|
|
||||||
|
|
||||||
let output = Command::new(rustc)
|
|
||||||
.arg("--version")
|
|
||||||
.output()
|
|
||||||
.expect("failed to run `rustc --version`");
|
|
||||||
|
|
||||||
if String::from_utf8_lossy(&output.stdout).contains("nightly") {
|
|
||||||
println!("cargo:rustc-cfg=nightly");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||||
|
|
||||||
/// Convenience helper for concatenating two consecutive flashes into one.
|
/// Convenience helper for concatenating two consecutive flashes into one.
|
||||||
@ -116,6 +117,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>
|
impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>
|
||||||
where
|
where
|
||||||
First: AsyncReadNorFlash<Error = E>,
|
First: AsyncReadNorFlash<Error = E>,
|
||||||
@ -144,6 +146,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>
|
impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>
|
||||||
where
|
where
|
||||||
First: AsyncNorFlash<Error = E>,
|
First: AsyncNorFlash<Error = E>,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@ -92,6 +93,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFla
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
|
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
|
||||||
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
||||||
{
|
{
|
||||||
@ -107,6 +109,7 @@ impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
|
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
|
||||||
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
|
||||||
{
|
{
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
mod asynch;
|
mod asynch;
|
||||||
mod blocking;
|
mod blocking;
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub use asynch::Partition;
|
pub use asynch::Partition;
|
||||||
pub use blocking::BlockingPartition;
|
pub use blocking::BlockingPartition;
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
||||||
#![cfg_attr(nightly, allow(stable_features, unknown_lints))]
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![allow(async_fn_in_trait)]
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub mod adapter;
|
pub mod adapter;
|
||||||
|
|
||||||
pub mod flash;
|
pub mod flash;
|
||||||
|
|
||||||
pub mod shared_bus;
|
pub mod shared_bus;
|
||||||
|
|
||||||
/// Set the configuration of a peripheral driver.
|
/// Set the configuration of a peripheral driver.
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
//!
|
//!
|
||||||
//! # Example (nrf52)
|
//! # Example (nrf52)
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust
|
||||||
//! use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
|
//! use embassy_embedded_hal::shared_bus::i2c::I2cDevice;
|
||||||
//! use embassy_sync::mutex::Mutex;
|
//! use embassy_sync::mutex::Mutex;
|
||||||
//! use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
//! use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||||
//!
|
//!
|
||||||
//! static I2C_BUS: StaticCell<Mutex<NoopRawMutex, Twim<TWISPI0>>> = StaticCell::new();
|
//! static I2C_BUS: StaticCell<Mutex::<ThreadModeRawMutex, Twim<TWISPI0>>> = StaticCell::new();
|
||||||
//! let config = twim::Config::default();
|
//! let config = twim::Config::default();
|
||||||
//! let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config);
|
//! let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
//! let i2c_bus = Mutex::new(i2c);
|
//! let i2c = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
|
||||||
|
//! let i2c_bus = Mutex::<ThreadModeRawMutex, _>::new(i2c);
|
||||||
//! let i2c_bus = I2C_BUS.init(i2c_bus);
|
//! let i2c_bus = I2C_BUS.init(i2c_bus);
|
||||||
//!
|
//!
|
||||||
//! // Device 1, using embedded-hal-async compatible driver for QMC5883L compass
|
//! // Device 1, using embedded-hal-async compatible driver for QMC5883L compass
|
||||||
|
@ -2,16 +2,17 @@
|
|||||||
//!
|
//!
|
||||||
//! # Example (nrf52)
|
//! # Example (nrf52)
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust
|
||||||
//! use embassy_embedded_hal::shared_bus::spi::SpiDevice;
|
//! use embassy_embedded_hal::shared_bus::spi::SpiDevice;
|
||||||
//! use embassy_sync::mutex::Mutex;
|
//! use embassy_sync::mutex::Mutex;
|
||||||
//! use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
//! use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||||
//!
|
//!
|
||||||
//! static SPI_BUS: StaticCell<Mutex<NoopRawMutex, spim::Spim<SPI3>>> = StaticCell::new();
|
//! static SPI_BUS: StaticCell<Mutex<ThreadModeRawMutex, spim::Spim<SPI3>>> = StaticCell::new();
|
||||||
//! let mut config = spim::Config::default();
|
//! let mut config = spim::Config::default();
|
||||||
//! config.frequency = spim::Frequency::M32;
|
//! config.frequency = spim::Frequency::M32;
|
||||||
//! let spi = spim::Spim::new_txonly(p.SPI3, Irqs, p.P0_15, p.P0_18, config);
|
//! let irq = interrupt::take!(SPIM3);
|
||||||
//! let spi_bus = Mutex::new(spi);
|
//! let spi = spim::Spim::new_txonly(p.SPI3, irq, p.P0_15, p.P0_18, config);
|
||||||
|
//! let spi_bus = Mutex::<ThreadModeRawMutex, _>::new(spi);
|
||||||
//! let spi_bus = SPI_BUS.init(spi_bus);
|
//! let spi_bus = SPI_BUS.init(spi_bus);
|
||||||
//!
|
//!
|
||||||
//! // Device 1, using embedded-hal-async compatible driver for ST7735 LCD display
|
//! // Device 1, using embedded-hal-async compatible driver for ST7735 LCD display
|
||||||
@ -62,36 +63,22 @@ where
|
|||||||
CS: OutputPin,
|
CS: OutputPin,
|
||||||
{
|
{
|
||||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
|
||||||
return Err(SpiDeviceError::DelayNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res = 'ops: {
|
let op_res: Result<(), BUS::Error> = try {
|
||||||
for op in operations {
|
for op in operations {
|
||||||
let res = match op {
|
match op {
|
||||||
Operation::Read(buf) => bus.read(buf).await,
|
Operation::Read(buf) => bus.read(buf).await?,
|
||||||
Operation::Write(buf) => bus.write(buf).await,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayNs(_) => unreachable!(),
|
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayNs(ns) => match bus.flush().await {
|
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
||||||
Err(e) => Err(e),
|
|
||||||
Ok(()) => {
|
|
||||||
embassy_time::Timer::after_nanos(*ns as _).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if let Err(e) = res {
|
|
||||||
break 'ops Err(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
@ -140,37 +127,23 @@ where
|
|||||||
CS: OutputPin,
|
CS: OutputPin,
|
||||||
{
|
{
|
||||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
|
||||||
return Err(SpiDeviceError::DelayNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res = 'ops: {
|
let op_res: Result<(), BUS::Error> = try {
|
||||||
for op in operations {
|
for op in operations {
|
||||||
let res = match op {
|
match op {
|
||||||
Operation::Read(buf) => bus.read(buf).await,
|
Operation::Read(buf) => bus.read(buf).await?,
|
||||||
Operation::Write(buf) => bus.write(buf).await,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayNs(_) => unreachable!(),
|
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayNs(ns) => match bus.flush().await {
|
Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await,
|
||||||
Err(e) => Err(e),
|
|
||||||
Ok(()) => {
|
|
||||||
embassy_time::Timer::after_nanos(*ns as _).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if let Err(e) = res {
|
|
||||||
break 'ops Err(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
|
@ -55,10 +55,6 @@ where
|
|||||||
CS: OutputPin,
|
CS: OutputPin,
|
||||||
{
|
{
|
||||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
|
||||||
return Err(SpiDeviceError::DelayNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
@ -69,10 +65,10 @@ where
|
|||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayNs(_) => unreachable!(),
|
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayNs(ns) => {
|
Operation::DelayUs(us) => {
|
||||||
embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _));
|
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -165,10 +161,6 @@ where
|
|||||||
CS: OutputPin,
|
CS: OutputPin,
|
||||||
{
|
{
|
||||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
if cfg!(not(feature = "time")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {
|
|
||||||
return Err(SpiDeviceError::DelayNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
@ -180,10 +172,10 @@ where
|
|||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||||
#[cfg(not(feature = "time"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayNs(_) => unreachable!(),
|
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayNs(ns) => {
|
Operation::DelayUs(us) => {
|
||||||
embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _));
|
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,9 @@ use core::fmt::Debug;
|
|||||||
|
|
||||||
use embedded_hal_1::{i2c, spi};
|
use embedded_hal_1::{i2c, spi};
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub mod asynch;
|
pub mod asynch;
|
||||||
|
|
||||||
pub mod blocking;
|
pub mod blocking;
|
||||||
|
|
||||||
/// Error returned by I2C device implementations in this crate.
|
/// Error returned by I2C device implementations in this crate.
|
||||||
@ -37,8 +39,8 @@ pub enum SpiDeviceError<BUS, CS> {
|
|||||||
Spi(BUS),
|
Spi(BUS),
|
||||||
/// Setting the value of the Chip Select (CS) pin failed.
|
/// Setting the value of the Chip Select (CS) pin failed.
|
||||||
Cs(CS),
|
Cs(CS),
|
||||||
/// Delay operations are not supported when the `time` Cargo feature is not enabled.
|
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||||
DelayNotSupported,
|
DelayUsNotSupported,
|
||||||
/// The SPI bus could not be configured.
|
/// The SPI bus could not be configured.
|
||||||
Config,
|
Config,
|
||||||
}
|
}
|
||||||
@ -52,7 +54,7 @@ where
|
|||||||
match self {
|
match self {
|
||||||
Self::Spi(e) => e.kind(),
|
Self::Spi(e) => e.kind(),
|
||||||
Self::Cs(_) => spi::ErrorKind::Other,
|
Self::Cs(_) => spi::ErrorKind::Other,
|
||||||
Self::DelayNotSupported => spi::ErrorKind::Other,
|
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||||
Self::Config => spi::ErrorKind::Other,
|
Self::Config => spi::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
# embassy-executor-macros
|
|
||||||
|
|
||||||
An [Embassy](https://embassy.dev) project.
|
|
||||||
|
|
||||||
NOTE: Do not use this crate directly. The macros are re-exported by `embassy-executor`.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This work is licensed under either of
|
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
||||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
|
||||||
|
|
||||||
at your option.
|
|
@ -5,18 +5,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
|
|
||||||
## 0.4.0 - 2023-12-05
|
|
||||||
|
|
||||||
- Removed `arch-xtensa`. Use the executor provided by the HAL crate (`esp-hal`, `esp32s3-hal`, etc...) instead.
|
|
||||||
- Added an arena allocator for tasks, allowing using the `main` and `task` macros on Rust 1.75 stable. (it is only used if the `nightly` feature is not enabled. When `nightly` is enabled, `type_alias_impl_trait` is used to statically allocate tasks, as before).
|
|
||||||
|
|
||||||
## 0.3.3 - 2023-11-15
|
|
||||||
|
|
||||||
- Add `main` macro reexport for Xtensa arch.
|
|
||||||
- Remove use of `atomic-polyfill`. The executor now has multiple implementations of its internal data structures for cases where the target supports atomics or doesn't.
|
|
||||||
|
|
||||||
## 0.3.2 - 2023-11-06
|
## 0.3.2 - 2023-11-06
|
||||||
|
|
||||||
- Use `atomic-polyfill` for `riscv32`
|
- Use `atomic-polyfill` for `riscv32`
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.4.0"
|
version = "0.3.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "async/await executor designed for embedded usage"
|
description = "async/await executor designed for embedded usage"
|
||||||
@ -14,7 +14,7 @@ categories = [
|
|||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
|
||||||
features = ["defmt"]
|
features = ["nightly", "defmt"]
|
||||||
flavors = [
|
flavors = [
|
||||||
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
|
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
|
||||||
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
|
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
|
||||||
@ -25,31 +25,7 @@ flavors = [
|
|||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
default-target = "thumbv7em-none-eabi"
|
default-target = "thumbv7em-none-eabi"
|
||||||
targets = ["thumbv7em-none-eabi"]
|
targets = ["thumbv7em-none-eabi"]
|
||||||
features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
defmt = { version = "0.3", optional = true }
|
|
||||||
log = { version = "0.4.14", optional = true }
|
|
||||||
rtos-trace = { version = "0.1.2", optional = true }
|
|
||||||
|
|
||||||
embassy-executor-macros = { version = "0.4.0", path = "../embassy-executor-macros" }
|
|
||||||
embassy-time = { version = "0.2", path = "../embassy-time", optional = true}
|
|
||||||
critical-section = "1.1"
|
|
||||||
|
|
||||||
# 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
|
|
||||||
cortex-m = { version = "0.7.6", optional = true }
|
|
||||||
|
|
||||||
# arch-wasm dependencies
|
|
||||||
wasm-bindgen = { version = "0.2.82", optional = true }
|
|
||||||
js-sys = { version = "0.3", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
critical-section = { version = "1.1", features = ["std"] }
|
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
||||||
@ -57,7 +33,8 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
_arch = [] # some arch was picked
|
_arch = [] # some arch was picked
|
||||||
arch-std = ["_arch", "critical-section/std"]
|
arch-std = ["_arch", "critical-section/std"]
|
||||||
arch-cortex-m = ["_arch", "dep:cortex-m"]
|
arch-cortex-m = ["_arch", "dep:cortex-m"]
|
||||||
arch-riscv32 = ["_arch", "dep:portable-atomic"]
|
arch-xtensa = ["_arch"]
|
||||||
|
arch-riscv32 = ["_arch"]
|
||||||
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
||||||
|
|
||||||
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
||||||
@ -66,53 +43,28 @@ executor-thread = []
|
|||||||
executor-interrupt = []
|
executor-interrupt = []
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy-executor-macros/nightly"]
|
nightly = []
|
||||||
|
|
||||||
turbowakers = []
|
turbowakers = []
|
||||||
|
|
||||||
integrated-timers = ["dep:embassy-time"]
|
integrated-timers = ["dep:embassy-time"]
|
||||||
|
|
||||||
# BEGIN AUTOGENERATED CONFIG FEATURES
|
# Trace interrupt invocations with rtos-trace.
|
||||||
# Generated by gen_config.py. DO NOT EDIT.
|
rtos-trace-interrupt = ["rtos-trace", "embassy-macros/rtos-trace-interrupt"]
|
||||||
task-arena-size-64 = []
|
|
||||||
task-arena-size-128 = []
|
|
||||||
task-arena-size-192 = []
|
|
||||||
task-arena-size-256 = []
|
|
||||||
task-arena-size-320 = []
|
|
||||||
task-arena-size-384 = []
|
|
||||||
task-arena-size-512 = []
|
|
||||||
task-arena-size-640 = []
|
|
||||||
task-arena-size-768 = []
|
|
||||||
task-arena-size-1024 = []
|
|
||||||
task-arena-size-1280 = []
|
|
||||||
task-arena-size-1536 = []
|
|
||||||
task-arena-size-2048 = []
|
|
||||||
task-arena-size-2560 = []
|
|
||||||
task-arena-size-3072 = []
|
|
||||||
task-arena-size-4096 = [] # Default
|
|
||||||
task-arena-size-5120 = []
|
|
||||||
task-arena-size-6144 = []
|
|
||||||
task-arena-size-8192 = []
|
|
||||||
task-arena-size-10240 = []
|
|
||||||
task-arena-size-12288 = []
|
|
||||||
task-arena-size-16384 = []
|
|
||||||
task-arena-size-20480 = []
|
|
||||||
task-arena-size-24576 = []
|
|
||||||
task-arena-size-32768 = []
|
|
||||||
task-arena-size-40960 = []
|
|
||||||
task-arena-size-49152 = []
|
|
||||||
task-arena-size-65536 = []
|
|
||||||
task-arena-size-81920 = []
|
|
||||||
task-arena-size-98304 = []
|
|
||||||
task-arena-size-131072 = []
|
|
||||||
task-arena-size-163840 = []
|
|
||||||
task-arena-size-196608 = []
|
|
||||||
task-arena-size-262144 = []
|
|
||||||
task-arena-size-327680 = []
|
|
||||||
task-arena-size-393216 = []
|
|
||||||
task-arena-size-524288 = []
|
|
||||||
task-arena-size-655360 = []
|
|
||||||
task-arena-size-786432 = []
|
|
||||||
task-arena-size-1048576 = []
|
|
||||||
|
|
||||||
# END AUTOGENERATED CONFIG FEATURES
|
[dependencies]
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
log = { version = "0.4.14", optional = true }
|
||||||
|
rtos-trace = { version = "0.1.2", optional = true }
|
||||||
|
|
||||||
|
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
||||||
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
|
||||||
|
atomic-polyfill = "1.0.1"
|
||||||
|
critical-section = "1.1"
|
||||||
|
|
||||||
|
# arch-cortex-m dependencies
|
||||||
|
cortex-m = { version = "0.7.6", optional = true }
|
||||||
|
|
||||||
|
# arch-wasm dependencies
|
||||||
|
wasm-bindgen = { version = "0.2.82", optional = true }
|
||||||
|
js-sys = { version = "0.3", optional = true }
|
||||||
|
@ -2,36 +2,10 @@
|
|||||||
|
|
||||||
An async/await executor designed for embedded usage.
|
An async/await executor designed for embedded usage.
|
||||||
|
|
||||||
- No `alloc`, no heap needed.
|
- No `alloc`, no heap needed. Task futures are statically allocated.
|
||||||
- With nightly Rust, task futures can be fully statically allocated.
|
|
||||||
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
- Efficient polling: a wake will only poll the woken task, not all of them.
|
- Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
||||||
|
|
||||||
## Task arena
|
|
||||||
|
|
||||||
When the `nightly` Cargo feature is not enabled, `embassy-executor` allocates tasks out of an arena (a very simple bump allocator).
|
|
||||||
|
|
||||||
If the task arena gets full, the program will panic at runtime. To guarantee this doesn't happen, you must set the size to the sum of sizes of all tasks.
|
|
||||||
|
|
||||||
Tasks are allocated from the arena when spawned for the first time. If the task exists, the allocation is not released to the arena, but can be reused to spawn the task again. For multiple-instance tasks (like `#[embassy_executor::task(pool_size = 4)]`), the first spawn will allocate memory for all instances. This is done for performance and to increase predictability (for example, spawning at least 1 instance of every task at boot guarantees an immediate panic if the arena is too small, while allocating instances on-demand could delay the panic to only when the program is under load).
|
|
||||||
|
|
||||||
The arena size can be configured in two ways:
|
|
||||||
|
|
||||||
- Via Cargo features: enable a Cargo feature like `task-arena-size-8192`. Only a selection of values
|
|
||||||
is available, check `Cargo.toml` for the list.
|
|
||||||
- Via environment variables at build time: set the variable named `EMBASSY_EXECUTOR_TASK_ARENA_SIZE`. For example
|
|
||||||
`EMBASSY_EXECUTOR_TASK_ARENA_SIZE=4321 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
|
|
||||||
Any value can be set, unlike with Cargo features.
|
|
||||||
|
|
||||||
Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
|
|
||||||
with different values, compilation fails.
|
|
||||||
|
|
||||||
## Statically allocating tasks
|
|
||||||
|
|
||||||
When using nightly Rust, enable the `nightly` Cargo feature. This will make `embassy-executor` use the `type_alias_impl_trait` feature to allocate all tasks in `static`s. Each task gets its own `static`, with the exact size to hold the task (or multiple instances of it, if using `pool_size`) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.
|
|
||||||
|
|
||||||
The configured arena size is ignored, no arena is used at all.
|
|
||||||
|
@ -1,97 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::env;
|
||||||
use std::fmt::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{env, fs};
|
|
||||||
|
|
||||||
static CONFIGS: &[(&str, usize)] = &[
|
|
||||||
// BEGIN AUTOGENERATED CONFIG FEATURES
|
|
||||||
// Generated by gen_config.py. DO NOT EDIT.
|
|
||||||
("TASK_ARENA_SIZE", 4096),
|
|
||||||
// END AUTOGENERATED CONFIG FEATURES
|
|
||||||
];
|
|
||||||
|
|
||||||
struct ConfigState {
|
|
||||||
value: usize,
|
|
||||||
seen_feature: bool,
|
|
||||||
seen_env: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let crate_name = env::var("CARGO_PKG_NAME")
|
|
||||||
.unwrap()
|
|
||||||
.to_ascii_uppercase()
|
|
||||||
.replace('-', "_");
|
|
||||||
|
|
||||||
// only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
|
|
||||||
// other file changed.
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
|
|
||||||
// Rebuild if config envvar changed.
|
|
||||||
for (name, _) in CONFIGS {
|
|
||||||
println!("cargo:rerun-if-env-changed={crate_name}_{name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut configs = HashMap::new();
|
|
||||||
for (name, default) in CONFIGS {
|
|
||||||
configs.insert(
|
|
||||||
*name,
|
|
||||||
ConfigState {
|
|
||||||
value: *default,
|
|
||||||
seen_env: false,
|
|
||||||
seen_feature: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let prefix = format!("{crate_name}_");
|
|
||||||
for (var, value) in env::vars() {
|
|
||||||
if let Some(name) = var.strip_prefix(&prefix) {
|
|
||||||
let Some(cfg) = configs.get_mut(name) else {
|
|
||||||
panic!("Unknown env var {name}")
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(value) = value.parse::<usize>() else {
|
|
||||||
panic!("Invalid value for env var {name}: {value}")
|
|
||||||
};
|
|
||||||
|
|
||||||
cfg.value = value;
|
|
||||||
cfg.seen_env = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
|
|
||||||
if let Some(i) = feature.rfind('_') {
|
|
||||||
let name = &feature[..i];
|
|
||||||
let value = &feature[i + 1..];
|
|
||||||
if let Some(cfg) = configs.get_mut(name) {
|
|
||||||
let Ok(value) = value.parse::<usize>() else {
|
|
||||||
panic!("Invalid value for feature {name}: {value}")
|
|
||||||
};
|
|
||||||
|
|
||||||
// envvars take priority.
|
|
||||||
if !cfg.seen_env {
|
|
||||||
if cfg.seen_feature {
|
|
||||||
panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.value = value;
|
|
||||||
cfg.seen_feature = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut data = String::new();
|
|
||||||
|
|
||||||
for (name, cfg) in &configs {
|
|
||||||
writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
||||||
let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
|
|
||||||
fs::write(out_file, data).unwrap();
|
|
||||||
|
|
||||||
// cortex-m targets
|
|
||||||
let target = env::var("TARGET").unwrap();
|
let target = env::var("TARGET").unwrap();
|
||||||
|
|
||||||
if target.starts_with("thumbv6m-") {
|
if target.starts_with("thumbv6m-") {
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
abspath = os.path.abspath(__file__)
|
|
||||||
dname = os.path.dirname(abspath)
|
|
||||||
os.chdir(dname)
|
|
||||||
|
|
||||||
features = []
|
|
||||||
|
|
||||||
|
|
||||||
def feature(name, default, min=None, max=None, pow2=None, vals=None, factors=[]):
|
|
||||||
if vals is None:
|
|
||||||
assert min is not None
|
|
||||||
assert max is not None
|
|
||||||
|
|
||||||
vals = set()
|
|
||||||
val = min
|
|
||||||
while val <= max:
|
|
||||||
vals.add(val)
|
|
||||||
for f in factors:
|
|
||||||
if val * f <= max:
|
|
||||||
vals.add(val * f)
|
|
||||||
if (pow2 == True or (isinstance(pow2, int) and val >= pow2)) and val > 0:
|
|
||||||
val *= 2
|
|
||||||
else:
|
|
||||||
val += 1
|
|
||||||
vals.add(default)
|
|
||||||
vals = sorted(list(vals))
|
|
||||||
|
|
||||||
features.append(
|
|
||||||
{
|
|
||||||
"name": name,
|
|
||||||
"default": default,
|
|
||||||
"vals": vals,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
feature(
|
|
||||||
"task_arena_size", default=4096, min=64, max=1024 * 1024, pow2=True, factors=[3, 5]
|
|
||||||
)
|
|
||||||
|
|
||||||
# ========= Update Cargo.toml
|
|
||||||
|
|
||||||
things = ""
|
|
||||||
for f in features:
|
|
||||||
name = f["name"].replace("_", "-")
|
|
||||||
for val in f["vals"]:
|
|
||||||
things += f"{name}-{val} = []"
|
|
||||||
if val == f["default"]:
|
|
||||||
things += " # Default"
|
|
||||||
things += "\n"
|
|
||||||
things += "\n"
|
|
||||||
|
|
||||||
SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n"
|
|
||||||
SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n"
|
|
||||||
HELP = "# Generated by gen_config.py. DO NOT EDIT.\n"
|
|
||||||
with open("Cargo.toml", "r") as f:
|
|
||||||
data = f.read()
|
|
||||||
before, data = data.split(SEPARATOR_START, maxsplit=1)
|
|
||||||
_, after = data.split(SEPARATOR_END, maxsplit=1)
|
|
||||||
data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after
|
|
||||||
with open("Cargo.toml", "w") as f:
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
|
|
||||||
# ========= Update build.rs
|
|
||||||
|
|
||||||
things = ""
|
|
||||||
for f in features:
|
|
||||||
name = f["name"].upper()
|
|
||||||
things += f' ("{name}", {f["default"]}),\n'
|
|
||||||
|
|
||||||
SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n"
|
|
||||||
SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n"
|
|
||||||
HELP = " // Generated by gen_config.py. DO NOT EDIT.\n"
|
|
||||||
with open("build.rs", "r") as f:
|
|
||||||
data = f.read()
|
|
||||||
before, data = data.split(SEPARATOR_START, maxsplit=1)
|
|
||||||
_, after = data.split(SEPARATOR_END, maxsplit=1)
|
|
||||||
data = before + SEPARATOR_START + HELP + things + " " + SEPARATOR_END + after
|
|
||||||
with open("build.rs", "w") as f:
|
|
||||||
f.write(data)
|
|
@ -51,7 +51,8 @@ mod thread {
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
pub use embassy_executor_macros::main_cortex_m as main;
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_cortex_m as main;
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
@ -114,12 +115,12 @@ mod thread {
|
|||||||
pub use interrupt::*;
|
pub use interrupt::*;
|
||||||
#[cfg(feature = "executor-interrupt")]
|
#[cfg(feature = "executor-interrupt")]
|
||||||
mod interrupt {
|
mod interrupt {
|
||||||
use core::cell::{Cell, UnsafeCell};
|
use core::cell::UnsafeCell;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
use atomic_polyfill::{AtomicBool, Ordering};
|
||||||
use cortex_m::interrupt::InterruptNumber;
|
use cortex_m::interrupt::InterruptNumber;
|
||||||
use cortex_m::peripheral::NVIC;
|
use cortex_m::peripheral::NVIC;
|
||||||
use critical_section::Mutex;
|
|
||||||
|
|
||||||
use crate::raw;
|
use crate::raw;
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ mod interrupt {
|
|||||||
/// It is somewhat more complex to use, it's recommended to use the thread-mode
|
/// It is somewhat more complex to use, it's recommended to use the thread-mode
|
||||||
/// [`Executor`] instead, if it works for your use case.
|
/// [`Executor`] instead, if it works for your use case.
|
||||||
pub struct InterruptExecutor {
|
pub struct InterruptExecutor {
|
||||||
started: Mutex<Cell<bool>>,
|
started: AtomicBool,
|
||||||
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
|
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ mod interrupt {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
started: Mutex::new(Cell::new(false)),
|
started: AtomicBool::new(false),
|
||||||
executor: UnsafeCell::new(MaybeUninit::uninit()),
|
executor: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,8 +167,7 @@ mod interrupt {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// - You MUST call this from the interrupt handler, and from nowhere else.
|
/// You MUST call this from the interrupt handler, and from nowhere else.
|
||||||
/// - You must not call this before calling `start()`.
|
|
||||||
pub unsafe fn on_interrupt(&'static self) {
|
pub unsafe fn on_interrupt(&'static self) {
|
||||||
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
||||||
executor.poll();
|
executor.poll();
|
||||||
@ -196,7 +196,11 @@ mod interrupt {
|
|||||||
/// do it after.
|
/// do it after.
|
||||||
///
|
///
|
||||||
pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
|
pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
|
||||||
if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {
|
if self
|
||||||
|
.started
|
||||||
|
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
panic!("InterruptExecutor::start() called multiple times on the same executor.");
|
panic!("InterruptExecutor::start() called multiple times on the same executor.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,10 +222,10 @@ mod interrupt {
|
|||||||
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
|
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
|
||||||
/// executor.
|
/// executor.
|
||||||
///
|
///
|
||||||
/// This MUST only be called on an executor that has already been started.
|
/// This MUST only be called on an executor that has already been spawned.
|
||||||
/// The function will panic otherwise.
|
/// The function will panic otherwise.
|
||||||
pub fn spawner(&'static self) -> crate::SendSpawner {
|
pub fn spawner(&'static self) -> crate::SendSpawner {
|
||||||
if !critical_section::with(|cs| self.started.borrow(cs).get()) {
|
if !self.started.load(Ordering::Acquire) {
|
||||||
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
|
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
|
||||||
}
|
}
|
||||||
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
||||||
|
@ -7,8 +7,9 @@ pub use thread::*;
|
|||||||
mod thread {
|
mod thread {
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
pub use embassy_executor_macros::main_riscv as main;
|
use atomic_polyfill::{AtomicBool, Ordering};
|
||||||
use portable_atomic::{AtomicBool, Ordering};
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_riscv as main;
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ mod thread {
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::{Condvar, Mutex};
|
use std::sync::{Condvar, Mutex};
|
||||||
|
|
||||||
pub use embassy_executor_macros::main_std as main;
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_std as main;
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ mod thread {
|
|||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
pub use embassy_executor_macros::main_wasm as main;
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::main_wasm as main;
|
||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
87
embassy-executor/src/arch/xtensa.rs
Normal file
87
embassy-executor/src/arch/xtensa.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#[cfg(feature = "executor-interrupt")]
|
||||||
|
compile_error!("`executor-interrupt` is not supported with `arch-xtensa`.");
|
||||||
|
|
||||||
|
#[cfg(feature = "executor-thread")]
|
||||||
|
pub use thread::*;
|
||||||
|
#[cfg(feature = "executor-thread")]
|
||||||
|
mod thread {
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
|
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
|
||||||
|
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[export_name = "__pender"]
|
||||||
|
fn __pender(_context: *mut ()) {
|
||||||
|
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Xtensa 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 {
|
||||||
|
self.inner.poll();
|
||||||
|
|
||||||
|
// Manual critical section implementation that only masks interrupts handlers.
|
||||||
|
// We must not acquire the cross-core on dual-core systems because that would
|
||||||
|
// prevent the other core from doing useful work while this core is sleeping.
|
||||||
|
let token: critical_section::RawRestoreState;
|
||||||
|
core::arch::asm!("rsil {0}, 5", out(reg) token);
|
||||||
|
|
||||||
|
// we do not care about race conditions between the load and store operations, interrupts
|
||||||
|
// will only set this value to true.
|
||||||
|
// if there is work to do, loop back to polling
|
||||||
|
if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
|
||||||
|
SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
|
core::arch::asm!(
|
||||||
|
"wsr.ps {0}",
|
||||||
|
"rsync", in(reg) token)
|
||||||
|
} else {
|
||||||
|
// waiti sets the PS.INTLEVEL when slipping into sleep
|
||||||
|
// because critical sections in Xtensa are implemented via increasing
|
||||||
|
// PS.INTLEVEL the critical section ends here
|
||||||
|
// take care not add code after `waiti` if it needs to be inside the CS
|
||||||
|
core::arch::asm!("waiti 0"); // critical section ends here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
|
#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
|
||||||
|
#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
@ -6,7 +7,8 @@
|
|||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
|
||||||
pub use embassy_executor_macros::task;
|
#[cfg(feature = "nightly")]
|
||||||
|
pub use embassy_macros::task;
|
||||||
|
|
||||||
macro_rules! check_at_most_one {
|
macro_rules! check_at_most_one {
|
||||||
(@amo [$($feats:literal)*] [] [$($res:tt)*]) => {
|
(@amo [$($feats:literal)*] [] [$($res:tt)*]) => {
|
||||||
@ -20,11 +22,12 @@ macro_rules! check_at_most_one {
|
|||||||
check_at_most_one!(@amo [$($f)*] [$($f)*] []);
|
check_at_most_one!(@amo [$($f)*] [$($f)*] []);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",);
|
check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "arch-wasm",);
|
||||||
|
|
||||||
#[cfg(feature = "_arch")]
|
#[cfg(feature = "_arch")]
|
||||||
#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.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-riscv32", path = "arch/riscv32.rs")]
|
||||||
|
#[cfg_attr(feature = "arch-xtensa", path = "arch/xtensa.rs")]
|
||||||
#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
|
#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
|
||||||
#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
|
#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
|
||||||
mod arch;
|
mod arch;
|
||||||
@ -38,101 +41,28 @@ pub mod raw;
|
|||||||
mod spawner;
|
mod spawner;
|
||||||
pub use spawner::*;
|
pub use spawner::*;
|
||||||
|
|
||||||
mod config {
|
|
||||||
#![allow(unused)]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/config.rs"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation details for embassy macros.
|
/// Implementation details for embassy macros.
|
||||||
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
|
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(not(feature = "nightly"))]
|
|
||||||
pub mod _export {
|
pub mod _export {
|
||||||
use core::alloc::Layout;
|
#[cfg(feature = "rtos-trace")]
|
||||||
use core::cell::{Cell, UnsafeCell};
|
pub use rtos_trace::trace;
|
||||||
use core::future::Future;
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use core::ptr::null_mut;
|
|
||||||
|
|
||||||
use critical_section::{CriticalSection, Mutex};
|
/// Expands the given block of code when `embassy-executor` is compiled with
|
||||||
|
/// the `rtos-trace-interrupt` feature.
|
||||||
use crate::raw::TaskPool;
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
struct Arena<const N: usize> {
|
#[cfg(feature = "rtos-trace-interrupt")]
|
||||||
buf: UnsafeCell<MaybeUninit<[u8; N]>>,
|
macro_rules! rtos_trace_interrupt {
|
||||||
ptr: Mutex<Cell<*mut u8>>,
|
($($tt:tt)*) => { $($tt)* };
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<const N: usize> Sync for Arena<N> {}
|
/// Does not expand the given block of code when `embassy-executor` is
|
||||||
unsafe impl<const N: usize> Send for Arena<N> {}
|
/// compiled without the `rtos-trace-interrupt` feature.
|
||||||
|
#[doc(hidden)]
|
||||||
impl<const N: usize> Arena<N> {
|
#[macro_export]
|
||||||
const fn new() -> Self {
|
#[cfg(not(feature = "rtos-trace-interrupt"))]
|
||||||
Self {
|
macro_rules! rtos_trace_interrupt {
|
||||||
buf: UnsafeCell::new(MaybeUninit::uninit()),
|
($($tt:tt)*) => {};
|
||||||
ptr: Mutex::new(Cell::new(null_mut())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alloc<T>(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit<T> {
|
|
||||||
let layout = Layout::new::<T>();
|
|
||||||
|
|
||||||
let start = self.buf.get().cast::<u8>();
|
|
||||||
let end = unsafe { start.add(N) };
|
|
||||||
|
|
||||||
let mut ptr = self.ptr.borrow(cs).get();
|
|
||||||
if ptr.is_null() {
|
|
||||||
ptr = self.buf.get().cast::<u8>();
|
|
||||||
}
|
|
||||||
|
|
||||||
let bytes_left = (end as usize) - (ptr as usize);
|
|
||||||
let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize);
|
|
||||||
|
|
||||||
if align_offset + layout.size() > bytes_left {
|
|
||||||
panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/");
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = unsafe { ptr.add(align_offset) };
|
|
||||||
let ptr = unsafe { ptr.add(align_offset + layout.size()) };
|
|
||||||
|
|
||||||
self.ptr.borrow(cs).set(ptr);
|
|
||||||
|
|
||||||
unsafe { &mut *(res as *mut MaybeUninit<T>) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new();
|
|
||||||
|
|
||||||
pub struct TaskPoolRef {
|
|
||||||
// type-erased `&'static mut TaskPool<F, N>`
|
|
||||||
// Needed because statics can't have generics.
|
|
||||||
ptr: Mutex<Cell<*mut ()>>,
|
|
||||||
}
|
|
||||||
unsafe impl Sync for TaskPoolRef {}
|
|
||||||
unsafe impl Send for TaskPoolRef {}
|
|
||||||
|
|
||||||
impl TaskPoolRef {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: Mutex::new(Cell::new(null_mut())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the pool for this ref, allocating it from the arena the first time.
|
|
||||||
///
|
|
||||||
/// safety: for a given TaskPoolRef instance, must always call with the exact
|
|
||||||
/// same generic params.
|
|
||||||
pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let ptr = self.ptr.borrow(cs);
|
|
||||||
if ptr.get().is_null() {
|
|
||||||
let pool = ARENA.alloc::<TaskPool<F, N>>(cs);
|
|
||||||
pool.write(TaskPool::new());
|
|
||||||
ptr.set(pool as *mut _ as _);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { &*(ptr.get() as *const _) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,17 +5,9 @@
|
|||||||
//! ## WARNING: here be dragons!
|
//! ## WARNING: here be dragons!
|
||||||
//!
|
//!
|
||||||
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
|
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
|
||||||
//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe.
|
//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_macros::task) macro, which are fully safe.
|
||||||
|
|
||||||
#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")]
|
|
||||||
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
|
|
||||||
mod run_queue;
|
mod run_queue;
|
||||||
|
|
||||||
#[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")]
|
|
||||||
#[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")]
|
|
||||||
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
|
|
||||||
mod state;
|
|
||||||
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
mod timer_queue;
|
mod timer_queue;
|
||||||
pub(crate) mod util;
|
pub(crate) mod util;
|
||||||
@ -29,6 +21,7 @@ use core::pin::Pin;
|
|||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
|
use atomic_polyfill::{AtomicU32, Ordering};
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
use embassy_time::driver::{self, AlarmHandle};
|
use embassy_time::driver::{self, AlarmHandle};
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
@ -37,14 +30,21 @@ use embassy_time::Instant;
|
|||||||
use rtos_trace::trace;
|
use rtos_trace::trace;
|
||||||
|
|
||||||
use self::run_queue::{RunQueue, RunQueueItem};
|
use self::run_queue::{RunQueue, RunQueueItem};
|
||||||
use self::state::State;
|
|
||||||
use self::util::{SyncUnsafeCell, UninitCell};
|
use self::util::{SyncUnsafeCell, UninitCell};
|
||||||
pub use self::waker::task_from_waker;
|
pub use self::waker::task_from_waker;
|
||||||
use super::SpawnToken;
|
use super::SpawnToken;
|
||||||
|
|
||||||
|
/// Task is spawned (has a future)
|
||||||
|
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||||
|
/// Task is in the executor run queue
|
||||||
|
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
||||||
|
/// Task is in the executor timer queue
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
||||||
|
|
||||||
/// Raw task header for use in task pointers.
|
/// Raw task header for use in task pointers.
|
||||||
pub(crate) struct TaskHeader {
|
pub(crate) struct TaskHeader {
|
||||||
pub(crate) state: State,
|
pub(crate) state: AtomicU32,
|
||||||
pub(crate) run_queue_item: RunQueueItem,
|
pub(crate) run_queue_item: RunQueueItem,
|
||||||
pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
|
pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
|
||||||
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
|
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
|
||||||
@ -97,7 +97,7 @@ impl TaskRef {
|
|||||||
/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished
|
/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished
|
||||||
/// running. Hence the relevant methods require `&'static self`. It may be reused, however.
|
/// running. Hence the relevant methods require `&'static self`. It may be reused, however.
|
||||||
///
|
///
|
||||||
/// Internally, the [embassy_executor::task](embassy_executor_macros::task) macro allocates an array of `TaskStorage`s
|
/// Internally, the [embassy_executor::task](embassy_macros::task) macro allocates an array of `TaskStorage`s
|
||||||
/// in a `static`. The most common reason to use the raw `Task` is to have control of where
|
/// in a `static`. The most common reason to use the raw `Task` is to have control of where
|
||||||
/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.
|
/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw: TaskHeader {
|
raw: TaskHeader {
|
||||||
state: State::new(),
|
state: AtomicU32::new(0),
|
||||||
run_queue_item: RunQueueItem::new(),
|
run_queue_item: RunQueueItem::new(),
|
||||||
executor: SyncUnsafeCell::new(None),
|
executor: SyncUnsafeCell::new(None),
|
||||||
// Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
|
// Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
|
||||||
@ -161,7 +161,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
match future.poll(&mut cx) {
|
match future.poll(&mut cx) {
|
||||||
Poll::Ready(_) => {
|
Poll::Ready(_) => {
|
||||||
this.future.drop_in_place();
|
this.future.drop_in_place();
|
||||||
this.raw.state.despawn();
|
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||||
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
this.raw.expires_at.set(Instant::MAX);
|
this.raw.expires_at.set(Instant::MAX);
|
||||||
@ -193,7 +193,11 @@ impl<F: Future + 'static> AvailableTask<F> {
|
|||||||
///
|
///
|
||||||
/// This function returns `None` if a task has already been spawned and has not finished running.
|
/// This function returns `None` if a task has already been spawned and has not finished running.
|
||||||
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
|
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
|
||||||
task.raw.state.spawn().then(|| Self { task })
|
task.raw
|
||||||
|
.state
|
||||||
|
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
||||||
|
.ok()
|
||||||
|
.map(|_| Self { task })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
||||||
@ -390,7 +394,8 @@ impl SyncExecutor {
|
|||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
task.expires_at.set(Instant::MAX);
|
task.expires_at.set(Instant::MAX);
|
||||||
|
|
||||||
if !task.state.run_dequeue() {
|
let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
|
||||||
|
if state & STATE_SPAWNED == 0 {
|
||||||
// If task is not running, ignore it. This can happen in the following scenario:
|
// If task is not running, ignore it. This can happen in the following scenario:
|
||||||
// - Task gets dequeued, poll starts
|
// - Task gets dequeued, poll starts
|
||||||
// - While task is being polled, it gets woken. It gets placed in the queue.
|
// - While task is being polled, it gets woken. It gets placed in the queue.
|
||||||
@ -541,7 +546,18 @@ impl Executor {
|
|||||||
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
||||||
pub fn wake_task(task: TaskRef) {
|
pub fn wake_task(task: TaskRef) {
|
||||||
let header = task.header();
|
let header = task.header();
|
||||||
if header.state.run_enqueue() {
|
|
||||||
|
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
|
||||||
|
// If already scheduled, or if not started,
|
||||||
|
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Mark it as scheduled
|
||||||
|
Some(state | STATE_RUN_QUEUED)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if res.is_ok() {
|
||||||
// We have just marked the task as scheduled, so enqueue it.
|
// We have just marked the task as scheduled, so enqueue it.
|
||||||
unsafe {
|
unsafe {
|
||||||
let executor = header.executor.get().unwrap_unchecked();
|
let executor = header.executor.get().unwrap_unchecked();
|
||||||
@ -555,7 +571,18 @@ pub fn wake_task(task: TaskRef) {
|
|||||||
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
||||||
pub fn wake_task_no_pend(task: TaskRef) {
|
pub fn wake_task_no_pend(task: TaskRef) {
|
||||||
let header = task.header();
|
let header = task.header();
|
||||||
if header.state.run_enqueue() {
|
|
||||||
|
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
|
||||||
|
// If already scheduled, or if not started,
|
||||||
|
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Mark it as scheduled
|
||||||
|
Some(state | STATE_RUN_QUEUED)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if res.is_ok() {
|
||||||
// We have just marked the task as scheduled, so enqueue it.
|
// We have just marked the task as scheduled, so enqueue it.
|
||||||
unsafe {
|
unsafe {
|
||||||
let executor = header.executor.get().unwrap_unchecked();
|
let executor = header.executor.get().unwrap_unchecked();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
|
||||||
|
use atomic_polyfill::{AtomicPtr, Ordering};
|
||||||
|
|
||||||
use super::{TaskHeader, TaskRef};
|
use super::{TaskHeader, TaskRef};
|
||||||
use crate::raw::util::SyncUnsafeCell;
|
use crate::raw::util::SyncUnsafeCell;
|
@ -1,75 +0,0 @@
|
|||||||
use core::cell::Cell;
|
|
||||||
|
|
||||||
use critical_section::{CriticalSection, Mutex};
|
|
||||||
|
|
||||||
use super::TaskRef;
|
|
||||||
|
|
||||||
pub(crate) struct RunQueueItem {
|
|
||||||
next: Mutex<Cell<Option<TaskRef>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunQueueItem {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
next: Mutex::new(Cell::new(None)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Atomic task queue using a very, very simple lock-free linked-list queue:
|
|
||||||
///
|
|
||||||
/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.
|
|
||||||
///
|
|
||||||
/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with
|
|
||||||
/// null. Then the batch is iterated following the next pointers until null is reached.
|
|
||||||
///
|
|
||||||
/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK
|
|
||||||
/// for our purposes: it can't create fairness problems since the next batch won't run until the
|
|
||||||
/// current batch is completely processed, so even if a task enqueues itself instantly (for example
|
|
||||||
/// by waking its own waker) can't prevent other tasks from running.
|
|
||||||
pub(crate) struct RunQueue {
|
|
||||||
head: Mutex<Cell<Option<TaskRef>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunQueue {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
head: Mutex::new(Cell::new(None)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enqueues an item. Returns true if the queue was empty.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `item` must NOT be already enqueued in any queue.
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let prev = self.head.borrow(cs).replace(Some(task));
|
|
||||||
task.header().run_queue_item.next.borrow(cs).set(prev);
|
|
||||||
|
|
||||||
prev.is_none()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Empty the queue, then call `on_task` for each task that was in the queue.
|
|
||||||
/// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
|
|
||||||
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
|
|
||||||
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
|
|
||||||
// Atomically empty the queue.
|
|
||||||
let mut next = critical_section::with(|cs| self.head.borrow(cs).take());
|
|
||||||
|
|
||||||
// Iterate the linked list of tasks that were previously in the queue.
|
|
||||||
while let Some(task) = next {
|
|
||||||
// If the task re-enqueues itself, the `next` pointer will get overwritten.
|
|
||||||
// Therefore, first read the next pointer, and only then process the task.
|
|
||||||
|
|
||||||
// safety: we know if the task is enqueued, no one else will touch the `next` pointer.
|
|
||||||
let cs = unsafe { CriticalSection::new() };
|
|
||||||
next = task.header().run_queue_item.next.borrow(cs).get();
|
|
||||||
|
|
||||||
on_task(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
|
|
||||||
/// Task is spawned (has a future)
|
|
||||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
|
||||||
/// Task is in the executor run queue
|
|
||||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
|
||||||
/// Task is in the executor timer queue
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
|
||||||
|
|
||||||
pub(crate) struct State {
|
|
||||||
state: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub const fn new() -> State {
|
|
||||||
Self {
|
|
||||||
state: AtomicU32::new(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If task is idle, mark it as spawned + run_queued and return true.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn spawn(&self) -> bool {
|
|
||||||
self.state
|
|
||||||
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as spawned.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn despawn(&self) {
|
|
||||||
self.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn run_enqueue(&self) -> bool {
|
|
||||||
self.state
|
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
|
|
||||||
// If already scheduled, or if not started,
|
|
||||||
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// Mark it as scheduled
|
|
||||||
Some(state | STATE_RUN_QUEUED)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as run-queued. Return whether the task is spawned.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn run_dequeue(&self) -> bool {
|
|
||||||
let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
|
|
||||||
state & STATE_SPAWNED != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timer_enqueue(&self) -> bool {
|
|
||||||
let old_state = self.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
|
||||||
old_state & STATE_TIMER_QUEUED == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as timer-queued.
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timer_dequeue(&self) {
|
|
||||||
self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
use core::arch::asm;
|
|
||||||
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
|
||||||
|
|
||||||
// Must be kept in sync with the layout of `State`!
|
|
||||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
|
||||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
|
|
||||||
|
|
||||||
#[repr(C, align(4))]
|
|
||||||
pub(crate) struct State {
|
|
||||||
/// Task is spawned (has a future)
|
|
||||||
spawned: AtomicBool,
|
|
||||||
/// Task is in the executor run queue
|
|
||||||
run_queued: AtomicBool,
|
|
||||||
/// Task is in the executor timer queue
|
|
||||||
timer_queued: AtomicBool,
|
|
||||||
pad: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub const fn new() -> State {
|
|
||||||
Self {
|
|
||||||
spawned: AtomicBool::new(false),
|
|
||||||
run_queued: AtomicBool::new(false),
|
|
||||||
timer_queued: AtomicBool::new(false),
|
|
||||||
pad: AtomicBool::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_u32(&self) -> &AtomicU32 {
|
|
||||||
unsafe { &*(self as *const _ as *const AtomicU32) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If task is idle, mark it as spawned + run_queued and return true.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn spawn(&self) -> bool {
|
|
||||||
compiler_fence(Ordering::Release);
|
|
||||||
let r = self
|
|
||||||
.as_u32()
|
|
||||||
.compare_exchange(
|
|
||||||
0,
|
|
||||||
STATE_SPAWNED | STATE_RUN_QUEUED,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
)
|
|
||||||
.is_ok();
|
|
||||||
compiler_fence(Ordering::Acquire);
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as spawned.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn despawn(&self) {
|
|
||||||
compiler_fence(Ordering::Release);
|
|
||||||
self.spawned.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn run_enqueue(&self) -> bool {
|
|
||||||
unsafe {
|
|
||||||
loop {
|
|
||||||
let state: u32;
|
|
||||||
asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack));
|
|
||||||
|
|
||||||
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
|
||||||
asm!("clrex", options(nomem, nostack));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let outcome: usize;
|
|
||||||
let new_state = state | STATE_RUN_QUEUED;
|
|
||||||
asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack));
|
|
||||||
if outcome == 0 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as run-queued. Return whether the task is spawned.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn run_dequeue(&self) -> bool {
|
|
||||||
compiler_fence(Ordering::Release);
|
|
||||||
|
|
||||||
let r = self.spawned.load(Ordering::Relaxed);
|
|
||||||
self.run_queued.store(false, Ordering::Relaxed);
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timer_enqueue(&self) -> bool {
|
|
||||||
!self.timer_queued.swap(true, Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as timer-queued.
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timer_dequeue(&self) {
|
|
||||||
self.timer_queued.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
use core::cell::Cell;
|
|
||||||
|
|
||||||
use critical_section::Mutex;
|
|
||||||
|
|
||||||
/// Task is spawned (has a future)
|
|
||||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
|
||||||
/// Task is in the executor run queue
|
|
||||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
|
||||||
/// Task is in the executor timer queue
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
|
||||||
|
|
||||||
pub(crate) struct State {
|
|
||||||
state: Mutex<Cell<u32>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub const fn new() -> State {
|
|
||||||
Self {
|
|
||||||
state: Mutex::new(Cell::new(0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update<R>(&self, f: impl FnOnce(&mut u32) -> R) -> R {
|
|
||||||
critical_section::with(|cs| {
|
|
||||||
let s = self.state.borrow(cs);
|
|
||||||
let mut val = s.get();
|
|
||||||
let r = f(&mut val);
|
|
||||||
s.set(val);
|
|
||||||
r
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If task is idle, mark it as spawned + run_queued and return true.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn spawn(&self) -> bool {
|
|
||||||
self.update(|s| {
|
|
||||||
if *s == 0 {
|
|
||||||
*s = STATE_SPAWNED | STATE_RUN_QUEUED;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as spawned.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn despawn(&self) {
|
|
||||||
self.update(|s| *s &= !STATE_SPAWNED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn run_enqueue(&self) -> bool {
|
|
||||||
self.update(|s| {
|
|
||||||
if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
*s |= STATE_RUN_QUEUED;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as run-queued. Return whether the task is spawned.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn run_dequeue(&self) -> bool {
|
|
||||||
self.update(|s| {
|
|
||||||
let ok = *s & STATE_SPAWNED != 0;
|
|
||||||
*s &= !STATE_RUN_QUEUED;
|
|
||||||
ok
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timer_enqueue(&self) -> bool {
|
|
||||||
self.update(|s| {
|
|
||||||
let ok = *s & STATE_TIMER_QUEUED == 0;
|
|
||||||
*s |= STATE_TIMER_QUEUED;
|
|
||||||
ok
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark the task as timer-queued.
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timer_dequeue(&self) {
|
|
||||||
self.update(|s| *s &= !STATE_TIMER_QUEUED);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
|
||||||
|
use atomic_polyfill::Ordering;
|
||||||
use embassy_time::Instant;
|
use embassy_time::Instant;
|
||||||
|
|
||||||
use super::TaskRef;
|
use super::{TaskRef, STATE_TIMER_QUEUED};
|
||||||
use crate::raw::util::SyncUnsafeCell;
|
use crate::raw::util::SyncUnsafeCell;
|
||||||
|
|
||||||
pub(crate) struct TimerQueueItem {
|
pub(crate) struct TimerQueueItem {
|
||||||
@ -31,7 +32,10 @@ impl TimerQueue {
|
|||||||
pub(crate) unsafe fn update(&self, p: TaskRef) {
|
pub(crate) unsafe fn update(&self, p: TaskRef) {
|
||||||
let task = p.header();
|
let task = p.header();
|
||||||
if task.expires_at.get() != Instant::MAX {
|
if task.expires_at.get() != Instant::MAX {
|
||||||
if task.state.timer_enqueue() {
|
let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||||
|
let is_new = old_state & STATE_TIMER_QUEUED == 0;
|
||||||
|
|
||||||
|
if is_new {
|
||||||
task.timer_queue_item.next.set(self.head.get());
|
task.timer_queue_item.next.set(self.head.get());
|
||||||
self.head.set(Some(p));
|
self.head.set(Some(p));
|
||||||
}
|
}
|
||||||
@ -71,7 +75,7 @@ impl TimerQueue {
|
|||||||
} else {
|
} else {
|
||||||
// Remove it
|
// Remove it
|
||||||
prev.set(task.timer_queue_item.next.get());
|
prev.set(task.timer_queue_item.next.get());
|
||||||
task.state.timer_dequeue();
|
task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,9 +115,9 @@ impl Spawner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by the `embassy_executor_macros::main!` macro to throw an error when spawn
|
// Used by the `embassy_macros::main!` macro to throw an error when spawn
|
||||||
// fails. This is here to allow conditional use of `defmt::unwrap!`
|
// fails. This is here to allow conditional use of `defmt::unwrap!`
|
||||||
// without introducing a `defmt` feature in the `embassy_executor_macros` package,
|
// without introducing a `defmt` feature in the `embassy_macros` package,
|
||||||
// which would require use of `-Z namespaced-features`.
|
// which would require use of `-Z namespaced-features`.
|
||||||
/// Spawn a task into an executor, panicking on failure.
|
/// Spawn a task into an executor, panicking on failure.
|
||||||
///
|
///
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
|
||||||
|
|
||||||
use std::boxed::Box;
|
|
||||||
use std::future::poll_fn;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::task::Poll;
|
|
||||||
|
|
||||||
use embassy_executor::raw::Executor;
|
|
||||||
use embassy_executor::task;
|
|
||||||
|
|
||||||
#[export_name = "__pender"]
|
|
||||||
fn __pender(context: *mut ()) {
|
|
||||||
unsafe {
|
|
||||||
let trace = &*(context as *const Trace);
|
|
||||||
trace.push("pend");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Trace {
|
|
||||||
trace: Arc<Mutex<Vec<&'static str>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Trace {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
trace: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn push(&self, value: &'static str) {
|
|
||||||
self.trace.lock().unwrap().push(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self) -> Vec<&'static str> {
|
|
||||||
self.trace.lock().unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup() -> (&'static Executor, Trace) {
|
|
||||||
let trace = Trace::new();
|
|
||||||
let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
|
|
||||||
let executor = &*Box::leak(Box::new(Executor::new(context)));
|
|
||||||
(executor, trace)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn executor_noop() {
|
|
||||||
let (executor, trace) = setup();
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
assert!(trace.get().is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn executor_task() {
|
|
||||||
#[task]
|
|
||||||
async fn task1(trace: Trace) {
|
|
||||||
trace.push("poll task1")
|
|
||||||
}
|
|
||||||
|
|
||||||
let (executor, trace) = setup();
|
|
||||||
executor.spawner().spawn(task1(trace.clone())).unwrap();
|
|
||||||
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
trace.get(),
|
|
||||||
&[
|
|
||||||
"pend", // spawning a task pends the executor
|
|
||||||
"poll task1", // poll only once.
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn executor_task_self_wake() {
|
|
||||||
#[task]
|
|
||||||
async fn task1(trace: Trace) {
|
|
||||||
poll_fn(|cx| {
|
|
||||||
trace.push("poll task1");
|
|
||||||
cx.waker().wake_by_ref();
|
|
||||||
Poll::Pending
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
let (executor, trace) = setup();
|
|
||||||
executor.spawner().spawn(task1(trace.clone())).unwrap();
|
|
||||||
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
trace.get(),
|
|
||||||
&[
|
|
||||||
"pend", // spawning a task pends the executor
|
|
||||||
"poll task1", //
|
|
||||||
"pend", // task self-wakes
|
|
||||||
"poll task1", //
|
|
||||||
"pend", // task self-wakes
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn executor_task_self_wake_twice() {
|
|
||||||
#[task]
|
|
||||||
async fn task1(trace: Trace) {
|
|
||||||
poll_fn(|cx| {
|
|
||||||
trace.push("poll task1");
|
|
||||||
cx.waker().wake_by_ref();
|
|
||||||
trace.push("poll task1 wake 2");
|
|
||||||
cx.waker().wake_by_ref();
|
|
||||||
Poll::Pending
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
let (executor, trace) = setup();
|
|
||||||
executor.spawner().spawn(task1(trace.clone())).unwrap();
|
|
||||||
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
unsafe { executor.poll() };
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
trace.get(),
|
|
||||||
&[
|
|
||||||
"pend", // spawning a task pends the executor
|
|
||||||
"poll task1", //
|
|
||||||
"pend", // task self-wakes
|
|
||||||
"poll task1 wake 2", // task self-wakes again, shouldn't pend
|
|
||||||
"poll task1", //
|
|
||||||
"pend", // task self-wakes
|
|
||||||
"poll task1 wake 2", // task self-wakes again, shouldn't pend
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-futures"
|
name = "embassy-futures"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "no-std, no-alloc utilities for working with futures"
|
description = "no-std, no-alloc utilities for working with futures"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! Atomic reusable ringbuffer.
|
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
|
||||||
|
|
||||||
@ -15,9 +14,8 @@ use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
|
|||||||
/// One concurrent writer and one concurrent reader are supported, even at
|
/// One concurrent writer and one concurrent reader are supported, even at
|
||||||
/// different execution priorities (like main and irq).
|
/// different execution priorities (like main and irq).
|
||||||
pub struct RingBuffer {
|
pub struct RingBuffer {
|
||||||
#[doc(hidden)]
|
|
||||||
pub buf: AtomicPtr<u8>,
|
pub buf: AtomicPtr<u8>,
|
||||||
len: AtomicUsize,
|
pub len: AtomicUsize,
|
||||||
|
|
||||||
// start and end wrap at len*2, not at len.
|
// start and end wrap at len*2, not at len.
|
||||||
// This allows distinguishing "full" and "empty".
|
// This allows distinguishing "full" and "empty".
|
||||||
@ -26,16 +24,11 @@ pub struct RingBuffer {
|
|||||||
//
|
//
|
||||||
// This avoids having to consider the ringbuffer "full" at len-1 instead of len.
|
// This avoids having to consider the ringbuffer "full" at len-1 instead of len.
|
||||||
// The usual solution is adding a "full" flag, but that can't be made atomic
|
// The usual solution is adding a "full" flag, but that can't be made atomic
|
||||||
#[doc(hidden)]
|
|
||||||
pub start: AtomicUsize,
|
pub start: AtomicUsize,
|
||||||
#[doc(hidden)]
|
|
||||||
pub end: AtomicUsize,
|
pub end: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type which can only read from a ring buffer.
|
|
||||||
pub struct Reader<'a>(&'a RingBuffer);
|
pub struct Reader<'a>(&'a RingBuffer);
|
||||||
|
|
||||||
/// A type which can only write to a ring buffer.
|
|
||||||
pub struct Writer<'a>(&'a RingBuffer);
|
pub struct Writer<'a>(&'a RingBuffer);
|
||||||
|
|
||||||
impl RingBuffer {
|
impl RingBuffer {
|
||||||
@ -96,12 +89,10 @@ impl RingBuffer {
|
|||||||
Writer(self)
|
Writer(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return length of buffer.
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len.load(Ordering::Relaxed)
|
self.len.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if buffer is full.
|
|
||||||
pub fn is_full(&self) -> bool {
|
pub fn is_full(&self) -> bool {
|
||||||
let len = self.len.load(Ordering::Relaxed);
|
let len = self.len.load(Ordering::Relaxed);
|
||||||
let start = self.start.load(Ordering::Relaxed);
|
let start = self.start.load(Ordering::Relaxed);
|
||||||
@ -110,7 +101,6 @@ impl RingBuffer {
|
|||||||
self.wrap(start + len) == end
|
self.wrap(start + len) == end
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if buffer is empty.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
let start = self.start.load(Ordering::Relaxed);
|
let start = self.start.load(Ordering::Relaxed);
|
||||||
let end = self.end.load(Ordering::Relaxed);
|
let end = self.end.load(Ordering::Relaxed);
|
||||||
@ -248,7 +238,6 @@ impl<'a> Writer<'a> {
|
|||||||
[(unsafe { buf.add(end) }, n0), (buf, n1)]
|
[(unsafe { buf.add(end) }, n0), (buf, n1)]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark n bytes as written and advance the write index.
|
|
||||||
pub fn push_done(&mut self, n: usize) {
|
pub fn push_done(&mut self, n: usize) {
|
||||||
trace!(" ringbuf: push {:?}", n);
|
trace!(" ringbuf: push {:?}", n);
|
||||||
let end = self.0.end.load(Ordering::Relaxed);
|
let end = self.0.end.load(Ordering::Relaxed);
|
||||||
@ -334,7 +323,6 @@ impl<'a> Reader<'a> {
|
|||||||
(unsafe { buf.add(start) }, n)
|
(unsafe { buf.add(start) }, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark n bytes as read and allow advance the read index.
|
|
||||||
pub fn pop_done(&mut self, n: usize) {
|
pub fn pop_done(&mut self, n: usize) {
|
||||||
trace!(" ringbuf: pop {:?}", n);
|
trace!(" ringbuf: pop {:?}", n);
|
||||||
|
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
//! Types for controlling when drop is invoked.
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
/// A type to delay the drop handler invocation.
|
#[must_use = "to delay the drop handler invokation to the end of the scope"]
|
||||||
#[must_use = "to delay the drop handler invocation to the end of the scope"]
|
|
||||||
pub struct OnDrop<F: FnOnce()> {
|
pub struct OnDrop<F: FnOnce()> {
|
||||||
f: MaybeUninit<F>,
|
f: MaybeUninit<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FnOnce()> OnDrop<F> {
|
impl<F: FnOnce()> OnDrop<F> {
|
||||||
/// Create a new instance.
|
|
||||||
pub fn new(f: F) -> Self {
|
pub fn new(f: F) -> Self {
|
||||||
Self { f: MaybeUninit::new(f) }
|
Self { f: MaybeUninit::new(f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prevent drop handler from running.
|
|
||||||
pub fn defuse(self) {
|
pub fn defuse(self) {
|
||||||
mem::forget(self)
|
mem::forget(self)
|
||||||
}
|
}
|
||||||
@ -38,7 +34,6 @@ pub struct DropBomb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DropBomb {
|
impl DropBomb {
|
||||||
/// Create a new instance.
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { _private: () }
|
Self { _private: () }
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/// Types for the peripheral singletons.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! peripherals_definition {
|
macro_rules! peripherals_definition {
|
||||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||||
@ -30,7 +29,6 @@ macro_rules! peripherals_definition {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define the peripherals struct.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! peripherals_struct {
|
macro_rules! peripherals_struct {
|
||||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||||
@ -89,7 +87,6 @@ macro_rules! peripherals_struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defining peripheral type.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! peripherals {
|
macro_rules! peripherals {
|
||||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||||
@ -108,7 +105,6 @@ macro_rules! peripherals {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience converting into reference.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! into_ref {
|
macro_rules! into_ref {
|
||||||
($($name:ident),*) => {
|
($($name:ident),*) => {
|
||||||
@ -118,7 +114,6 @@ macro_rules! into_ref {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement the peripheral trait.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_peripheral {
|
macro_rules! impl_peripheral {
|
||||||
($type:ident) => {
|
($type:ident) => {
|
||||||
|
@ -20,7 +20,6 @@ pub struct PeripheralRef<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> PeripheralRef<'a, T> {
|
impl<'a, T> PeripheralRef<'a, T> {
|
||||||
/// Create a new reference to a peripheral.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(inner: T) -> Self {
|
pub fn new(inner: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! Types for dealing with rational numbers.
|
|
||||||
use core::ops::{Add, Div, Mul};
|
use core::ops::{Add, Div, Mul};
|
||||||
|
|
||||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
|
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
|
||||||
|
31
embassy-lora/Cargo.toml
Normal file
31
embassy-lora/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-lora"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
|
||||||
|
features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/unstable-pac", "time", "defmt"]
|
||||||
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
stm32wl = ["dep:embassy-stm32"]
|
||||||
|
time = ["embassy-time", "lorawan-device"]
|
||||||
|
defmt = ["dep:defmt", "lorawan-device/defmt"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
||||||
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||||
|
|
||||||
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
|
lora-phy = { version = "2" }
|
||||||
|
lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true }
|
317
embassy-lora/src/iv.rs
Normal file
317
embassy-lora/src/iv.rs
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
use embassy_stm32::interrupt;
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
use embassy_stm32::interrupt::InterruptExt;
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
use embassy_stm32::pac;
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
use embassy_sync::signal::Signal;
|
||||||
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
|
use embedded_hal_async::delay::DelayUs;
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use lora_phy::mod_params::RadioError::*;
|
||||||
|
use lora_phy::mod_params::{BoardType, RadioError};
|
||||||
|
use lora_phy::mod_traits::InterfaceVariant;
|
||||||
|
|
||||||
|
/// Interrupt handler.
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
pub struct InterruptHandler {}
|
||||||
|
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
impl interrupt::typelevel::Handler<interrupt::typelevel::SUBGHZ_RADIO> for InterruptHandler {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
|
interrupt::SUBGHZ_RADIO.disable();
|
||||||
|
IRQ_SIGNAL.signal(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||||
|
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination
|
||||||
|
pub struct Stm32wlInterfaceVariant<CTRL> {
|
||||||
|
board_type: BoardType,
|
||||||
|
rf_switch_rx: Option<CTRL>,
|
||||||
|
rf_switch_tx: Option<CTRL>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
impl<'a, CTRL> Stm32wlInterfaceVariant<CTRL>
|
||||||
|
where
|
||||||
|
CTRL: OutputPin,
|
||||||
|
{
|
||||||
|
/// Create an InterfaceVariant instance for an stm32wl/sx1262 combination
|
||||||
|
pub fn new(
|
||||||
|
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SUBGHZ_RADIO, InterruptHandler>,
|
||||||
|
rf_switch_rx: Option<CTRL>,
|
||||||
|
rf_switch_tx: Option<CTRL>,
|
||||||
|
) -> Result<Self, RadioError> {
|
||||||
|
interrupt::SUBGHZ_RADIO.disable();
|
||||||
|
Ok(Self {
|
||||||
|
board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board
|
||||||
|
rf_switch_rx,
|
||||||
|
rf_switch_tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "stm32wl")]
|
||||||
|
impl<CTRL> InterfaceVariant for Stm32wlInterfaceVariant<CTRL>
|
||||||
|
where
|
||||||
|
CTRL: OutputPin,
|
||||||
|
{
|
||||||
|
fn set_board_type(&mut self, board_type: BoardType) {
|
||||||
|
self.board_type = board_type;
|
||||||
|
}
|
||||||
|
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||||
|
pac::PWR.subghzspicr().modify(|w| w.set_nss(false));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||||
|
pac::PWR.subghzspicr().modify(|w| w.set_nss(true));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||||
|
pac::RCC.csr().modify(|w| w.set_rfrst(true));
|
||||||
|
pac::RCC.csr().modify(|w| w.set_rfrst(false));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||||
|
while pac::PWR.sr2().read().rfbusys() {}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||||
|
unsafe { interrupt::SUBGHZ_RADIO.enable() };
|
||||||
|
IRQ_SIGNAL.wait().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination
|
||||||
|
pub struct Stm32l0InterfaceVariant<CTRL, WAIT> {
|
||||||
|
board_type: BoardType,
|
||||||
|
nss: CTRL,
|
||||||
|
reset: CTRL,
|
||||||
|
irq: WAIT,
|
||||||
|
rf_switch_rx: Option<CTRL>,
|
||||||
|
rf_switch_tx: Option<CTRL>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CTRL, WAIT> Stm32l0InterfaceVariant<CTRL, WAIT>
|
||||||
|
where
|
||||||
|
CTRL: OutputPin,
|
||||||
|
WAIT: Wait,
|
||||||
|
{
|
||||||
|
/// Create an InterfaceVariant instance for an stm32l0/sx1276 combination
|
||||||
|
pub fn new(
|
||||||
|
nss: CTRL,
|
||||||
|
reset: CTRL,
|
||||||
|
irq: WAIT,
|
||||||
|
rf_switch_rx: Option<CTRL>,
|
||||||
|
rf_switch_tx: Option<CTRL>,
|
||||||
|
) -> Result<Self, RadioError> {
|
||||||
|
Ok(Self {
|
||||||
|
board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board
|
||||||
|
nss,
|
||||||
|
reset,
|
||||||
|
irq,
|
||||||
|
rf_switch_rx,
|
||||||
|
rf_switch_tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CTRL, WAIT> InterfaceVariant for Stm32l0InterfaceVariant<CTRL, WAIT>
|
||||||
|
where
|
||||||
|
CTRL: OutputPin,
|
||||||
|
WAIT: Wait,
|
||||||
|
{
|
||||||
|
fn set_board_type(&mut self, board_type: BoardType) {
|
||||||
|
self.board_type = board_type;
|
||||||
|
}
|
||||||
|
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.nss.set_low().map_err(|_| NSS)
|
||||||
|
}
|
||||||
|
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.nss.set_high().map_err(|_| NSS)
|
||||||
|
}
|
||||||
|
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||||
|
delay.delay_ms(10).await;
|
||||||
|
self.reset.set_low().map_err(|_| Reset)?;
|
||||||
|
delay.delay_ms(10).await;
|
||||||
|
self.reset.set_high().map_err(|_| Reset)?;
|
||||||
|
delay.delay_ms(10).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.irq.wait_for_high().await.map_err(|_| Irq)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board
|
||||||
|
pub struct GenericSx126xInterfaceVariant<CTRL, WAIT> {
|
||||||
|
board_type: BoardType,
|
||||||
|
nss: CTRL,
|
||||||
|
reset: CTRL,
|
||||||
|
dio1: WAIT,
|
||||||
|
busy: WAIT,
|
||||||
|
rf_switch_rx: Option<CTRL>,
|
||||||
|
rf_switch_tx: Option<CTRL>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CTRL, WAIT> GenericSx126xInterfaceVariant<CTRL, WAIT>
|
||||||
|
where
|
||||||
|
CTRL: OutputPin,
|
||||||
|
WAIT: Wait,
|
||||||
|
{
|
||||||
|
/// Create an InterfaceVariant instance for an nrf52840/sx1262 combination
|
||||||
|
pub fn new(
|
||||||
|
nss: CTRL,
|
||||||
|
reset: CTRL,
|
||||||
|
dio1: WAIT,
|
||||||
|
busy: WAIT,
|
||||||
|
rf_switch_rx: Option<CTRL>,
|
||||||
|
rf_switch_tx: Option<CTRL>,
|
||||||
|
) -> Result<Self, RadioError> {
|
||||||
|
Ok(Self {
|
||||||
|
board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board
|
||||||
|
nss,
|
||||||
|
reset,
|
||||||
|
dio1,
|
||||||
|
busy,
|
||||||
|
rf_switch_rx,
|
||||||
|
rf_switch_tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CTRL, WAIT> InterfaceVariant for GenericSx126xInterfaceVariant<CTRL, WAIT>
|
||||||
|
where
|
||||||
|
CTRL: OutputPin,
|
||||||
|
WAIT: Wait,
|
||||||
|
{
|
||||||
|
fn set_board_type(&mut self, board_type: BoardType) {
|
||||||
|
self.board_type = board_type;
|
||||||
|
}
|
||||||
|
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.nss.set_low().map_err(|_| NSS)
|
||||||
|
}
|
||||||
|
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.nss.set_high().map_err(|_| NSS)
|
||||||
|
}
|
||||||
|
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||||
|
delay.delay_ms(10).await;
|
||||||
|
self.reset.set_low().map_err(|_| Reset)?;
|
||||||
|
delay.delay_ms(20).await;
|
||||||
|
self.reset.set_high().map_err(|_| Reset)?;
|
||||||
|
delay.delay_ms(10).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.busy.wait_for_low().await.map_err(|_| Busy)
|
||||||
|
}
|
||||||
|
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||||
|
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_high().map_err(|_| RfSwitchRx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_high().map_err(|_| RfSwitchTx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn disable_rf_switch(&mut self) -> Result<(), RadioError> {
|
||||||
|
match &mut self.rf_switch_rx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?,
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
match &mut self.rf_switch_tx {
|
||||||
|
Some(pin) => pin.set_low().map_err(|_| RfSwitchTx),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
embassy-lora/src/lib.rs
Normal file
40
embassy-lora/src/lib.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![feature(async_fn_in_trait, impl_trait_projections)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
|
//! embassy-lora holds LoRa-specific functionality.
|
||||||
|
|
||||||
|
pub(crate) mod fmt;
|
||||||
|
|
||||||
|
/// interface variants required by the external lora physical layer crate (lora-phy)
|
||||||
|
pub mod iv;
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
|
||||||
|
/// A convenience timer to use with the LoRaWAN crate
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
pub struct LoraTimer {
|
||||||
|
start: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl LoraTimer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { start: Instant::now() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.start = Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn at(&mut self, millis: u64) {
|
||||||
|
Timer::at(self.start + Duration::from_millis(millis)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delay_ms(&mut self, millis: u64) {
|
||||||
|
Timer::after_millis(millis).await
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor-macros"
|
name = "embassy-macros"
|
||||||
version = "0.4.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "macros for creating the entry point and tasks for embassy-executor"
|
description = "macros for creating the entry point and tasks for embassy-executor"
|
||||||
@ -21,4 +21,5 @@ proc-macro2 = "1.0.29"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = []
|
# Enabling this cause interrupt::take! to require embassy-executor
|
||||||
|
rtos-trace-interrupt = []
|
21
embassy-macros/README.md
Normal file
21
embassy-macros/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# embassy-macros
|
||||||
|
|
||||||
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
Macros for creating the main entry point and tasks that can be spawned by `embassy-executor`.
|
||||||
|
|
||||||
|
NOTE: The macros are re-exported by the `embassy-executor` crate which should be used instead of adding a direct dependency on the `embassy-macros` crate.
|
||||||
|
|
||||||
|
## Minimum supported Rust version (MSRV)
|
||||||
|
|
||||||
|
The `task` and `main` macros require the type alias impl trait (TAIT) nightly feature in order to compile.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user