diff options
178 files changed, 9129 insertions, 3316 deletions
diff --git a/.gitattributes b/.gitattributes index a51376f0d..2850eb3b1 100644 --- a/.gitattributes +++ b/.gitattributes | |||
| @@ -17,6 +17,13 @@ | |||
| 17 | 17 | ||
| 18 | .vscode/*.json linguist-language=JSON-with-Comments | 18 | .vscode/*.json linguist-language=JSON-with-Comments |
| 19 | 19 | ||
| 20 | # Configure changelog files to use union merge strategy | ||
| 21 | # This prevents merge conflicts by automatically combining changes from both branches | ||
| 22 | CHANGELOG.md merge=union | ||
| 23 | changelog.md merge=union | ||
| 24 | CHANGELOG.txt merge=union | ||
| 25 | changelog.txt merge=union | ||
| 26 | |||
| 20 | *.raw binary | 27 | *.raw binary |
| 21 | *.bin binary | 28 | *.bin binary |
| 22 | *.png binary | 29 | *.png binary |
diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh index 95cb4100c..2d7c4db3f 100755 --- a/.github/ci/build-nightly.sh +++ b/.github/ci/build-nightly.sh | |||
| @@ -7,6 +7,7 @@ set -euo pipefail | |||
| 7 | export RUSTUP_HOME=/ci/cache/rustup | 7 | export RUSTUP_HOME=/ci/cache/rustup |
| 8 | export CARGO_HOME=/ci/cache/cargo | 8 | export CARGO_HOME=/ci/cache/cargo |
| 9 | export CARGO_TARGET_DIR=/ci/cache/target | 9 | export CARGO_TARGET_DIR=/ci/cache/target |
| 10 | export PATH=$CARGO_HOME/bin:$PATH | ||
| 10 | mv rust-toolchain-nightly.toml rust-toolchain.toml | 11 | mv rust-toolchain-nightly.toml rust-toolchain.toml |
| 11 | 12 | ||
| 12 | # needed for "dumb HTTP" transport support | 13 | # needed for "dumb HTTP" transport support |
| @@ -22,6 +23,8 @@ fi | |||
| 22 | hashtime restore /ci/cache/filetime.json || true | 23 | hashtime restore /ci/cache/filetime.json || true |
| 23 | hashtime save /ci/cache/filetime.json | 24 | hashtime save /ci/cache/filetime.json |
| 24 | 25 | ||
| 26 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe | ||
| 27 | |||
| 25 | ./ci-nightly.sh | 28 | ./ci-nightly.sh |
| 26 | 29 | ||
| 27 | # Save lockfiles | 30 | # Save lockfiles |
diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index 103575bc9..b6626639d 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh | |||
| @@ -7,13 +7,14 @@ set -euo pipefail | |||
| 7 | export RUSTUP_HOME=/ci/cache/rustup | 7 | export RUSTUP_HOME=/ci/cache/rustup |
| 8 | export CARGO_HOME=/ci/cache/cargo | 8 | export CARGO_HOME=/ci/cache/cargo |
| 9 | export CARGO_TARGET_DIR=/ci/cache/target | 9 | export CARGO_TARGET_DIR=/ci/cache/target |
| 10 | export PATH=$CARGO_HOME/bin:$PATH | ||
| 10 | 11 | ||
| 11 | # needed for "dumb HTTP" transport support | 12 | # needed for "dumb HTTP" transport support |
| 12 | # used when pointing stm32-metapac to a CI-built one. | 13 | # used when pointing stm32-metapac to a CI-built one. |
| 13 | export CARGO_NET_GIT_FETCH_WITH_CLI=true | 14 | export CARGO_NET_GIT_FETCH_WITH_CLI=true |
| 14 | 15 | ||
| 15 | cargo install espup | 16 | cargo install espup --locked |
| 16 | /ci/cache/cargo/bin/espup install --toolchain-version 1.84.0.0 | 17 | espup install --toolchain-version 1.88.0.0 |
| 17 | 18 | ||
| 18 | # Restore lockfiles | 19 | # Restore lockfiles |
| 19 | if [ -f /ci/cache/lockfiles.tar ]; then | 20 | if [ -f /ci/cache/lockfiles.tar ]; then |
| @@ -24,11 +25,7 @@ fi | |||
| 24 | hashtime restore /ci/cache/filetime.json || true | 25 | hashtime restore /ci/cache/filetime.json || true |
| 25 | hashtime save /ci/cache/filetime.json | 26 | hashtime save /ci/cache/filetime.json |
| 26 | 27 | ||
| 27 | mkdir .cargo | 28 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe |
| 28 | cat > .cargo/config.toml<< EOF | ||
| 29 | [unstable] | ||
| 30 | build-std = ["alloc", "core"] | ||
| 31 | EOF | ||
| 32 | 29 | ||
| 33 | ./ci-xtensa.sh | 30 | ./ci-xtensa.sh |
| 34 | 31 | ||
diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 68a7c0c34..59bcefed6 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh | |||
| @@ -7,6 +7,7 @@ set -euo pipefail | |||
| 7 | export RUSTUP_HOME=/ci/cache/rustup | 7 | export RUSTUP_HOME=/ci/cache/rustup |
| 8 | export CARGO_HOME=/ci/cache/cargo | 8 | export CARGO_HOME=/ci/cache/cargo |
| 9 | export CARGO_TARGET_DIR=/ci/cache/target | 9 | export CARGO_TARGET_DIR=/ci/cache/target |
| 10 | export PATH=$CARGO_HOME/bin:$PATH | ||
| 10 | if [ -f /ci/secrets/teleprobe-token.txt ]; then | 11 | if [ -f /ci/secrets/teleprobe-token.txt ]; then |
| 11 | echo Got teleprobe token! | 12 | echo Got teleprobe token! |
| 12 | export TELEPROBE_HOST=https://teleprobe.embassy.dev | 13 | export TELEPROBE_HOST=https://teleprobe.embassy.dev |
| @@ -27,6 +28,8 @@ fi | |||
| 27 | hashtime restore /ci/cache/filetime.json || true | 28 | hashtime restore /ci/cache/filetime.json || true |
| 28 | hashtime save /ci/cache/filetime.json | 29 | hashtime save /ci/cache/filetime.json |
| 29 | 30 | ||
| 31 | cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe | ||
| 32 | |||
| 30 | ./ci.sh | 33 | ./ci.sh |
| 31 | 34 | ||
| 32 | # Save lockfiles | 35 | # Save lockfiles |
diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index ac96008d8..876c261a1 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh | |||
| @@ -19,42 +19,42 @@ mv rust-toolchain-nightly.toml rust-toolchain.toml | |||
| 19 | # which makes rustup very sad | 19 | # which makes rustup very sad |
| 20 | rustc --version > /dev/null | 20 | rustc --version > /dev/null |
| 21 | 21 | ||
| 22 | docserver-builder -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup | 22 | docserver build -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup |
| 23 | docserver-builder -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup | 23 | docserver build -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup |
| 24 | docserver-builder -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup | 24 | docserver build -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup |
| 25 | docserver-builder -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup | 25 | docserver build -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup |
| 26 | docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup | 26 | docserver build -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup |
| 27 | docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup | 27 | docserver build -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup |
| 28 | docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup | 28 | docserver build -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup |
| 29 | docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup | 29 | docserver build -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup |
| 30 | docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup | 30 | docserver build -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup |
| 31 | docserver-builder -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup | 31 | docserver build -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup |
| 32 | docserver-builder -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup | 32 | docserver build -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup |
| 33 | docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup | 33 | docserver build -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup |
| 34 | docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup | 34 | docserver build -i ./cyw43 -o webroot/crates/cyw43/git.zup |
| 35 | docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup | 35 | docserver build -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup |
| 36 | docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static | 36 | docserver build -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static |
| 37 | 37 | ||
| 38 | docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup | 38 | docserver build -i ./embassy-time -o webroot/crates/embassy-time/git.zup |
| 39 | docserver-builder -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup | 39 | docserver build -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup |
| 40 | docserver-builder -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup | 40 | docserver build -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup |
| 41 | 41 | ||
| 42 | docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup | 42 | docserver build -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup |
| 43 | docserver-builder -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup | 43 | docserver build -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup |
| 44 | docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup | 44 | docserver build -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup |
| 45 | docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup | 45 | docserver build -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup |
| 46 | docserver-builder -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup | 46 | docserver build -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup |
| 47 | 47 | ||
| 48 | docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup | 48 | docserver build -i ./embassy-net -o webroot/crates/embassy-net/git.zup |
| 49 | docserver-builder -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup | 49 | docserver build -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup |
| 50 | docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup | 50 | docserver build -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup |
| 51 | docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup | 51 | docserver build -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup |
| 52 | docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup | 52 | docserver build -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup |
| 53 | docserver-builder -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup | 53 | docserver build -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup |
| 54 | docserver-builder -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup | 54 | docserver build -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup |
| 55 | docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup | 55 | docserver build -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup |
| 56 | docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup | 56 | docserver build -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup |
| 57 | docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup | 57 | docserver build -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup |
| 58 | 58 | ||
| 59 | export KUBECONFIG=/ci/secrets/kubeconfig.yml | 59 | export KUBECONFIG=/ci/secrets/kubeconfig.yml |
| 60 | POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) | 60 | POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) |
| @@ -65,6 +65,6 @@ kubectl cp webroot/static $POD:/data | |||
| 65 | # so that it doesn't prevent other crates from getting docs updates when it breaks. | 65 | # so that it doesn't prevent other crates from getting docs updates when it breaks. |
| 66 | 66 | ||
| 67 | rm -rf webroot | 67 | rm -rf webroot |
| 68 | docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup | 68 | docserver build -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup |
| 69 | POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) | 69 | POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) |
| 70 | kubectl cp webroot/crates $POD:/data | 70 | kubectl cp webroot/crates $POD:/data |
diff --git a/.vscode/settings.json b/.vscode/settings.json index 6edd9312a..c504f3ccd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -17,7 +17,7 @@ | |||
| 17 | //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", | 17 | //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", |
| 18 | "rust-analyzer.cargo.features": [ | 18 | "rust-analyzer.cargo.features": [ |
| 19 | // Comment out these features when working on the examples. Most example crates do not have any cargo features. | 19 | // Comment out these features when working on the examples. Most example crates do not have any cargo features. |
| 20 | "stm32f446re", | 20 | "stm32f107rb", |
| 21 | "time-driver-any", | 21 | "time-driver-any", |
| 22 | "unstable-pac", | 22 | "unstable-pac", |
| 23 | "exti", | 23 | "exti", |
diff --git a/ci-nightly.sh b/ci-nightly.sh index d486d442c..afe9f534c 100755 --- a/ci-nightly.sh +++ b/ci-nightly.sh | |||
| @@ -8,19 +8,4 @@ if [[ -z "${CARGO_TARGET_DIR}" ]]; then | |||
| 8 | export CARGO_TARGET_DIR=target_ci | 8 | export CARGO_TARGET_DIR=target_ci |
| 9 | fi | 9 | fi |
| 10 | 10 | ||
| 11 | cargo batch \ | 11 | cargo embassy-devtool build --group nightly |
| 12 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \ | ||
| 13 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ | ||
| 14 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ | ||
| 15 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ | ||
| 16 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt \ | ||
| 17 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \ | ||
| 18 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \ | ||
| 19 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \ | ||
| 20 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \ | ||
| 21 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \ | ||
| 22 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \ | ||
| 23 | --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-rtic \ | ||
| 24 | --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features nightly,arch-cortex-ar,executor-thread \ | ||
| 25 | |||
| 26 | RUSTFLAGS="$RUSTFLAGS -C target-cpu=atmega328p" cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-none -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p | ||
diff --git a/ci-xtensa.sh b/ci-xtensa.sh index 6c9807e98..0dd41a9ce 100755 --- a/ci-xtensa.sh +++ b/ci-xtensa.sh | |||
| @@ -9,30 +9,4 @@ if [[ -z "${CARGO_TARGET_DIR}" ]]; then | |||
| 9 | export CARGO_TARGET_DIR=target_ci | 9 | export CARGO_TARGET_DIR=target_ci |
| 10 | fi | 10 | fi |
| 11 | 11 | ||
| 12 | cargo batch \ | 12 | cargo embassy-devtool build --group xtensa |
| 13 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf \ | ||
| 14 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features log \ | ||
| 15 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt \ | ||
| 16 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ | ||
| 17 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread \ | ||
| 18 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread \ | ||
| 19 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread \ | ||
| 20 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin \ | ||
| 21 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,rtos-trace \ | ||
| 22 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ | ||
| 23 | --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ | ||
| 24 | --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \ | ||
| 25 | --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target xtensa-esp32s2-none-elf \ | ||
| 26 | --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \ | ||
| 27 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ | ||
| 28 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ | ||
| 29 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ | ||
| 30 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \ | ||
| 31 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ | ||
| 32 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ | ||
| 33 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ | ||
| 34 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ | ||
| 35 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ | ||
| 36 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ | ||
| 37 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \ | ||
| 38 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ | ||
| @@ -10,352 +10,21 @@ if ! command -v cargo-batch &> /dev/null; then | |||
| 10 | exit 1 | 10 | exit 1 |
| 11 | fi | 11 | fi |
| 12 | 12 | ||
| 13 | if ! command -v cargo-embassy-devtool &> /dev/null; then | ||
| 14 | echo "cargo-embassy-devtool could not be found. Install it with the following command:" | ||
| 15 | echo "" | ||
| 16 | echo " cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked" | ||
| 17 | echo "" | ||
| 18 | exit 1 | ||
| 19 | fi | ||
| 20 | |||
| 13 | export RUSTFLAGS=-Dwarnings | 21 | export RUSTFLAGS=-Dwarnings |
| 14 | export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info | 22 | export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info |
| 15 | if [[ -z "${CARGO_TARGET_DIR}" ]]; then | 23 | if [[ -z "${CARGO_TARGET_DIR}" ]]; then |
| 16 | export CARGO_TARGET_DIR=target_ci | 24 | export CARGO_TARGET_DIR=target_ci |
| 17 | fi | 25 | fi |
| 18 | 26 | ||
| 19 | TARGET=$(rustc -vV | sed -n 's|host: ||p') | 27 | cargo embassy-devtool build |
| 20 | |||
| 21 | BUILD_EXTRA="" | ||
| 22 | if [ $TARGET = "x86_64-unknown-linux-gnu" ] || [ $TARGET = "aarch64-unknown-linux-gnu" ]; then | ||
| 23 | BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --artifact-dir out/examples/std" | ||
| 24 | fi | ||
| 25 | |||
| 26 | # CI intentionally does not use -eabihf on thumbv7em to minimize dep compile time. | ||
| 27 | cargo batch \ | ||
| 28 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 29 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ | ||
| 30 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ | ||
| 31 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | ||
| 32 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \ | ||
| 33 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \ | ||
| 34 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,metadata-name \ | ||
| 35 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace \ | ||
| 36 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \ | ||
| 37 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace,rtos-trace \ | ||
| 38 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ | ||
| 39 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ | ||
| 40 | --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \ | ||
| 41 | --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \ | ||
| 42 | --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \ | ||
| 43 | --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ | ||
| 44 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ | ||
| 45 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ | ||
| 46 | --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 47 | --- build --release --manifest-path embassy-embedded-hal/Cargo.toml --target thumbv7em-none-eabi --features time \ | ||
| 48 | --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | ||
| 49 | --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ | ||
| 50 | --- build --release --manifest-path embassy-time/Cargo.toml --features defmt,std \ | ||
| 51 | --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi \ | ||
| 52 | --- build --release --manifest-path embassy-time-queue-utils/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \ | ||
| 53 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ | ||
| 54 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ | ||
| 55 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ | ||
| 56 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \ | ||
| 57 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ | ||
| 58 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ | ||
| 59 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ | ||
| 60 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ | ||
| 61 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ | ||
| 62 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ | ||
| 63 | --- 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 \ | ||
| 64 | --- 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 \ | ||
| 65 | --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt633s,defmt,unstable-pac,time,time-driver-rtc \ | ||
| 66 | --- build --release --manifest-path embassy-imxrt/Cargo.toml --target thumbv8m.main-none-eabihf --features mimxrt685s,defmt,unstable-pac,time,time-driver-rtc \ | ||
| 67 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,gpiote,time,time-driver-rtc1 \ | ||
| 68 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time,time-driver-rtc1 \ | ||
| 69 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time,time-driver-rtc1 \ | ||
| 70 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time,time-driver-rtc1 \ | ||
| 71 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time,time-driver-rtc1 \ | ||
| 72 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time,time-driver-rtc1,reset-pin-as-gpio \ | ||
| 73 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time,time-driver-rtc1,nfc-pins-as-gpio \ | ||
| 74 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time,time-driver-rtc1 \ | ||
| 75 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time,time-driver-rtc1 \ | ||
| 76 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time,time-driver-rtc1 \ | ||
| 77 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time,time-driver-rtc1 \ | ||
| 78 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time,time-driver-rtc1 \ | ||
| 79 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-s,gpiote,time,time-driver-rtc1 \ | ||
| 80 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-ns,gpiote,time,time-driver-rtc1 \ | ||
| 81 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time \ | ||
| 82 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \ | ||
| 83 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time,time-driver-rtc1 \ | ||
| 84 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time \ | ||
| 85 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time-driver-rtc1 \ | ||
| 86 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time,time-driver-rtc1 \ | ||
| 87 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time \ | ||
| 88 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time-driver-rtc1 \ | ||
| 89 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time,time-driver-rtc1 \ | ||
| 90 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \ | ||
| 91 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \ | ||
| 92 | --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \ | ||
| 93 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt,rp2040 \ | ||
| 94 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log,rp2040 \ | ||
| 95 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics,rp2040 \ | ||
| 96 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio,rp2040 \ | ||
| 97 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,defmt,rp235xa \ | ||
| 98 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,log,rp235xa \ | ||
| 99 | --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,rp235xa,binary-info \ | ||
| 100 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \ | ||
| 101 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \ | ||
| 102 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \ | ||
| 103 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \ | ||
| 104 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,time \ | ||
| 105 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time-driver-any,time \ | ||
| 106 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time \ | ||
| 107 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,time \ | ||
| 108 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti \ | ||
| 109 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt \ | ||
| 110 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,single-bank,defmt \ | ||
| 111 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb,defmt,exti,time-driver-any,time \ | ||
| 112 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c051f6,defmt,exti,time-driver-any,time \ | ||
| 113 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c091gb,defmt,exti,time-driver-any,time \ | ||
| 114 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c092rc,defmt,exti,time-driver-any,time \ | ||
| 115 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ | ||
| 116 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ | ||
| 117 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ | ||
| 118 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \ | ||
| 119 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \ | ||
| 120 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \ | ||
| 121 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \ | ||
| 122 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \ | ||
| 123 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \ | ||
| 124 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \ | ||
| 125 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \ | ||
| 126 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any \ | ||
| 127 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any \ | ||
| 128 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any,time \ | ||
| 129 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any,time \ | ||
| 130 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any,time \ | ||
| 131 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \ | ||
| 132 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,exti,time-driver-any,time \ | ||
| 133 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f412zg,defmt,exti,time-driver-any,time \ | ||
| 134 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f413vh,defmt,exti,time-driver-any,time \ | ||
| 135 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f415zg,defmt,exti,time-driver-any,time \ | ||
| 136 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f417zg,defmt,exti,time-driver-any,time \ | ||
| 137 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f423zh,defmt,exti,time-driver-any,time \ | ||
| 138 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f427zi,defmt,exti,time-driver-any,time \ | ||
| 139 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \ | ||
| 140 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f437zi,log,exti,time-driver-any,time \ | ||
| 141 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f439zi,defmt,exti,time-driver-any,time \ | ||
| 142 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446ze,defmt,exti,time-driver-any,time \ | ||
| 143 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f469zi,defmt,exti,time-driver-any,time \ | ||
| 144 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f479zi,defmt,exti,time-driver-any,time \ | ||
| 145 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f730i8,defmt,exti,time-driver-any,time \ | ||
| 146 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi,defmt,exti,time-driver-any,time \ | ||
| 147 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h735zg,defmt,exti,time-driver-any,time \ | ||
| 148 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time,split-pc2,split-pc3 \ | ||
| 149 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \ | ||
| 150 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \ | ||
| 151 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \ | ||
| 152 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7r3z8,defmt,exti,time-driver-tim1,time \ | ||
| 153 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7r7a8,defmt,exti,time-driver-tim1,time \ | ||
| 154 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s3a8,defmt,exti,time-driver-tim1,time \ | ||
| 155 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s7z8,defmt,exti,time-driver-tim1,time \ | ||
| 156 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \ | ||
| 157 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \ | ||
| 158 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \ | ||
| 159 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb15cc,defmt,exti,time-driver-any,time \ | ||
| 160 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \ | ||
| 161 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l041f6,defmt,exti,time-driver-any,time \ | ||
| 162 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l051k8,defmt,exti,time-driver-any,time \ | ||
| 163 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073cz,defmt,exti,time-driver-any,low-power,time \ | ||
| 164 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \ | ||
| 165 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \ | ||
| 166 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \ | ||
| 167 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \ | ||
| 168 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0b0ce,defmt,exti,time-driver-any,time \ | ||
| 169 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \ | ||
| 170 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \ | ||
| 171 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,dual-bank,defmt,exti,time-driver-any,low-power,time \ | ||
| 172 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \ | ||
| 173 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \ | ||
| 174 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g431kb,defmt,exti,time-driver-any,time \ | ||
| 175 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,dual-bank,defmt,exti,time-driver-any,time \ | ||
| 176 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \ | ||
| 177 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \ | ||
| 178 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100c4,defmt,exti,time-driver-any,time \ | ||
| 179 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \ | ||
| 180 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h523cc,defmt,exti,time-driver-any,time \ | ||
| 181 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ | ||
| 182 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ | ||
| 183 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ | ||
| 184 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba62cg,defmt,exti,time-driver-any,time \ | ||
| 185 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba65ri,defmt,exti,time-driver-any,low-power,time \ | ||
| 186 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ | ||
| 187 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ | ||
| 188 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ | ||
| 189 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg,defmt,exti,time-driver-any,low-power,time \ | ||
| 190 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ | ||
| 191 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ | ||
| 192 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ | ||
| 193 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ | ||
| 194 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,rt,defmt,time-driver-pit \ | ||
| 195 | --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1062,rt,defmt,time-driver-pit \ | ||
| 196 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ | ||
| 197 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ | ||
| 198 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ | ||
| 199 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1306rhb,defmt,time-driver-any \ | ||
| 200 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l2228pn,defmt,time-driver-any \ | ||
| 201 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1345dgs28,defmt,time-driver-any \ | ||
| 202 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1106dgs28,defmt,time-driver-any \ | ||
| 203 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1228pm,defmt,time-driver-any \ | ||
| 204 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1107ycj,defmt,time-driver-any \ | ||
| 205 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3105rhb,defmt,time-driver-any \ | ||
| 206 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1505pt,defmt,time-driver-any \ | ||
| 207 | --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1519rhb,defmt,time-driver-any \ | ||
| 208 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ | ||
| 209 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ | ||
| 210 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ | ||
| 211 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs' \ | ||
| 212 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \ | ||
| 213 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs,bluetooth' \ | ||
| 214 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs,bluetooth' \ | ||
| 215 | --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \ | ||
| 216 | --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \ | ||
| 217 | --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ | ||
| 218 | --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf5340-app-s \ | ||
| 219 | --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ | ||
| 220 | --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \ | ||
| 221 | --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \ | ||
| 222 | --- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \ | ||
| 223 | --- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/rp2040 \ | ||
| 224 | --- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \ | ||
| 225 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --no-default-features \ | ||
| 226 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi \ | ||
| 227 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features log \ | ||
| 228 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | ||
| 229 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features usbd-hid \ | ||
| 230 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-interface-count-1 \ | ||
| 231 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-interface-count-8 \ | ||
| 232 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-handler-count-8 \ | ||
| 233 | --- build --release --manifest-path embassy-usb/Cargo.toml --target thumbv6m-none-eabi --features max-handler-count-8 \ | ||
| 234 | --- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 235 | --- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 236 | --- build --release --manifest-path docs/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 237 | --- build --release --manifest-path docs/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 238 | --- build --release --manifest-path docs/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ | ||
| 239 | --- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52810 \ | ||
| 240 | --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840 \ | ||
| 241 | --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf5340 \ | ||
| 242 | --- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf54l15 \ | ||
| 243 | --- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9160 \ | ||
| 244 | --- build --release --manifest-path examples/nrf9151/s/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9151/s \ | ||
| 245 | --- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9151/ns \ | ||
| 246 | --- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/nrf51 \ | ||
| 247 | --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/rp \ | ||
| 248 | --- build --release --manifest-path examples/rp235x/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/rp235x \ | ||
| 249 | --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32f0 \ | ||
| 250 | --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32f1 \ | ||
| 251 | --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32f2 \ | ||
| 252 | --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f3 \ | ||
| 253 | --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f334 \ | ||
| 254 | --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f4 \ | ||
| 255 | --- build --release --manifest-path examples/stm32f469/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f469 \ | ||
| 256 | --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32f7 \ | ||
| 257 | --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32c0 \ | ||
| 258 | --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32g0 \ | ||
| 259 | --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32g4 \ | ||
| 260 | --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32h5 \ | ||
| 261 | --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7 \ | ||
| 262 | --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7b0 \ | ||
| 263 | --- build --release --manifest-path examples/stm32h723/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h723 \ | ||
| 264 | --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h735 \ | ||
| 265 | --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm4 \ | ||
| 266 | --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h755cm7 \ | ||
| 267 | --- build --release --manifest-path examples/stm32h7rs/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32h7rs \ | ||
| 268 | --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32l0 \ | ||
| 269 | --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --artifact-dir out/examples/stm32l1 \ | ||
| 270 | --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32l4 \ | ||
| 271 | --- build --release --manifest-path examples/stm32l432/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32l432 \ | ||
| 272 | --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32l5 \ | ||
| 273 | --- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/stm32u0 \ | ||
| 274 | --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32u5 \ | ||
| 275 | --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wb \ | ||
| 276 | --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ | ||
| 277 | --- build --release --manifest-path examples/stm32wba6/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba6 \ | ||
| 278 | --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ | ||
| 279 | --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ | ||
| 280 | --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \ | ||
| 281 | --- build --release --manifest-path examples/mimxrt1062-evk/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1062-evk \ | ||
| 282 | --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ | ||
| 283 | --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ | ||
| 284 | --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ | ||
| 285 | --- build --release --manifest-path examples/mspm0l2228/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l2228 \ | ||
| 286 | --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --artifact-dir out/examples/boot/nrf52840 \ | ||
| 287 | --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --artifact-dir out/examples/boot/nrf9160 \ | ||
| 288 | --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns,skip-include --artifact-dir out/examples/boot/nrf9120 \ | ||
| 289 | --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns,skip-include --artifact-dir out/examples/boot/nrf9151 \ | ||
| 290 | --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns,skip-include --artifact-dir out/examples/boot/nrf9161 \ | ||
| 291 | --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --artifact-dir out/examples/boot/rp \ | ||
| 292 | --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32f3 \ | ||
| 293 | --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32f7 \ | ||
| 294 | --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32h7 \ | ||
| 295 | --- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l0 \ | ||
| 296 | --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l1 \ | ||
| 297 | --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l4 \ | ||
| 298 | --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32wl \ | ||
| 299 | --- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/boot/stm32wb-dfu \ | ||
| 300 | --- build --release --manifest-path examples/boot/application/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/boot/stm32wba-dfu \ | ||
| 301 | --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ | ||
| 302 | --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ | ||
| 303 | --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \ | ||
| 304 | --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \ | ||
| 305 | --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \ | ||
| 306 | --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ | ||
| 307 | --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \ | ||
| 308 | --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \ | ||
| 309 | --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg,verify \ | ||
| 310 | --- build --release --manifest-path examples/boot/bootloader/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-stm32/stm32wba65ri,verify \ | ||
| 311 | --- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h743zi \ | ||
| 312 | --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --artifact-dir out/examples/wasm \ | ||
| 313 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --artifact-dir out/tests/stm32f103c8 \ | ||
| 314 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --artifact-dir out/tests/stm32f429zi \ | ||
| 315 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --artifact-dir out/tests/stm32f446re \ | ||
| 316 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ | ||
| 317 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --artifact-dir out/tests/stm32g071rb \ | ||
| 318 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --artifact-dir out/tests/stm32c031c6 \ | ||
| 319 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c071rb --artifact-dir out/tests/stm32c071rb \ | ||
| 320 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --artifact-dir out/tests/stm32h755zi \ | ||
| 321 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --artifact-dir out/tests/stm32h753zi \ | ||
| 322 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --artifact-dir out/tests/stm32h7a3zi \ | ||
| 323 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --artifact-dir out/tests/stm32wb55rg \ | ||
| 324 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h563zi --artifact-dir out/tests/stm32h563zi \ | ||
| 325 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u585ai --artifact-dir out/tests/stm32u585ai \ | ||
| 326 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5a5zj --artifact-dir out/tests/stm32u5a5zj \ | ||
| 327 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba52cg --artifact-dir out/tests/stm32wba52cg \ | ||
| 328 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --artifact-dir out/tests/stm32l073rz \ | ||
| 329 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --artifact-dir out/tests/stm32l152re \ | ||
| 330 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --artifact-dir out/tests/stm32l4a6zg \ | ||
| 331 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r5zi --artifact-dir out/tests/stm32l4r5zi \ | ||
| 332 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze --artifact-dir out/tests/stm32l552ze \ | ||
| 333 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f767zi --artifact-dir out/tests/stm32f767zi \ | ||
| 334 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --artifact-dir out/tests/stm32f207zg \ | ||
| 335 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --artifact-dir out/tests/stm32f303ze \ | ||
| 336 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --artifact-dir out/tests/stm32l496zg \ | ||
| 337 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --artifact-dir out/tests/stm32wl55jc \ | ||
| 338 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7s3l8 --artifact-dir out/tests/stm32h7s3l8 \ | ||
| 339 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --artifact-dir out/tests/stm32f091rc \ | ||
| 340 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --artifact-dir out/tests/stm32h503rb \ | ||
| 341 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc --artifact-dir out/tests/stm32u083rc \ | ||
| 342 | --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features rp2040 --artifact-dir out/tests/rpi-pico \ | ||
| 343 | --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv8m.main-none-eabihf --features rp235xb --artifact-dir out/tests/pimoroni-pico-plus-2 \ | ||
| 344 | --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51422 --artifact-dir out/tests/nrf51422-dk \ | ||
| 345 | --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832 --artifact-dir out/tests/nrf52832-dk \ | ||
| 346 | --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833 --artifact-dir out/tests/nrf52833-dk \ | ||
| 347 | --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840 --artifact-dir out/tests/nrf52840-dk \ | ||
| 348 | --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340 --artifact-dir out/tests/nrf5340-dk \ | ||
| 349 | --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160 --artifact-dir out/tests/nrf9160-dk \ | ||
| 350 | --- build --release --manifest-path tests/mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507 --artifact-dir out/tests/mspm0g3507 \ | ||
| 351 | --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ | ||
| 352 | $BUILD_EXTRA | ||
| 353 | |||
| 354 | |||
| 355 | # MSPM0C1104 must be built seperately since cargo batch does not consider env vars set in `.cargo/config.toml`. | ||
| 356 | # Since the target has 1KB of ram, we need to limit defmt's buffer size. | ||
| 357 | DEFMT_RTT_BUFFER_SIZE="72" cargo batch \ | ||
| 358 | --- build --release --manifest-path examples/mspm0c1104/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0c1104 \ | ||
| 359 | 28 | ||
| 360 | # temporarily disabled, these boards are dead. | 29 | # temporarily disabled, these boards are dead. |
| 361 | rm -rf out/tests/stm32f103c8 | 30 | rm -rf out/tests/stm32f103c8 |
| @@ -397,8 +66,10 @@ rm out/tests/pimoroni-pico-plus-2/pwm | |||
| 397 | rm out/tests/rpi-pico/pwm | 66 | rm out/tests/rpi-pico/pwm |
| 398 | rm out/tests/rpi-pico/cyw43-perf | 67 | rm out/tests/rpi-pico/cyw43-perf |
| 399 | 68 | ||
| 400 | # tests are implemented but the HIL test farm doesn't actually have this board yet | 69 | # tests are implemented but the HIL test farm doesn't actually have these boards, yet |
| 401 | rm -rf out/tests/stm32c071rb | 70 | rm -rf out/tests/stm32c071rb |
| 71 | rm -rf out/tests/stm32f100rd | ||
| 72 | rm -rf out/tests/stm32f107vc | ||
| 402 | 73 | ||
| 403 | if [[ -z "${TELEPROBE_TOKEN-}" ]]; then | 74 | if [[ -z "${TELEPROBE_TOKEN-}" ]]; then |
| 404 | echo No teleprobe token found, skipping running HIL tests | 75 | echo No teleprobe token found, skipping running HIL tests |
diff --git a/docs/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc index 620794c31..cedbedada 100644 --- a/docs/pages/embassy_in_the_wild.adoc +++ b/docs/pages/embassy_in_the_wild.adoc | |||
| @@ -2,6 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | 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/pages/embassy_in_the_wild.adoc[add more]! | 3 | 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/pages/embassy_in_the_wild.adoc[add more]! |
| 4 | 4 | ||
| 5 | _newer entries at the top_ | ||
| 6 | |||
| 7 | * link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation] | ||
| 8 | ** A `no_std` Raspberry Pi Pico clock demonstrating layered Embassy tasks (Display->Blinker->Clock) for clean separation of multiplexing, blinking, and UI logic. Features single-button HH:MM/MM:SS time-set UI, heapless data structures, and a Renode emulator for hardware-free testing. See link:https://medium.com/@carlmkadie/how-rust-embassy-shine-on-embedded-devices-part-2-aad1adfccf72[this article] for details. | ||
| 5 | * link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2] | 9 | * link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2] |
| 6 | ** A hobbyist project building a tracked robot with basic autonomous and manual drive mode. | 10 | ** A hobbyist project building a tracked robot with basic autonomous and manual drive mode. |
| 7 | * link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock] | 11 | * link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock] |
diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml index f803e6644..9c2b40d03 100644 --- a/embassy-executor-macros/Cargo.toml +++ b/embassy-executor-macros/Cargo.toml | |||
| @@ -23,3 +23,4 @@ proc-macro = true | |||
| 23 | 23 | ||
| 24 | [features] | 24 | [features] |
| 25 | nightly = [] | 25 | nightly = [] |
| 26 | metadata-name = [] | ||
diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index f043ff7f6..dc470e51c 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs | |||
| @@ -170,7 +170,7 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe | |||
| 170 | let f_body = f.body; | 170 | let f_body = f.body; |
| 171 | let out = &f.sig.output; | 171 | let out = &f.sig.output; |
| 172 | 172 | ||
| 173 | let name_main_task = if cfg!(feature = "rtos-trace") { | 173 | let name_main_task = if cfg!(feature = "metadata-name") { |
| 174 | quote!( | 174 | quote!( |
| 175 | main_task.metadata().set_name("main\0"); | 175 | main_task.metadata().set_name("main\0"); |
| 176 | ) | 176 | ) |
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 3e6c180c6..6f079a11a 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md | |||
| @@ -8,9 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Added new metadata API for tasks | 11 | - Added new metadata API for tasks. |
| 12 | - Named main task when rtos-trace feature is enabled. | 12 | - Main task automatically gets a name of `main` when the `metadata-name` feature is enabled. |
| 13 | - Upgraded rtos-trace | 13 | - Upgraded rtos-trace |
| 14 | - Added optional "highest priority" scheduling | ||
| 15 | - Added optional "earliest deadline first" EDF scheduling | ||
| 14 | 16 | ||
| 15 | ## 0.9.1 - 2025-08-31 | 17 | ## 0.9.1 - 2025-08-31 |
| 16 | 18 | ||
| @@ -20,8 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 20 | 22 | ||
| 21 | - Added `extern "Rust" fn __embassy_time_queue_item_from_waker` | 23 | - Added `extern "Rust" fn __embassy_time_queue_item_from_waker` |
| 22 | - Removed `TaskRef::dangling` | 24 | - Removed `TaskRef::dangling` |
| 23 | - Added `embassy_time_queue_utils` as a dependency | 25 | - Added `embassy-executor-timer-queue` as a dependency |
| 24 | - Moved the `TimeQueueItem` struct and `timer-item-payload-size-*` features into embassy-time-queue-utils | 26 | - Moved the `TimeQueueItem` struct and `timer-item-payload-size-*` features (as `timer-item-size-X-words`) into `embassy-executor-timer-queue` |
| 25 | 27 | ||
| 26 | ## 0.8.0 - 2025-07-31 | 28 | ## 0.8.0 - 2025-07-31 |
| 27 | 29 | ||
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index dc423aba2..f6dce5c0e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-executor" | 2 | name = "embassy-executor" |
| 3 | version = "0.9.0" | 3 | version = "0.9.1" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "async/await executor designed for embedded usage" | 6 | description = "async/await executor designed for embedded usage" |
| @@ -24,18 +24,49 @@ build = [ | |||
| 24 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, | 24 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, |
| 25 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, | 25 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, |
| 26 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, | 26 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, |
| 27 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver"]}, | ||
| 28 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority"]}, | ||
| 29 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority", "scheduler-deadline"]}, | ||
| 30 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-deadline"]}, | ||
| 31 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-priority", "scheduler-deadline"]}, | ||
| 32 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline"]}, | ||
| 27 | {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, | 33 | {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, |
| 28 | {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, | 34 | {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, |
| 29 | {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, | 35 | {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, |
| 30 | {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32"]}, | 36 | {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32"]}, |
| 31 | {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"]}, | 37 | {target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"]}, |
| 38 | # Nightly builds | ||
| 39 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly"]}, | ||
| 40 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "log"]}, | ||
| 41 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "defmt"]}, | ||
| 42 | {group = "nightly", target = "thumbv6m-none-eabi", features = ["nightly", "defmt"]}, | ||
| 43 | {group = "nightly", target = "thumbv6m-none-eabi", features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]}, | ||
| 44 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m"]}, | ||
| 45 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-thread"]}, | ||
| 46 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-interrupt"]}, | ||
| 47 | {group = "nightly", target = "thumbv7em-none-eabi", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt"]}, | ||
| 48 | {group = "nightly", target = "riscv32imac-unknown-none-elf", features = ["nightly", "arch-riscv32"]}, | ||
| 49 | {group = "nightly", target = "riscv32imac-unknown-none-elf", features = ["nightly", "arch-riscv32", "executor-thread"]}, | ||
| 50 | {group = "nightly", target = "armv7a-none-eabi", features = ["nightly", "arch-cortex-ar", "executor-thread"]}, | ||
| 51 | {group = "nightly", target = "avr-none", features = ["nightly", "arch-avr", "avr-device/atmega328p"], build-std = ["core", "alloc"], env = { RUSTFLAGS = "-Ctarget-cpu=atmega328p" }}, | ||
| 52 | # Xtensa builds | ||
| 53 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = []}, | ||
| 54 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["log"]}, | ||
| 55 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt"]}, | ||
| 56 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt"]}, | ||
| 57 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "arch-spin", "executor-thread"]}, | ||
| 58 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt", "arch-spin", "executor-thread"]}, | ||
| 59 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s3-none-elf", features = ["defmt", "arch-spin", "executor-thread"]}, | ||
| 60 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin"]}, | ||
| 61 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin", "rtos-trace"]}, | ||
| 62 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["arch-spin", "executor-thread"]}, | ||
| 32 | ] | 63 | ] |
| 33 | 64 | ||
| 34 | 65 | ||
| 35 | [package.metadata.embassy_docs] | 66 | [package.metadata.embassy_docs] |
| 36 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" | 67 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" |
| 37 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" | 68 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" |
| 38 | features = ["defmt"] | 69 | features = ["defmt", "scheduler-deadline", "scheduler-priority"] |
| 39 | flavors = [ | 70 | flavors = [ |
| 40 | { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, | 71 | { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, |
| 41 | { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, | 72 | { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, |
| @@ -46,7 +77,7 @@ flavors = [ | |||
| 46 | [package.metadata.docs.rs] | 77 | [package.metadata.docs.rs] |
| 47 | default-target = "thumbv7em-none-eabi" | 78 | default-target = "thumbv7em-none-eabi" |
| 48 | targets = ["thumbv7em-none-eabi"] | 79 | targets = ["thumbv7em-none-eabi"] |
| 49 | features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] | 80 | features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "scheduler-priority", "embassy-time-driver"] |
| 50 | 81 | ||
| 51 | [dependencies] | 82 | [dependencies] |
| 52 | defmt = { version = "1.0.1", optional = true } | 83 | defmt = { version = "1.0.1", optional = true } |
| @@ -76,6 +107,11 @@ js-sys = { version = "0.3", optional = true } | |||
| 76 | # arch-avr dependencies | 107 | # arch-avr dependencies |
| 77 | avr-device = { version = "0.7.0", optional = true } | 108 | avr-device = { version = "0.7.0", optional = true } |
| 78 | 109 | ||
| 110 | |||
| 111 | [dependencies.cordyceps] | ||
| 112 | version = "0.3.4" | ||
| 113 | features = ["no-cache-pad"] | ||
| 114 | |||
| 79 | [dev-dependencies] | 115 | [dev-dependencies] |
| 80 | critical-section = { version = "1.1", features = ["std"] } | 116 | critical-section = { version = "1.1", features = ["std"] } |
| 81 | trybuild = "1.0" | 117 | trybuild = "1.0" |
| @@ -112,7 +148,7 @@ arch-spin = ["_arch"] | |||
| 112 | #! ### Metadata | 148 | #! ### Metadata |
| 113 | 149 | ||
| 114 | ## Enable the `name` field in task metadata. | 150 | ## Enable the `name` field in task metadata. |
| 115 | metadata-name = [] | 151 | metadata-name = ["embassy-executor-macros/metadata-name"] |
| 116 | 152 | ||
| 117 | #! ### Executor | 153 | #! ### Executor |
| 118 | 154 | ||
| @@ -123,21 +159,16 @@ executor-interrupt = [] | |||
| 123 | ## Enable tracing hooks | 159 | ## Enable tracing hooks |
| 124 | trace = ["_any_trace"] | 160 | trace = ["_any_trace"] |
| 125 | ## Enable support for rtos-trace framework | 161 | ## Enable support for rtos-trace framework |
| 126 | rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] | 162 | rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "embassy-time-driver"] |
| 127 | _any_trace = [] | 163 | _any_trace = [] |
| 128 | 164 | ||
| 129 | #! ### Timer Item Payload Size | 165 | ## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize |
| 130 | #! Sets the size of the payload for timer items, allowing integrated timer implementors to store | 166 | ## tasks based on the remaining time before their deadline. Adds some overhead. |
| 131 | #! additional data in the timer item. The payload field will be aligned to this value as well. | 167 | scheduler-deadline = [] |
| 132 | #! If these features are not defined, the timer item will contain no payload field. | 168 | |
| 133 | 169 | ## Enable "Highest Priority First" Scheduler. Adds some overhead. | |
| 134 | _timer-item-payload = [] # A size was picked | 170 | scheduler-priority = [] |
| 135 | 171 | ||
| 136 | ## 1 bytes | 172 | ## Enable the embassy_time_driver dependency. |
| 137 | timer-item-payload-size-1 = ["_timer-item-payload"] | 173 | ## This can unlock extra APIs, for example for the `sheduler-deadline` |
| 138 | ## 2 bytes | 174 | embassy-time-driver = ["dep:embassy-time-driver"] |
| 139 | timer-item-payload-size-2 = ["_timer-item-payload"] | ||
| 140 | ## 4 bytes | ||
| 141 | timer-item-payload-size-4 = ["_timer-item-payload"] | ||
| 142 | ## 8 bytes | ||
| 143 | timer-item-payload-size-8 = ["_timer-item-payload"] | ||
diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index f92c9b37c..bc0df0f83 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs | |||
| @@ -1,17 +1,25 @@ | |||
| 1 | #[cfg(feature = "metadata-name")] | 1 | #[cfg(feature = "metadata-name")] |
| 2 | use core::cell::Cell; | 2 | use core::cell::Cell; |
| 3 | use core::future::{poll_fn, Future}; | 3 | use core::future::{poll_fn, Future}; |
| 4 | #[cfg(feature = "scheduler-priority")] | ||
| 5 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 4 | use core::task::Poll; | 6 | use core::task::Poll; |
| 5 | 7 | ||
| 6 | #[cfg(feature = "metadata-name")] | 8 | #[cfg(feature = "metadata-name")] |
| 7 | use critical_section::Mutex; | 9 | use critical_section::Mutex; |
| 8 | 10 | ||
| 9 | use crate::raw; | 11 | use crate::raw; |
| 12 | #[cfg(feature = "scheduler-deadline")] | ||
| 13 | use crate::raw::Deadline; | ||
| 10 | 14 | ||
| 11 | /// Metadata associated with a task. | 15 | /// Metadata associated with a task. |
| 12 | pub struct Metadata { | 16 | pub struct Metadata { |
| 13 | #[cfg(feature = "metadata-name")] | 17 | #[cfg(feature = "metadata-name")] |
| 14 | name: Mutex<Cell<Option<&'static str>>>, | 18 | name: Mutex<Cell<Option<&'static str>>>, |
| 19 | #[cfg(feature = "scheduler-priority")] | ||
| 20 | priority: AtomicU8, | ||
| 21 | #[cfg(feature = "scheduler-deadline")] | ||
| 22 | deadline: raw::Deadline, | ||
| 15 | } | 23 | } |
| 16 | 24 | ||
| 17 | impl Metadata { | 25 | impl Metadata { |
| @@ -19,12 +27,26 @@ impl Metadata { | |||
| 19 | Self { | 27 | Self { |
| 20 | #[cfg(feature = "metadata-name")] | 28 | #[cfg(feature = "metadata-name")] |
| 21 | name: Mutex::new(Cell::new(None)), | 29 | name: Mutex::new(Cell::new(None)), |
| 30 | #[cfg(feature = "scheduler-priority")] | ||
| 31 | priority: AtomicU8::new(0), | ||
| 32 | // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This | ||
| 33 | // will be lazily initalized in `initialize_impl` | ||
| 34 | #[cfg(feature = "scheduler-deadline")] | ||
| 35 | deadline: raw::Deadline::new_unset(), | ||
| 22 | } | 36 | } |
| 23 | } | 37 | } |
| 24 | 38 | ||
| 25 | pub(crate) fn reset(&self) { | 39 | pub(crate) fn reset(&self) { |
| 26 | #[cfg(feature = "metadata-name")] | 40 | #[cfg(feature = "metadata-name")] |
| 27 | critical_section::with(|cs| self.name.borrow(cs).set(None)); | 41 | critical_section::with(|cs| self.name.borrow(cs).set(None)); |
| 42 | |||
| 43 | #[cfg(feature = "scheduler-priority")] | ||
| 44 | self.set_priority(0); | ||
| 45 | |||
| 46 | // By default, deadlines are set to the maximum value, so that any task WITH | ||
| 47 | // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline | ||
| 48 | #[cfg(feature = "scheduler-deadline")] | ||
| 49 | self.unset_deadline(); | ||
| 28 | } | 50 | } |
| 29 | 51 | ||
| 30 | /// Get the metadata for the current task. | 52 | /// Get the metadata for the current task. |
| @@ -52,4 +74,75 @@ impl Metadata { | |||
| 52 | pub fn set_name(&self, name: &'static str) { | 74 | pub fn set_name(&self, name: &'static str) { |
| 53 | critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) | 75 | critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) |
| 54 | } | 76 | } |
| 77 | |||
| 78 | /// Get this task's priority. | ||
| 79 | #[cfg(feature = "scheduler-priority")] | ||
| 80 | pub fn priority(&self) -> u8 { | ||
| 81 | self.priority.load(Ordering::Relaxed) | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Set this task's priority. | ||
| 85 | #[cfg(feature = "scheduler-priority")] | ||
| 86 | pub fn set_priority(&self, priority: u8) { | ||
| 87 | self.priority.store(priority, Ordering::Relaxed) | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Get this task's deadline. | ||
| 91 | #[cfg(feature = "scheduler-deadline")] | ||
| 92 | pub fn deadline(&self) -> u64 { | ||
| 93 | self.deadline.instant_ticks() | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Set this task's deadline. | ||
| 97 | /// | ||
| 98 | /// This method does NOT check whether the deadline has already passed. | ||
| 99 | #[cfg(feature = "scheduler-deadline")] | ||
| 100 | pub fn set_deadline(&self, instant_ticks: u64) { | ||
| 101 | self.deadline.set(instant_ticks); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Remove this task's deadline. | ||
| 105 | /// This brings it back to the defaul where it's not scheduled ahead of other tasks. | ||
| 106 | #[cfg(feature = "scheduler-deadline")] | ||
| 107 | pub fn unset_deadline(&self) { | ||
| 108 | self.deadline.set(Deadline::UNSET_TICKS); | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Set this task's deadline `duration_ticks` in the future from when | ||
| 112 | /// this future is polled. This deadline is saturated to the max tick value. | ||
| 113 | /// | ||
| 114 | /// Analogous to `Timer::after`. | ||
| 115 | #[cfg(all(feature = "scheduler-deadline", feature = "embassy-time-driver"))] | ||
| 116 | pub fn set_deadline_after(&self, duration_ticks: u64) { | ||
| 117 | let now = embassy_time_driver::now(); | ||
| 118 | |||
| 119 | // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave | ||
| 120 | // it for now, we can probably make this wrapping_add for performance | ||
| 121 | // reasons later. | ||
| 122 | let deadline = now.saturating_add(duration_ticks); | ||
| 123 | |||
| 124 | self.set_deadline(deadline); | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Set the this task's deadline `increment_ticks` from the previous deadline. | ||
| 128 | /// | ||
| 129 | /// This deadline is saturated to the max tick value. | ||
| 130 | /// | ||
| 131 | /// Note that by default (unless otherwise set), tasks start life with the deadline | ||
| 132 | /// not set, which means this method will have no effect. | ||
| 133 | /// | ||
| 134 | /// Analogous to one increment of `Ticker::every().next()`. | ||
| 135 | /// | ||
| 136 | /// Returns the deadline that was set. | ||
| 137 | #[cfg(feature = "scheduler-deadline")] | ||
| 138 | pub fn increment_deadline(&self, duration_ticks: u64) { | ||
| 139 | let last = self.deadline(); | ||
| 140 | |||
| 141 | // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave | ||
| 142 | // it for now, we can probably make this wrapping_add for performance | ||
| 143 | // reasons later. | ||
| 144 | let deadline = last.saturating_add(duration_ticks); | ||
| 145 | |||
| 146 | self.set_deadline(deadline); | ||
| 147 | } | ||
| 55 | } | 148 | } |
diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs new file mode 100644 index 000000000..cc89fadb0 --- /dev/null +++ b/embassy-executor/src/raw/deadline.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | use core::sync::atomic::{AtomicU32, Ordering}; | ||
| 2 | |||
| 3 | /// A type for interacting with the deadline of the current task | ||
| 4 | /// | ||
| 5 | /// Requires the `scheduler-deadline` feature. | ||
| 6 | /// | ||
| 7 | /// Note: Interacting with the deadline should be done locally in a task. | ||
| 8 | /// In theory you could try to set or read the deadline from another task, | ||
| 9 | /// but that will result in weird (though not unsound) behavior. | ||
| 10 | pub(crate) struct Deadline { | ||
| 11 | instant_ticks_hi: AtomicU32, | ||
| 12 | instant_ticks_lo: AtomicU32, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl Deadline { | ||
| 16 | pub(crate) const fn new(instant_ticks: u64) -> Self { | ||
| 17 | Self { | ||
| 18 | instant_ticks_hi: AtomicU32::new((instant_ticks >> 32) as u32), | ||
| 19 | instant_ticks_lo: AtomicU32::new(instant_ticks as u32), | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | pub(crate) const fn new_unset() -> Self { | ||
| 24 | Self::new(Self::UNSET_TICKS) | ||
| 25 | } | ||
| 26 | |||
| 27 | pub(crate) fn set(&self, instant_ticks: u64) { | ||
| 28 | self.instant_ticks_hi | ||
| 29 | .store((instant_ticks >> 32) as u32, Ordering::Relaxed); | ||
| 30 | self.instant_ticks_lo.store(instant_ticks as u32, Ordering::Relaxed); | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Deadline value in ticks, same time base and ticks as `embassy-time` | ||
| 34 | pub(crate) fn instant_ticks(&self) -> u64 { | ||
| 35 | let hi = self.instant_ticks_hi.load(Ordering::Relaxed) as u64; | ||
| 36 | let lo = self.instant_ticks_lo.load(Ordering::Relaxed) as u64; | ||
| 37 | |||
| 38 | (hi << 32) | lo | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Sentinel value representing an "unset" deadline, which has lower priority | ||
| 42 | /// than any other set deadline value | ||
| 43 | pub(crate) const UNSET_TICKS: u64 = u64::MAX; | ||
| 44 | } | ||
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 4280c5750..9f36c60bc 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -7,8 +7,6 @@ | |||
| 7 | //! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe | 7 | //! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe |
| 8 | //! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe. | 8 | //! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe. |
| 9 | 9 | ||
| 10 | #[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")] | ||
| 11 | #[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")] | ||
| 12 | mod run_queue; | 10 | mod run_queue; |
| 13 | 11 | ||
| 14 | #[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")] | 12 | #[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")] |
| @@ -28,6 +26,9 @@ pub(crate) mod util; | |||
| 28 | #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] | 26 | #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] |
| 29 | mod waker; | 27 | mod waker; |
| 30 | 28 | ||
| 29 | #[cfg(feature = "scheduler-deadline")] | ||
| 30 | mod deadline; | ||
| 31 | |||
| 31 | use core::future::Future; | 32 | use core::future::Future; |
| 32 | use core::marker::PhantomData; | 33 | use core::marker::PhantomData; |
| 33 | use core::mem; | 34 | use core::mem; |
| @@ -38,6 +39,8 @@ use core::sync::atomic::AtomicPtr; | |||
| 38 | use core::sync::atomic::Ordering; | 39 | use core::sync::atomic::Ordering; |
| 39 | use core::task::{Context, Poll, Waker}; | 40 | use core::task::{Context, Poll, Waker}; |
| 40 | 41 | ||
| 42 | #[cfg(feature = "scheduler-deadline")] | ||
| 43 | pub(crate) use deadline::Deadline; | ||
| 41 | use embassy_executor_timer_queue::TimerQueueItem; | 44 | use embassy_executor_timer_queue::TimerQueueItem; |
| 42 | #[cfg(feature = "arch-avr")] | 45 | #[cfg(feature = "arch-avr")] |
| 43 | use portable_atomic::AtomicPtr; | 46 | use portable_atomic::AtomicPtr; |
| @@ -96,6 +99,7 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static | |||
| 96 | pub(crate) struct TaskHeader { | 99 | pub(crate) struct TaskHeader { |
| 97 | pub(crate) state: State, | 100 | pub(crate) state: State, |
| 98 | pub(crate) run_queue_item: RunQueueItem, | 101 | pub(crate) run_queue_item: RunQueueItem, |
| 102 | |||
| 99 | pub(crate) executor: AtomicPtr<SyncExecutor>, | 103 | pub(crate) executor: AtomicPtr<SyncExecutor>, |
| 100 | poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, | 104 | poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, |
| 101 | 105 | ||
diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs new file mode 100644 index 000000000..b8b052310 --- /dev/null +++ b/embassy-executor/src/raw/run_queue.rs | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | use core::ptr::{addr_of_mut, NonNull}; | ||
| 2 | |||
| 3 | use cordyceps::sorted_list::Links; | ||
| 4 | use cordyceps::Linked; | ||
| 5 | #[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))] | ||
| 6 | use cordyceps::SortedList; | ||
| 7 | |||
| 8 | #[cfg(target_has_atomic = "ptr")] | ||
| 9 | type TransferStack<T> = cordyceps::TransferStack<T>; | ||
| 10 | |||
| 11 | #[cfg(not(target_has_atomic = "ptr"))] | ||
| 12 | type TransferStack<T> = MutexTransferStack<T>; | ||
| 13 | |||
| 14 | use super::{TaskHeader, TaskRef}; | ||
| 15 | |||
| 16 | /// Use `cordyceps::sorted_list::Links` as the singly linked list | ||
| 17 | /// for RunQueueItems. | ||
| 18 | pub(crate) type RunQueueItem = Links<TaskHeader>; | ||
| 19 | |||
| 20 | /// Implements the `Linked` trait, allowing for singly linked list usage | ||
| 21 | /// of any of cordyceps' `TransferStack` (used for the atomic runqueue), | ||
| 22 | /// `SortedList` (used with the DRS scheduler), or `Stack`, which is | ||
| 23 | /// popped atomically from the `TransferStack`. | ||
| 24 | unsafe impl Linked<Links<TaskHeader>> for TaskHeader { | ||
| 25 | type Handle = TaskRef; | ||
| 26 | |||
| 27 | // Convert a TaskRef into a TaskHeader ptr | ||
| 28 | fn into_ptr(r: TaskRef) -> NonNull<TaskHeader> { | ||
| 29 | r.ptr | ||
| 30 | } | ||
| 31 | |||
| 32 | // Convert a TaskHeader into a TaskRef | ||
| 33 | unsafe fn from_ptr(ptr: NonNull<TaskHeader>) -> TaskRef { | ||
| 34 | TaskRef { ptr } | ||
| 35 | } | ||
| 36 | |||
| 37 | // Given a pointer to a TaskHeader, obtain a pointer to the Links structure, | ||
| 38 | // which can be used to traverse to other TaskHeader nodes in the linked list | ||
| 39 | unsafe fn links(ptr: NonNull<TaskHeader>) -> NonNull<Links<TaskHeader>> { | ||
| 40 | let ptr: *mut TaskHeader = ptr.as_ptr(); | ||
| 41 | NonNull::new_unchecked(addr_of_mut!((*ptr).run_queue_item)) | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Atomic task queue using a very, very simple lock-free linked-list queue: | ||
| 46 | /// | ||
| 47 | /// To enqueue a task, task.next is set to the old head, and head is atomically set to task. | ||
| 48 | /// | ||
| 49 | /// Dequeuing is done in batches: the queue is emptied by atomically replacing head with | ||
| 50 | /// null. Then the batch is iterated following the next pointers until null is reached. | ||
| 51 | /// | ||
| 52 | /// Note that batches will be iterated in the reverse order as they were enqueued. This is OK | ||
| 53 | /// for our purposes: it can't create fairness problems since the next batch won't run until the | ||
| 54 | /// current batch is completely processed, so even if a task enqueues itself instantly (for example | ||
| 55 | /// by waking its own waker) can't prevent other tasks from running. | ||
| 56 | pub(crate) struct RunQueue { | ||
| 57 | stack: TransferStack<TaskHeader>, | ||
| 58 | } | ||
| 59 | |||
| 60 | impl RunQueue { | ||
| 61 | pub const fn new() -> Self { | ||
| 62 | Self { | ||
| 63 | stack: TransferStack::new(), | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Enqueues an item. Returns true if the queue was empty. | ||
| 68 | /// | ||
| 69 | /// # Safety | ||
| 70 | /// | ||
| 71 | /// `item` must NOT be already enqueued in any queue. | ||
| 72 | #[inline(always)] | ||
| 73 | pub(crate) unsafe fn enqueue(&self, task: TaskRef, _tok: super::state::Token) -> bool { | ||
| 74 | self.stack.push_was_empty( | ||
| 75 | task, | ||
| 76 | #[cfg(not(target_has_atomic = "ptr"))] | ||
| 77 | _tok, | ||
| 78 | ) | ||
| 79 | } | ||
| 80 | |||
| 81 | /// # Standard atomic runqueue | ||
| 82 | /// | ||
| 83 | /// Empty the queue, then call `on_task` for each task that was in the queue. | ||
| 84 | /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue | ||
| 85 | /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. | ||
| 86 | #[cfg(not(any(feature = "scheduler-priority", feature = "scheduler-deadline")))] | ||
| 87 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { | ||
| 88 | let taken = self.stack.take_all(); | ||
| 89 | for taskref in taken { | ||
| 90 | run_dequeue(&taskref); | ||
| 91 | on_task(taskref); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// # Earliest Deadline First Scheduler | ||
| 96 | /// | ||
| 97 | /// This algorithm will loop until all enqueued tasks are processed. | ||
| 98 | /// | ||
| 99 | /// Before polling a task, all currently enqueued tasks will be popped from the | ||
| 100 | /// runqueue, and will be added to the working `sorted` list, a linked-list that | ||
| 101 | /// sorts tasks by their deadline, with nearest deadline items in the front, and | ||
| 102 | /// furthest deadline items in the back. | ||
| 103 | /// | ||
| 104 | /// After popping and sorting all pending tasks, the SOONEST task will be popped | ||
| 105 | /// from the front of the queue, and polled by calling `on_task` on it. | ||
| 106 | /// | ||
| 107 | /// This process will repeat until the local `sorted` queue AND the global | ||
| 108 | /// runqueue are both empty, at which point this function will return. | ||
| 109 | #[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))] | ||
| 110 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { | ||
| 111 | let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| { | ||
| 112 | // compare by priority first | ||
| 113 | #[cfg(feature = "scheduler-priority")] | ||
| 114 | { | ||
| 115 | let lp = lhs.metadata.priority(); | ||
| 116 | let rp = rhs.metadata.priority(); | ||
| 117 | if lp != rp { | ||
| 118 | return lp.cmp(&rp).reverse(); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | // compare deadlines in case of tie. | ||
| 122 | #[cfg(feature = "scheduler-deadline")] | ||
| 123 | { | ||
| 124 | let ld = lhs.metadata.deadline(); | ||
| 125 | let rd = rhs.metadata.deadline(); | ||
| 126 | if ld != rd { | ||
| 127 | return ld.cmp(&rd); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | core::cmp::Ordering::Equal | ||
| 131 | }); | ||
| 132 | |||
| 133 | loop { | ||
| 134 | // For each loop, grab any newly pended items | ||
| 135 | let taken = self.stack.take_all(); | ||
| 136 | |||
| 137 | // Sort these into the list - this is potentially expensive! We do an | ||
| 138 | // insertion sort of new items, which iterates the linked list. | ||
| 139 | // | ||
| 140 | // Something on the order of `O(n * m)`, where `n` is the number | ||
| 141 | // of new tasks, and `m` is the number of already pending tasks. | ||
| 142 | sorted.extend(taken); | ||
| 143 | |||
| 144 | // Pop the task with the SOONEST deadline. If there are no tasks | ||
| 145 | // pending, then we are done. | ||
| 146 | let Some(taskref) = sorted.pop_front() else { | ||
| 147 | return; | ||
| 148 | }; | ||
| 149 | |||
| 150 | // We got one task, mark it as dequeued, and process the task. | ||
| 151 | run_dequeue(&taskref); | ||
| 152 | on_task(taskref); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /// atomic state does not require a cs... | ||
| 158 | #[cfg(target_has_atomic = "ptr")] | ||
| 159 | #[inline(always)] | ||
| 160 | fn run_dequeue(taskref: &TaskRef) { | ||
| 161 | taskref.header().state.run_dequeue(); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// ...while non-atomic state does | ||
| 165 | #[cfg(not(target_has_atomic = "ptr"))] | ||
| 166 | #[inline(always)] | ||
| 167 | fn run_dequeue(taskref: &TaskRef) { | ||
| 168 | critical_section::with(|cs| { | ||
| 169 | taskref.header().state.run_dequeue(cs); | ||
| 170 | }) | ||
| 171 | } | ||
| 172 | |||
| 173 | /// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex | ||
| 174 | #[cfg(not(target_has_atomic = "ptr"))] | ||
| 175 | struct MutexTransferStack<T: Linked<cordyceps::stack::Links<T>>> { | ||
| 176 | inner: critical_section::Mutex<core::cell::UnsafeCell<cordyceps::Stack<T>>>, | ||
| 177 | } | ||
| 178 | |||
| 179 | #[cfg(not(target_has_atomic = "ptr"))] | ||
| 180 | impl<T: Linked<cordyceps::stack::Links<T>>> MutexTransferStack<T> { | ||
| 181 | const fn new() -> Self { | ||
| 182 | Self { | ||
| 183 | inner: critical_section::Mutex::new(core::cell::UnsafeCell::new(cordyceps::Stack::new())), | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | /// Push an item to the transfer stack, returning whether the stack was previously empty | ||
| 188 | fn push_was_empty(&self, item: T::Handle, token: super::state::Token) -> bool { | ||
| 189 | // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access | ||
| 190 | // for the lifetime of the token, but does NOT protect against re-entrant access. | ||
| 191 | // However, we never *return* the reference, nor do we recurse (or call another method | ||
| 192 | // like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the | ||
| 193 | // presence of the critical section is sufficient to guarantee exclusive access to | ||
| 194 | // the `inner` field for the purposes of this function. | ||
| 195 | let inner = unsafe { &mut *self.inner.borrow(token).get() }; | ||
| 196 | let is_empty = inner.is_empty(); | ||
| 197 | inner.push(item); | ||
| 198 | is_empty | ||
| 199 | } | ||
| 200 | |||
| 201 | fn take_all(&self) -> cordyceps::Stack<T> { | ||
| 202 | critical_section::with(|cs| { | ||
| 203 | // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access | ||
| 204 | // for the lifetime of the token, but does NOT protect against re-entrant access. | ||
| 205 | // However, we never *return* the reference, nor do we recurse (or call another method | ||
| 206 | // like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the | ||
| 207 | // presence of the critical section is sufficient to guarantee exclusive access to | ||
| 208 | // the `inner` field for the purposes of this function. | ||
| 209 | let inner = unsafe { &mut *self.inner.borrow(cs).get() }; | ||
| 210 | inner.take_all() | ||
| 211 | }) | ||
| 212 | } | ||
| 213 | } | ||
diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs deleted file mode 100644 index ce511d79a..000000000 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ /dev/null | |||
| @@ -1,88 +0,0 @@ | |||
| 1 | use core::ptr; | ||
| 2 | use core::ptr::NonNull; | ||
| 3 | use core::sync::atomic::{AtomicPtr, Ordering}; | ||
| 4 | |||
| 5 | use super::{TaskHeader, TaskRef}; | ||
| 6 | use crate::raw::util::SyncUnsafeCell; | ||
| 7 | |||
| 8 | pub(crate) struct RunQueueItem { | ||
| 9 | next: SyncUnsafeCell<Option<TaskRef>>, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl RunQueueItem { | ||
| 13 | pub const fn new() -> Self { | ||
| 14 | Self { | ||
| 15 | next: SyncUnsafeCell::new(None), | ||
| 16 | } | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | /// Atomic task queue using a very, very simple lock-free linked-list queue: | ||
| 21 | /// | ||
| 22 | /// To enqueue a task, task.next is set to the old head, and head is atomically set to task. | ||
| 23 | /// | ||
| 24 | /// Dequeuing is done in batches: the queue is emptied by atomically replacing head with | ||
| 25 | /// null. Then the batch is iterated following the next pointers until null is reached. | ||
| 26 | /// | ||
| 27 | /// Note that batches will be iterated in the reverse order as they were enqueued. This is OK | ||
| 28 | /// for our purposes: it can't create fairness problems since the next batch won't run until the | ||
| 29 | /// current batch is completely processed, so even if a task enqueues itself instantly (for example | ||
| 30 | /// by waking its own waker) can't prevent other tasks from running. | ||
| 31 | pub(crate) struct RunQueue { | ||
| 32 | head: AtomicPtr<TaskHeader>, | ||
| 33 | } | ||
| 34 | |||
| 35 | impl RunQueue { | ||
| 36 | pub const fn new() -> Self { | ||
| 37 | Self { | ||
| 38 | head: AtomicPtr::new(ptr::null_mut()), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Enqueues an item. Returns true if the queue was empty. | ||
| 43 | /// | ||
| 44 | /// # Safety | ||
| 45 | /// | ||
| 46 | /// `item` must NOT be already enqueued in any queue. | ||
| 47 | #[inline(always)] | ||
| 48 | pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool { | ||
| 49 | let mut was_empty = false; | ||
| 50 | |||
| 51 | self.head | ||
| 52 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { | ||
| 53 | was_empty = prev.is_null(); | ||
| 54 | unsafe { | ||
| 55 | // safety: the pointer is either null or valid | ||
| 56 | let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())); | ||
| 57 | // safety: there are no concurrent accesses to `next` | ||
| 58 | task.header().run_queue_item.next.set(prev); | ||
| 59 | } | ||
| 60 | Some(task.as_ptr() as *mut _) | ||
| 61 | }) | ||
| 62 | .ok(); | ||
| 63 | |||
| 64 | was_empty | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Empty the queue, then call `on_task` for each task that was in the queue. | ||
| 68 | /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue | ||
| 69 | /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. | ||
| 70 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { | ||
| 71 | // Atomically empty the queue. | ||
| 72 | let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); | ||
| 73 | |||
| 74 | // safety: the pointer is either null or valid | ||
| 75 | let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) }; | ||
| 76 | |||
| 77 | // Iterate the linked list of tasks that were previously in the queue. | ||
| 78 | while let Some(task) = next { | ||
| 79 | // If the task re-enqueues itself, the `next` pointer will get overwritten. | ||
| 80 | // Therefore, first read the next pointer, and only then process the task. | ||
| 81 | // safety: there are no concurrent accesses to `next` | ||
| 82 | next = unsafe { task.header().run_queue_item.next.get() }; | ||
| 83 | |||
| 84 | task.header().state.run_dequeue(); | ||
| 85 | on_task(task); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/embassy-executor/src/raw/run_queue_critical_section.rs b/embassy-executor/src/raw/run_queue_critical_section.rs deleted file mode 100644 index 86c4085ed..000000000 --- a/embassy-executor/src/raw/run_queue_critical_section.rs +++ /dev/null | |||
| @@ -1,74 +0,0 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | |||
| 3 | use critical_section::{CriticalSection, Mutex}; | ||
| 4 | |||
| 5 | use super::TaskRef; | ||
| 6 | |||
| 7 | pub(crate) struct RunQueueItem { | ||
| 8 | next: Mutex<Cell<Option<TaskRef>>>, | ||
| 9 | } | ||
| 10 | |||
| 11 | impl RunQueueItem { | ||
| 12 | pub const fn new() -> Self { | ||
| 13 | Self { | ||
| 14 | next: Mutex::new(Cell::new(None)), | ||
| 15 | } | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | /// Atomic task queue using a very, very simple lock-free linked-list queue: | ||
| 20 | /// | ||
| 21 | /// To enqueue a task, task.next is set to the old head, and head is atomically set to task. | ||
| 22 | /// | ||
| 23 | /// Dequeuing is done in batches: the queue is emptied by atomically replacing head with | ||
| 24 | /// null. Then the batch is iterated following the next pointers until null is reached. | ||
| 25 | /// | ||
| 26 | /// Note that batches will be iterated in the reverse order as they were enqueued. This is OK | ||
| 27 | /// for our purposes: it can't create fairness problems since the next batch won't run until the | ||
| 28 | /// current batch is completely processed, so even if a task enqueues itself instantly (for example | ||
| 29 | /// by waking its own waker) can't prevent other tasks from running. | ||
| 30 | pub(crate) struct RunQueue { | ||
| 31 | head: Mutex<Cell<Option<TaskRef>>>, | ||
| 32 | } | ||
| 33 | |||
| 34 | impl RunQueue { | ||
| 35 | pub const fn new() -> Self { | ||
| 36 | Self { | ||
| 37 | head: Mutex::new(Cell::new(None)), | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Enqueues an item. Returns true if the queue was empty. | ||
| 42 | /// | ||
| 43 | /// # Safety | ||
| 44 | /// | ||
| 45 | /// `item` must NOT be already enqueued in any queue. | ||
| 46 | #[inline(always)] | ||
| 47 | pub(crate) unsafe fn enqueue(&self, task: TaskRef, cs: CriticalSection<'_>) -> bool { | ||
| 48 | let prev = self.head.borrow(cs).replace(Some(task)); | ||
| 49 | task.header().run_queue_item.next.borrow(cs).set(prev); | ||
| 50 | |||
| 51 | prev.is_none() | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Empty the queue, then call `on_task` for each task that was in the queue. | ||
| 55 | /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue | ||
| 56 | /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. | ||
| 57 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { | ||
| 58 | // Atomically empty the queue. | ||
| 59 | let mut next = critical_section::with(|cs| self.head.borrow(cs).take()); | ||
| 60 | |||
| 61 | // Iterate the linked list of tasks that were previously in the queue. | ||
| 62 | while let Some(task) = next { | ||
| 63 | // If the task re-enqueues itself, the `next` pointer will get overwritten. | ||
| 64 | // Therefore, first read the next pointer, and only then process the task. | ||
| 65 | |||
| 66 | critical_section::with(|cs| { | ||
| 67 | next = task.header().run_queue_item.next.borrow(cs).get(); | ||
| 68 | task.header().state.run_dequeue(cs); | ||
| 69 | }); | ||
| 70 | |||
| 71 | on_task(task); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md index eca0defd7..b846f21b1 100644 --- a/embassy-mspm0/CHANGELOG.md +++ b/embassy-mspm0/CHANGELOG.md | |||
| @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - feat: Add I2C Controller (blocking & async) + examples for mspm0l1306, mspm0g3507 (tested MCUs) (#4435) | 11 | - feat: Add I2C Controller (blocking & async) + examples for mspm0l1306, mspm0g3507 (tested MCUs) (#4435) |
| 12 | - fix gpio interrupt not being set for mspm0l110x | 12 | - fix gpio interrupt not being set for mspm0l110x |
| 13 | - feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574) | 13 | - feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574) |
| 14 | - feat: Add MSPM0C1105/C1106 support | ||
| 15 | - feat: Add adc implementation (#4646) | ||
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index 92f7a2655..1b32c4d43 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml | |||
| @@ -69,7 +69,7 @@ cortex-m = "0.7.6" | |||
| 69 | critical-section = "1.2.0" | 69 | critical-section = "1.2.0" |
| 70 | 70 | ||
| 71 | # mspm0-metapac = { version = "" } | 71 | # mspm0-metapac = { version = "" } |
| 72 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-fe17d879548757ca29821da66a1bebf2debd4846" } | 72 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-d7bf3d01ac0780e716a45b0474234d39443dc5cf" } |
| 73 | 73 | ||
| 74 | [build-dependencies] | 74 | [build-dependencies] |
| 75 | proc-macro2 = "1.0.94" | 75 | proc-macro2 = "1.0.94" |
| @@ -77,7 +77,7 @@ quote = "1.0.40" | |||
| 77 | cfg_aliases = "0.2.1" | 77 | cfg_aliases = "0.2.1" |
| 78 | 78 | ||
| 79 | # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } | 79 | # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } |
| 80 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-fe17d879548757ca29821da66a1bebf2debd4846", default-features = false, features = ["metadata"] } | 80 | mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-d7bf3d01ac0780e716a45b0474234d39443dc5cf", default-features = false, features = ["metadata"] } |
| 81 | 81 | ||
| 82 | [features] | 82 | [features] |
| 83 | default = ["rt"] | 83 | default = ["rt"] |
| @@ -159,6 +159,24 @@ mspm0c1104dsg = ["mspm0-metapac/mspm0c1104dsg"] | |||
| 159 | mspm0c1104dyy = ["mspm0-metapac/mspm0c1104dyy"] | 159 | mspm0c1104dyy = ["mspm0-metapac/mspm0c1104dyy"] |
| 160 | mspm0c1104ruk = ["mspm0-metapac/mspm0c1104ruk"] | 160 | mspm0c1104ruk = ["mspm0-metapac/mspm0c1104ruk"] |
| 161 | mspm0c1104ycj = ["mspm0-metapac/mspm0c1104ycj"] | 161 | mspm0c1104ycj = ["mspm0-metapac/mspm0c1104ycj"] |
| 162 | mspm0c1105pt = ["mspm0-metapac/mspm0c1105pt"] | ||
| 163 | mspm0c1105rgz = ["mspm0-metapac/mspm0c1105rgz"] | ||
| 164 | mspm0c1105rhb = ["mspm0-metapac/mspm0c1105rhb"] | ||
| 165 | mspm0c1105dgs32 = ["mspm0-metapac/mspm0c1105dgs32"] | ||
| 166 | mspm0c1105dgs28 = ["mspm0-metapac/mspm0c1105dgs28"] | ||
| 167 | mspm0c1105rge = ["mspm0-metapac/mspm0c1105rge"] | ||
| 168 | mspm0c1105dgs20 = ["mspm0-metapac/mspm0c1105dgs20"] | ||
| 169 | mspm0c1105ruk = ["mspm0-metapac/mspm0c1105ruk"] | ||
| 170 | mspm0c1105zcm = ["mspm0-metapac/mspm0c1105zcm"] | ||
| 171 | mspm0c1106pt = ["mspm0-metapac/mspm0c1106pt"] | ||
| 172 | mspm0c1106rgz = ["mspm0-metapac/mspm0c1106rgz"] | ||
| 173 | mspm0c1106rhb = ["mspm0-metapac/mspm0c1106rhb"] | ||
| 174 | mspm0c1106dgs32 = ["mspm0-metapac/mspm0c1106dgs32"] | ||
| 175 | mspm0c1106dgs28 = ["mspm0-metapac/mspm0c1106dgs28"] | ||
| 176 | mspm0c1106rge = ["mspm0-metapac/mspm0c1106rge"] | ||
| 177 | mspm0c1106dgs20 = ["mspm0-metapac/mspm0c1106dgs20"] | ||
| 178 | mspm0c1106ruk = ["mspm0-metapac/mspm0c1106ruk"] | ||
| 179 | mspm0c1106zcm = ["mspm0-metapac/mspm0c1106zcm"] | ||
| 162 | mspm0g1105dgs28 = ["mspm0-metapac/mspm0g1105dgs28"] | 180 | mspm0g1105dgs28 = ["mspm0-metapac/mspm0g1105dgs28"] |
| 163 | mspm0g1105pm = ["mspm0-metapac/mspm0g1105pm"] | 181 | mspm0g1105pm = ["mspm0-metapac/mspm0g1105pm"] |
| 164 | mspm0g1105pt = ["mspm0-metapac/mspm0g1105pt"] | 182 | mspm0g1105pt = ["mspm0-metapac/mspm0g1105pt"] |
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 256192f8b..d294bc422 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs | |||
| @@ -68,6 +68,7 @@ fn generate_code() { | |||
| 68 | g.extend(generate_pin_trait_impls()); | 68 | g.extend(generate_pin_trait_impls()); |
| 69 | g.extend(generate_groups()); | 69 | g.extend(generate_groups()); |
| 70 | g.extend(generate_dma_channel_count()); | 70 | g.extend(generate_dma_channel_count()); |
| 71 | g.extend(generate_adc_constants(&mut cfgs)); | ||
| 71 | 72 | ||
| 72 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | 73 | let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); |
| 73 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); | 74 | let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); |
| @@ -79,10 +80,14 @@ fn get_chip_cfgs(chip_name: &str) -> Vec<String> { | |||
| 79 | let mut cfgs = Vec::new(); | 80 | let mut cfgs = Vec::new(); |
| 80 | 81 | ||
| 81 | // GPIO on C110x is special as it does not belong to an interrupt group. | 82 | // GPIO on C110x is special as it does not belong to an interrupt group. |
| 82 | if chip_name.starts_with("mspm0c110") || chip_name.starts_with("msps003f") { | 83 | if chip_name.starts_with("mspm0c1103") || chip_name.starts_with("mspm0c1104") || chip_name.starts_with("msps003f") { |
| 83 | cfgs.push("mspm0c110x".to_string()); | 84 | cfgs.push("mspm0c110x".to_string()); |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 87 | if chip_name.starts_with("mspm0c1105") || chip_name.starts_with("mspm0c1106") { | ||
| 88 | cfgs.push("mspm0c1105_c1106".to_string()); | ||
| 89 | } | ||
| 90 | |||
| 86 | // Family ranges (temporary until int groups are generated) | 91 | // Family ranges (temporary until int groups are generated) |
| 87 | // | 92 | // |
| 88 | // TODO: Remove this once int group stuff is generated. | 93 | // TODO: Remove this once int group stuff is generated. |
| @@ -216,6 +221,22 @@ fn generate_dma_channel_count() -> TokenStream { | |||
| 216 | quote! { pub const DMA_CHANNELS: usize = #count; } | 221 | quote! { pub const DMA_CHANNELS: usize = #count; } |
| 217 | } | 222 | } |
| 218 | 223 | ||
| 224 | fn generate_adc_constants(cfgs: &mut CfgSet) -> TokenStream { | ||
| 225 | let vrsel = METADATA.adc_vrsel; | ||
| 226 | let memctl = METADATA.adc_memctl; | ||
| 227 | |||
| 228 | cfgs.declare("adc_neg_vref"); | ||
| 229 | match vrsel { | ||
| 230 | 3 => (), | ||
| 231 | 5 => cfgs.enable("adc_neg_vref"), | ||
| 232 | _ => panic!("Unsupported ADC VRSEL value: {vrsel}"), | ||
| 233 | } | ||
| 234 | quote! { | ||
| 235 | pub const ADC_VRSEL: u8 = #vrsel; | ||
| 236 | pub const ADC_MEMCTL: u8 = #memctl; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 219 | #[derive(Debug, Clone)] | 240 | #[derive(Debug, Clone)] |
| 220 | struct Singleton { | 241 | struct Singleton { |
| 221 | name: String, | 242 | name: String, |
| @@ -537,6 +558,8 @@ fn generate_interrupts() -> TokenStream { | |||
| 537 | pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) { | 558 | pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) { |
| 538 | use crate::interrupt::typelevel::Interrupt; | 559 | use crate::interrupt::typelevel::Interrupt; |
| 539 | 560 | ||
| 561 | // This is empty for C1105/6 | ||
| 562 | #[allow(unused_unsafe)] | ||
| 540 | unsafe { | 563 | unsafe { |
| 541 | #(#group_interrupt_enables)* | 564 | #(#group_interrupt_enables)* |
| 542 | } | 565 | } |
| @@ -555,6 +578,7 @@ fn generate_peripheral_instances() -> TokenStream { | |||
| 555 | "uart" => Some(quote! { impl_uart_instance!(#peri); }), | 578 | "uart" => Some(quote! { impl_uart_instance!(#peri); }), |
| 556 | "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), | 579 | "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), |
| 557 | "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), | 580 | "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), |
| 581 | "adc" => Some(quote! { impl_adc_instance!(#peri); }), | ||
| 558 | _ => None, | 582 | _ => None, |
| 559 | }; | 583 | }; |
| 560 | 584 | ||
| @@ -603,6 +627,10 @@ fn generate_pin_trait_impls() -> TokenStream { | |||
| 603 | ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }), | 627 | ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }), |
| 604 | ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }), | 628 | ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }), |
| 605 | ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }), | 629 | ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }), |
| 630 | ("adc", s) => { | ||
| 631 | let signal = s.parse::<u8>().unwrap(); | ||
| 632 | Some(quote! { impl_adc_pin!(#peri, #pin_name, #signal); }) | ||
| 633 | } | ||
| 606 | 634 | ||
| 607 | _ => None, | 635 | _ => None, |
| 608 | }; | 636 | }; |
diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs new file mode 100644 index 000000000..5b93e9a6e --- /dev/null +++ b/embassy-mspm0/src/adc.rs | |||
| @@ -0,0 +1,510 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | |||
| 10 | use crate::interrupt::{Interrupt, InterruptExt}; | ||
| 11 | use crate::mode::{Async, Blocking, Mode}; | ||
| 12 | use crate::pac::adc::{vals, Adc as Regs}; | ||
| 13 | use crate::{interrupt, Peri}; | ||
| 14 | |||
| 15 | /// Interrupt handler. | ||
| 16 | pub struct InterruptHandler<T: Instance> { | ||
| 17 | _phantom: PhantomData<T>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 21 | unsafe fn on_interrupt() { | ||
| 22 | // Mis is cleared upon reading iidx | ||
| 23 | let iidx = T::info().regs.cpu_int(0).iidx().read().stat(); | ||
| 24 | // TODO: Running in sequence mode, we get an interrupt per finished result. It would be | ||
| 25 | // nice to wake up only after all results are finished. | ||
| 26 | if vals::CpuIntIidxStat::MEMRESIFG0 <= iidx && iidx <= vals::CpuIntIidxStat::MEMRESIFG23 { | ||
| 27 | T::state().waker.wake(); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | // Constants from the metapac crate | ||
| 33 | const ADC_VRSEL: u8 = crate::_generated::ADC_VRSEL; | ||
| 34 | const ADC_MEMCTL: u8 = crate::_generated::ADC_MEMCTL; | ||
| 35 | |||
| 36 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 37 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 38 | /// Conversion resolution of the ADC results. | ||
| 39 | pub enum Resolution { | ||
| 40 | /// 12-bits resolution | ||
| 41 | BIT12, | ||
| 42 | |||
| 43 | /// 10-bits resolution | ||
| 44 | BIT10, | ||
| 45 | |||
| 46 | /// 8-bits resolution | ||
| 47 | BIT8, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl Resolution { | ||
| 51 | /// Number of bits of the resolution. | ||
| 52 | pub fn bits(&self) -> u8 { | ||
| 53 | match self { | ||
| 54 | Resolution::BIT12 => 12, | ||
| 55 | Resolution::BIT10 => 10, | ||
| 56 | Resolution::BIT8 => 8, | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 62 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 63 | /// Reference voltage (Vref) selection for the ADC channels. | ||
| 64 | pub enum Vrsel { | ||
| 65 | /// VDDA reference | ||
| 66 | VddaVssa = 0, | ||
| 67 | |||
| 68 | /// External reference from pin | ||
| 69 | ExtrefVrefm = 1, | ||
| 70 | |||
| 71 | /// Internal reference | ||
| 72 | IntrefVssa = 2, | ||
| 73 | |||
| 74 | /// VDDA and VREFM connected to VREF+ and VREF- of ADC | ||
| 75 | #[cfg(adc_neg_vref)] | ||
| 76 | VddaVrefm = 3, | ||
| 77 | |||
| 78 | /// INTREF and VREFM connected to VREF+ and VREF- of ADC | ||
| 79 | #[cfg(adc_neg_vref)] | ||
| 80 | IntrefVrefm = 4, | ||
| 81 | } | ||
| 82 | |||
| 83 | /// ADC configuration. | ||
| 84 | #[derive(Copy, Clone)] | ||
| 85 | #[non_exhaustive] | ||
| 86 | pub struct Config { | ||
| 87 | /// Resolution of the ADC conversion. The number of bits used to represent an ADC measurement. | ||
| 88 | pub resolution: Resolution, | ||
| 89 | /// ADC voltage reference selection. | ||
| 90 | /// | ||
| 91 | /// This value is used when reading a single channel. When reading a sequence | ||
| 92 | /// the vr_select is provided per channel. | ||
| 93 | pub vr_select: Vrsel, | ||
| 94 | /// The sample time in number of ADC sample clock cycles. | ||
| 95 | pub sample_time: u16, | ||
| 96 | } | ||
| 97 | |||
| 98 | impl Default for Config { | ||
| 99 | fn default() -> Self { | ||
| 100 | Self { | ||
| 101 | resolution: Resolution::BIT12, | ||
| 102 | vr_select: Vrsel::VddaVssa, | ||
| 103 | sample_time: 50, | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | /// ADC (Analog to Digial Converter) Driver. | ||
| 109 | pub struct Adc<'d, T: Instance, M: Mode> { | ||
| 110 | #[allow(unused)] | ||
| 111 | adc: crate::Peri<'d, T>, | ||
| 112 | info: &'static Info, | ||
| 113 | state: &'static State, | ||
| 114 | config: Config, | ||
| 115 | _phantom: PhantomData<M>, | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<'d, T: Instance> Adc<'d, T, Blocking> { | ||
| 119 | /// A new blocking ADC driver instance. | ||
| 120 | pub fn new_blocking(peri: Peri<'d, T>, config: Config) -> Self { | ||
| 121 | let mut this = Self { | ||
| 122 | adc: peri, | ||
| 123 | info: T::info(), | ||
| 124 | state: T::state(), | ||
| 125 | config, | ||
| 126 | _phantom: PhantomData, | ||
| 127 | }; | ||
| 128 | this.setup(); | ||
| 129 | this | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | impl<'d, T: Instance> Adc<'d, T, Async> { | ||
| 134 | /// A new asynchronous ADC driver instance. | ||
| 135 | pub fn new_async( | ||
| 136 | peri: Peri<'d, T>, | ||
| 137 | config: Config, | ||
| 138 | _irqs: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 139 | ) -> Self { | ||
| 140 | let mut this = Self { | ||
| 141 | adc: peri, | ||
| 142 | info: T::info(), | ||
| 143 | state: T::state(), | ||
| 144 | config, | ||
| 145 | _phantom: PhantomData, | ||
| 146 | }; | ||
| 147 | this.setup(); | ||
| 148 | unsafe { | ||
| 149 | this.info.interrupt.enable(); | ||
| 150 | } | ||
| 151 | this | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { | ||
| 156 | const SINGLE_CHANNEL: u8 = 0; | ||
| 157 | |||
| 158 | fn setup(&mut self) { | ||
| 159 | let config = &self.config; | ||
| 160 | assert!( | ||
| 161 | (config.vr_select as u8) < ADC_VRSEL, | ||
| 162 | "Reference voltage selection out of bounds" | ||
| 163 | ); | ||
| 164 | // Reset adc | ||
| 165 | self.info.regs.gprcm(0).rstctl().write(|reg| { | ||
| 166 | reg.set_resetstkyclr(true); | ||
| 167 | reg.set_resetassert(true); | ||
| 168 | reg.set_key(vals::ResetKey::KEY); | ||
| 169 | }); | ||
| 170 | |||
| 171 | // Power up adc | ||
| 172 | self.info.regs.gprcm(0).pwren().modify(|reg| { | ||
| 173 | reg.set_enable(true); | ||
| 174 | reg.set_key(vals::PwrenKey::KEY); | ||
| 175 | }); | ||
| 176 | |||
| 177 | // Wait for cycles similar to TI power setup | ||
| 178 | cortex_m::asm::delay(16); | ||
| 179 | |||
| 180 | // Set clock config | ||
| 181 | self.info.regs.gprcm(0).clkcfg().modify(|reg| { | ||
| 182 | reg.set_key(vals::ClkcfgKey::KEY); | ||
| 183 | reg.set_sampclk(vals::Sampclk::SYSOSC); | ||
| 184 | }); | ||
| 185 | self.info.regs.ctl0().modify(|reg| { | ||
| 186 | reg.set_sclkdiv(vals::Sclkdiv::DIV_BY_4); | ||
| 187 | }); | ||
| 188 | self.info.regs.clkfreq().modify(|reg| { | ||
| 189 | reg.set_frange(vals::Frange::RANGE24TO32); | ||
| 190 | }); | ||
| 191 | |||
| 192 | // Init single conversion with software trigger and auto sampling | ||
| 193 | // | ||
| 194 | // We use sequence to support sequence operation in the future, but only set up a single | ||
| 195 | // channel | ||
| 196 | self.info.regs.ctl1().modify(|reg| { | ||
| 197 | reg.set_conseq(vals::Conseq::SEQUENCE); | ||
| 198 | reg.set_sampmode(vals::Sampmode::AUTO); | ||
| 199 | reg.set_trigsrc(vals::Trigsrc::SOFTWARE); | ||
| 200 | }); | ||
| 201 | let res = match config.resolution { | ||
| 202 | Resolution::BIT12 => vals::Res::BIT_12, | ||
| 203 | Resolution::BIT10 => vals::Res::BIT_10, | ||
| 204 | Resolution::BIT8 => vals::Res::BIT_8, | ||
| 205 | }; | ||
| 206 | self.info.regs.ctl2().modify(|reg| { | ||
| 207 | // Startadd detemines the channel used in single mode. | ||
| 208 | reg.set_startadd(Self::SINGLE_CHANNEL); | ||
| 209 | reg.set_endadd(Self::SINGLE_CHANNEL); | ||
| 210 | reg.set_res(res); | ||
| 211 | reg.set_df(false); | ||
| 212 | }); | ||
| 213 | |||
| 214 | // Set the sample time used by all channels for now | ||
| 215 | self.info.regs.scomp0().modify(|reg| { | ||
| 216 | reg.set_val(config.sample_time); | ||
| 217 | }); | ||
| 218 | } | ||
| 219 | |||
| 220 | fn setup_blocking_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) { | ||
| 221 | channel.setup(); | ||
| 222 | |||
| 223 | // CTL0.ENC must be 0 to write the MEMCTL register | ||
| 224 | while self.info.regs.ctl0().read().enc() { | ||
| 225 | // Wait until the ADC is not in conversion mode | ||
| 226 | } | ||
| 227 | |||
| 228 | // Conversion mem config | ||
| 229 | let vrsel = vals::Vrsel::from_bits(self.config.vr_select as u8); | ||
| 230 | self.info.regs.memctl(Self::SINGLE_CHANNEL as usize).modify(|reg| { | ||
| 231 | reg.set_chansel(channel.channel()); | ||
| 232 | reg.set_vrsel(vrsel); | ||
| 233 | reg.set_stime(vals::Stime::SEL_SCOMP0); | ||
| 234 | reg.set_avgen(false); | ||
| 235 | reg.set_bcsen(false); | ||
| 236 | reg.set_trig(vals::Trig::AUTO_NEXT); | ||
| 237 | reg.set_wincomp(false); | ||
| 238 | }); | ||
| 239 | self.info.regs.ctl2().modify(|reg| { | ||
| 240 | // Set end address to the number of used channels | ||
| 241 | reg.set_endadd(Self::SINGLE_CHANNEL); | ||
| 242 | }); | ||
| 243 | } | ||
| 244 | |||
| 245 | fn enable_conversion(&mut self) { | ||
| 246 | // Enable conversion | ||
| 247 | self.info.regs.ctl0().modify(|reg| { | ||
| 248 | reg.set_enc(true); | ||
| 249 | }); | ||
| 250 | } | ||
| 251 | |||
| 252 | fn start_conversion(&mut self) { | ||
| 253 | // Start conversion | ||
| 254 | self.info.regs.ctl1().modify(|reg| { | ||
| 255 | reg.set_sc(vals::Sc::START); | ||
| 256 | }); | ||
| 257 | } | ||
| 258 | |||
| 259 | fn conversion_result(&mut self, channel_id: usize) -> u16 { | ||
| 260 | // Read result | ||
| 261 | self.info.regs.memres(channel_id).read().data() | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Read one ADC channel in blocking mode using the config provided at initialization. | ||
| 265 | pub fn blocking_read(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 { | ||
| 266 | self.setup_blocking_channel(channel); | ||
| 267 | self.enable_conversion(); | ||
| 268 | self.start_conversion(); | ||
| 269 | |||
| 270 | while self.info.regs.ctl0().read().enc() {} | ||
| 271 | |||
| 272 | self.conversion_result(Self::SINGLE_CHANNEL as usize) | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | impl<'d, T: Instance> Adc<'d, T, Async> { | ||
| 277 | /// Maximum length allowed for [`Self::read_sequence`]. | ||
| 278 | pub const MAX_SEQUENCE_LEN: usize = ADC_MEMCTL as usize; | ||
| 279 | |||
| 280 | async fn wait_for_conversion(&self) { | ||
| 281 | let info = self.info; | ||
| 282 | let state = self.state; | ||
| 283 | poll_fn(move |cx| { | ||
| 284 | state.waker.register(cx.waker()); | ||
| 285 | |||
| 286 | if !info.regs.ctl0().read().enc() { | ||
| 287 | Poll::Ready(()) | ||
| 288 | } else { | ||
| 289 | Poll::Pending | ||
| 290 | } | ||
| 291 | }) | ||
| 292 | .await; | ||
| 293 | } | ||
| 294 | |||
| 295 | fn setup_async_channel(&self, id: usize, channel: &Peri<'d, impl AdcChannel<T>>, vrsel: Vrsel) { | ||
| 296 | let vrsel = vals::Vrsel::from_bits(vrsel as u8); | ||
| 297 | // Conversion mem config | ||
| 298 | self.info.regs.memctl(id).modify(|reg| { | ||
| 299 | reg.set_chansel(channel.channel()); | ||
| 300 | reg.set_vrsel(vrsel); | ||
| 301 | reg.set_stime(vals::Stime::SEL_SCOMP0); | ||
| 302 | reg.set_avgen(false); | ||
| 303 | reg.set_bcsen(false); | ||
| 304 | reg.set_trig(vals::Trig::AUTO_NEXT); | ||
| 305 | reg.set_wincomp(false); | ||
| 306 | }); | ||
| 307 | |||
| 308 | // Clear interrupt status | ||
| 309 | self.info.regs.cpu_int(0).iclr().write(|reg| { | ||
| 310 | reg.set_memresifg(id, true); | ||
| 311 | }); | ||
| 312 | // Enable interrupt | ||
| 313 | self.info.regs.cpu_int(0).imask().modify(|reg| { | ||
| 314 | reg.set_memresifg(id, true); | ||
| 315 | }); | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Read one ADC channel asynchronously using the config provided at initialization. | ||
| 319 | pub async fn read_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 { | ||
| 320 | channel.setup(); | ||
| 321 | |||
| 322 | // CTL0.ENC must be 0 to write the MEMCTL register | ||
| 323 | self.wait_for_conversion().await; | ||
| 324 | |||
| 325 | self.info.regs.ctl2().modify(|reg| { | ||
| 326 | // Set end address to the number of used channels | ||
| 327 | reg.set_endadd(Self::SINGLE_CHANNEL); | ||
| 328 | }); | ||
| 329 | |||
| 330 | self.setup_async_channel(Self::SINGLE_CHANNEL as usize, channel, self.config.vr_select); | ||
| 331 | |||
| 332 | self.enable_conversion(); | ||
| 333 | self.start_conversion(); | ||
| 334 | self.wait_for_conversion().await; | ||
| 335 | |||
| 336 | self.conversion_result(Self::SINGLE_CHANNEL as usize) | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Read one or multiple ADC channels using the Vrsel provided per channel. | ||
| 340 | /// | ||
| 341 | /// `sequence` iterator and `readings` must have the same length. | ||
| 342 | /// | ||
| 343 | /// Example | ||
| 344 | /// ```rust,ignore | ||
| 345 | /// use embassy_mspm0::adc::{Adc, AdcChannel, Vrsel}; | ||
| 346 | /// | ||
| 347 | /// let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs); | ||
| 348 | /// let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)]; | ||
| 349 | /// let mut readings = [0u16; 2]; | ||
| 350 | /// | ||
| 351 | /// adc.read_sequence(sequence.into_iter(), &mut readings).await; | ||
| 352 | /// defmt::info!("Measurements: {}", readings); | ||
| 353 | /// ``` | ||
| 354 | pub async fn read_sequence<'a>( | ||
| 355 | &mut self, | ||
| 356 | sequence: impl ExactSizeIterator<Item = (&'a Peri<'d, AnyAdcChannel<T>>, Vrsel)>, | ||
| 357 | readings: &mut [u16], | ||
| 358 | ) where | ||
| 359 | 'd: 'a, | ||
| 360 | { | ||
| 361 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 362 | assert!( | ||
| 363 | sequence.len() == readings.len(), | ||
| 364 | "Sequence length must be equal to readings length" | ||
| 365 | ); | ||
| 366 | assert!( | ||
| 367 | sequence.len() <= Self::MAX_SEQUENCE_LEN, | ||
| 368 | "Asynchronous read sequence cannot be more than {} in length", | ||
| 369 | Self::MAX_SEQUENCE_LEN | ||
| 370 | ); | ||
| 371 | |||
| 372 | // CTL0.ENC must be 0 to write the MEMCTL register | ||
| 373 | self.wait_for_conversion().await; | ||
| 374 | |||
| 375 | self.info.regs.ctl2().modify(|reg| { | ||
| 376 | // Set end address to the number of used channels | ||
| 377 | reg.set_endadd((sequence.len() - 1) as u8); | ||
| 378 | }); | ||
| 379 | |||
| 380 | for (i, (channel, vrsel)) in sequence.enumerate() { | ||
| 381 | self.setup_async_channel(i, channel, vrsel); | ||
| 382 | } | ||
| 383 | self.enable_conversion(); | ||
| 384 | self.start_conversion(); | ||
| 385 | self.wait_for_conversion().await; | ||
| 386 | |||
| 387 | for (i, r) in readings.iter_mut().enumerate() { | ||
| 388 | *r = self.conversion_result(i); | ||
| 389 | } | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | /// Peripheral instance trait. | ||
| 394 | #[allow(private_bounds)] | ||
| 395 | pub trait Instance: PeripheralType + SealedInstance { | ||
| 396 | type Interrupt: crate::interrupt::typelevel::Interrupt; | ||
| 397 | } | ||
| 398 | |||
| 399 | /// Peripheral state. | ||
| 400 | pub(crate) struct State { | ||
| 401 | waker: AtomicWaker, | ||
| 402 | } | ||
| 403 | |||
| 404 | impl State { | ||
| 405 | pub const fn new() -> Self { | ||
| 406 | Self { | ||
| 407 | waker: AtomicWaker::new(), | ||
| 408 | } | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Peripheral information. | ||
| 413 | pub(crate) struct Info { | ||
| 414 | pub(crate) regs: Regs, | ||
| 415 | pub(crate) interrupt: Interrupt, | ||
| 416 | } | ||
| 417 | |||
| 418 | /// Peripheral instance trait. | ||
| 419 | pub(crate) trait SealedInstance { | ||
| 420 | fn info() -> &'static Info; | ||
| 421 | fn state() -> &'static State; | ||
| 422 | } | ||
| 423 | |||
| 424 | macro_rules! impl_adc_instance { | ||
| 425 | ($instance: ident) => { | ||
| 426 | impl crate::adc::SealedInstance for crate::peripherals::$instance { | ||
| 427 | fn info() -> &'static crate::adc::Info { | ||
| 428 | use crate::adc::Info; | ||
| 429 | use crate::interrupt::typelevel::Interrupt; | ||
| 430 | |||
| 431 | static INFO: Info = Info { | ||
| 432 | regs: crate::pac::$instance, | ||
| 433 | interrupt: crate::interrupt::typelevel::$instance::IRQ, | ||
| 434 | }; | ||
| 435 | &INFO | ||
| 436 | } | ||
| 437 | |||
| 438 | fn state() -> &'static crate::adc::State { | ||
| 439 | use crate::adc::State; | ||
| 440 | |||
| 441 | static STATE: State = State::new(); | ||
| 442 | &STATE | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | impl crate::adc::Instance for crate::peripherals::$instance { | ||
| 447 | type Interrupt = crate::interrupt::typelevel::$instance; | ||
| 448 | } | ||
| 449 | }; | ||
| 450 | } | ||
| 451 | |||
| 452 | /// A type-erased channel for a given ADC instance. | ||
| 453 | /// | ||
| 454 | /// This is useful in scenarios where you need the ADC channels to have the same type, such as | ||
| 455 | /// storing them in an array. | ||
| 456 | pub struct AnyAdcChannel<T> { | ||
| 457 | pub(crate) channel: u8, | ||
| 458 | pub(crate) _phantom: PhantomData<T>, | ||
| 459 | } | ||
| 460 | |||
| 461 | impl_peripheral!(AnyAdcChannel<T: Instance>); | ||
| 462 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | ||
| 463 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | ||
| 464 | fn channel(&self) -> u8 { | ||
| 465 | self.channel | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | impl<T> AnyAdcChannel<T> { | ||
| 470 | #[allow(unused)] | ||
| 471 | pub(crate) fn get_hw_channel(&self) -> u8 { | ||
| 472 | self.channel | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | /// ADC channel. | ||
| 477 | #[allow(private_bounds)] | ||
| 478 | pub trait AdcChannel<T>: PeripheralType + Into<AnyAdcChannel<T>> + SealedAdcChannel<T> + Sized {} | ||
| 479 | |||
| 480 | pub(crate) trait SealedAdcChannel<T> { | ||
| 481 | fn setup(&self) {} | ||
| 482 | |||
| 483 | fn channel(&self) -> u8; | ||
| 484 | } | ||
| 485 | |||
| 486 | macro_rules! impl_adc_pin { | ||
| 487 | ($inst: ident, $pin: ident, $ch: expr) => { | ||
| 488 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {} | ||
| 489 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin { | ||
| 490 | fn setup(&self) { | ||
| 491 | crate::gpio::SealedPin::set_as_analog(self); | ||
| 492 | } | ||
| 493 | |||
| 494 | fn channel(&self) -> u8 { | ||
| 495 | $ch | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | impl From<crate::peripherals::$pin> for crate::adc::AnyAdcChannel<peripherals::$inst> { | ||
| 500 | fn from(val: crate::peripherals::$pin) -> Self { | ||
| 501 | crate::adc::SealedAdcChannel::<peripherals::$inst>::setup(&val); | ||
| 502 | |||
| 503 | Self { | ||
| 504 | channel: crate::adc::SealedAdcChannel::<peripherals::$inst>::channel(&val), | ||
| 505 | _phantom: core::marker::PhantomData, | ||
| 506 | } | ||
| 507 | } | ||
| 508 | } | ||
| 509 | }; | ||
| 510 | } | ||
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index f77848888..d5fd36dbf 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs | |||
| @@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 10 | 10 | ||
| 11 | use crate::pac::gpio::vals::*; | 11 | use crate::pac::gpio::vals::*; |
| 12 | use crate::pac::gpio::{self}; | 12 | use crate::pac::gpio::{self}; |
| 13 | #[cfg(all(feature = "rt", any(mspm0c110x, mspm0l110x)))] | 13 | #[cfg(all(feature = "rt", any(mspm0c110x, mspm0c1105_c1106, mspm0l110x)))] |
| 14 | use crate::pac::interrupt; | 14 | use crate::pac::interrupt; |
| 15 | use crate::pac::{self}; | 15 | use crate::pac::{self}; |
| 16 | 16 | ||
| @@ -1108,24 +1108,30 @@ fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { | |||
| 1108 | // C110x and L110x have a dedicated interrupts just for GPIOA. | 1108 | // C110x and L110x have a dedicated interrupts just for GPIOA. |
| 1109 | // | 1109 | // |
| 1110 | // These chips do not have a GROUP1 interrupt. | 1110 | // These chips do not have a GROUP1 interrupt. |
| 1111 | #[cfg(all(feature = "rt", any(mspm0c110x, mspm0l110x)))] | 1111 | #[cfg(all(feature = "rt", any(mspm0c110x, mspm0c1105_c1106, mspm0l110x)))] |
| 1112 | #[interrupt] | 1112 | #[interrupt] |
| 1113 | fn GPIOA() { | 1113 | fn GPIOA() { |
| 1114 | irq_handler(pac::GPIOA, &PORTA_WAKERS); | 1114 | irq_handler(pac::GPIOA, &PORTA_WAKERS); |
| 1115 | } | 1115 | } |
| 1116 | 1116 | ||
| 1117 | #[cfg(all(feature = "rt", mspm0c1105_c1106))] | ||
| 1118 | #[interrupt] | ||
| 1119 | fn GPIOB() { | ||
| 1120 | irq_handler(pac::GPIOB, &PORTB_WAKERS); | ||
| 1121 | } | ||
| 1122 | |||
| 1117 | // These symbols are weakly defined as DefaultHandler and are called by the interrupt group implementation. | 1123 | // These symbols are weakly defined as DefaultHandler and are called by the interrupt group implementation. |
| 1118 | // | 1124 | // |
| 1119 | // Defining these as no_mangle is required so that the linker will pick these over the default handler. | 1125 | // Defining these as no_mangle is required so that the linker will pick these over the default handler. |
| 1120 | 1126 | ||
| 1121 | #[cfg(all(feature = "rt", not(any(mspm0c110x, mspm0l110x))))] | 1127 | #[cfg(all(feature = "rt", not(any(mspm0c110x, mspm0c1105_c1106, mspm0l110x))))] |
| 1122 | #[no_mangle] | 1128 | #[no_mangle] |
| 1123 | #[allow(non_snake_case)] | 1129 | #[allow(non_snake_case)] |
| 1124 | fn GPIOA() { | 1130 | fn GPIOA() { |
| 1125 | irq_handler(pac::GPIOA, &PORTA_WAKERS); | 1131 | irq_handler(pac::GPIOA, &PORTA_WAKERS); |
| 1126 | } | 1132 | } |
| 1127 | 1133 | ||
| 1128 | #[cfg(all(feature = "rt", gpio_pb))] | 1134 | #[cfg(all(feature = "rt", gpio_pb, not(mspm0c1105_c1106)))] |
| 1129 | #[no_mangle] | 1135 | #[no_mangle] |
| 1130 | #[allow(non_snake_case)] | 1136 | #[allow(non_snake_case)] |
| 1131 | fn GPIOB() { | 1137 | fn GPIOB() { |
diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 7e22bb724..1906e37ba 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs | |||
| @@ -195,7 +195,7 @@ impl Config { | |||
| 195 | .unwrap(); | 195 | .unwrap(); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | #[cfg(any(mspm0c110x))] | 198 | #[cfg(any(mspm0c110x, mspm0c1105_c1106))] |
| 199 | fn calculate_clock_source(&self) -> u32 { | 199 | fn calculate_clock_source(&self) -> u32 { |
| 200 | // Assume that BusClk has default value. | 200 | // Assume that BusClk has default value. |
| 201 | // TODO: calculate BusClk more precisely. | 201 | // TODO: calculate BusClk more precisely. |
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 0cb19a379..13f0ce662 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs | |||
| @@ -13,6 +13,7 @@ pub(crate) mod fmt; | |||
| 13 | // This must be declared early as well for | 13 | // This must be declared early as well for |
| 14 | mod macros; | 14 | mod macros; |
| 15 | 15 | ||
| 16 | pub mod adc; | ||
| 16 | pub mod dma; | 17 | pub mod dma; |
| 17 | pub mod gpio; | 18 | pub mod gpio; |
| 18 | pub mod i2c; | 19 | pub mod i2c; |
diff --git a/embassy-net-wiznet/CHANGELOG.md b/embassy-net-wiznet/CHANGELOG.md index e464efa69..a74dc3125 100644 --- a/embassy-net-wiznet/CHANGELOG.md +++ b/embassy-net-wiznet/CHANGELOG.md | |||
| @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Added experimental W6100 driver with disabled MAC filter (does not currently work with it enabled) | ||
| 12 | - Introduced `SOCKET_INTR_CLR` register which is needed on W6100 and later models (on W5100/W5500 this is shared with `SOCKET_INTR` and the address is the same) | ||
| 13 | |||
| 11 | ## 0.2.1 - 2025-08-26 | 14 | ## 0.2.1 - 2025-08-26 |
| 12 | 15 | ||
| 13 | ## 0.1.1 - 2025-08-14 | 16 | ## 0.1.1 - 2025-08-14 |
diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs index 2e7a9ed6c..47d7c5dc3 100644 --- a/embassy-net-wiznet/src/chip/mod.rs +++ b/embassy-net-wiznet/src/chip/mod.rs | |||
| @@ -4,6 +4,8 @@ pub use w5500::W5500; | |||
| 4 | mod w5100s; | 4 | mod w5100s; |
| 5 | use embedded_hal_async::spi::SpiDevice; | 5 | use embedded_hal_async::spi::SpiDevice; |
| 6 | pub use w5100s::W5100S; | 6 | pub use w5100s::W5100S; |
| 7 | mod w6100; | ||
| 8 | pub use w6100::W6100; | ||
| 7 | 9 | ||
| 8 | pub(crate) trait SealedChip { | 10 | pub(crate) trait SealedChip { |
| 9 | type Address; | 11 | type Address; |
| @@ -28,7 +30,9 @@ pub(crate) trait SealedChip { | |||
| 28 | const SOCKET_RECVD_SIZE: Self::Address; | 30 | const SOCKET_RECVD_SIZE: Self::Address; |
| 29 | const SOCKET_RX_DATA_READ_PTR: Self::Address; | 31 | const SOCKET_RX_DATA_READ_PTR: Self::Address; |
| 30 | const SOCKET_INTR_MASK: Self::Address; | 32 | const SOCKET_INTR_MASK: Self::Address; |
| 33 | #[allow(dead_code)] | ||
| 31 | const SOCKET_INTR: Self::Address; | 34 | const SOCKET_INTR: Self::Address; |
| 35 | const SOCKET_INTR_CLR: Self::Address; | ||
| 32 | 36 | ||
| 33 | const SOCKET_MODE_VALUE: u8; | 37 | const SOCKET_MODE_VALUE: u8; |
| 34 | 38 | ||
diff --git a/embassy-net-wiznet/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs index 4c4b7ab16..1eef2369e 100644 --- a/embassy-net-wiznet/src/chip/w5100s.rs +++ b/embassy-net-wiznet/src/chip/w5100s.rs | |||
| @@ -29,6 +29,7 @@ impl super::SealedChip for W5100S { | |||
| 29 | const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; | 29 | const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; |
| 30 | const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; | 30 | const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; |
| 31 | const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; | 31 | const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; |
| 32 | const SOCKET_INTR_CLR: Self::Address = SOCKET_BASE + 0x02; | ||
| 32 | 33 | ||
| 33 | const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); | 34 | const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); |
| 34 | 35 | ||
diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs index 5cfcb94e4..198ba3226 100644 --- a/embassy-net-wiznet/src/chip/w5500.rs +++ b/embassy-net-wiznet/src/chip/w5500.rs | |||
| @@ -33,6 +33,7 @@ impl super::SealedChip for W5500 { | |||
| 33 | const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); | 33 | const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); |
| 34 | const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); | 34 | const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); |
| 35 | const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); | 35 | const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); |
| 36 | const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x02); | ||
| 36 | 37 | ||
| 37 | const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); | 38 | const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); |
| 38 | 39 | ||
diff --git a/embassy-net-wiznet/src/chip/w6100.rs b/embassy-net-wiznet/src/chip/w6100.rs new file mode 100644 index 000000000..740b0edaf --- /dev/null +++ b/embassy-net-wiznet/src/chip/w6100.rs | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | use embedded_hal_async::spi::{Operation, SpiDevice}; | ||
| 2 | |||
| 3 | #[repr(u8)] | ||
| 4 | pub enum RegisterBlock { | ||
| 5 | Common = 0x00, | ||
| 6 | Socket0 = 0x01, | ||
| 7 | TxBuf = 0x02, | ||
| 8 | RxBuf = 0x03, | ||
| 9 | } | ||
| 10 | |||
| 11 | /// Wiznet W6100 chip. | ||
| 12 | pub enum W6100 {} | ||
| 13 | |||
| 14 | impl super::Chip for W6100 {} | ||
| 15 | impl super::SealedChip for W6100 { | ||
| 16 | type Address = (RegisterBlock, u16); | ||
| 17 | |||
| 18 | const CHIP_VERSION: u8 = 0x46; | ||
| 19 | |||
| 20 | const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x2004); | ||
| 21 | const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x4120); | ||
| 22 | // SIMR (SOCKET Interrupt Mask Register) | ||
| 23 | const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x2114); | ||
| 24 | const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x3000); | ||
| 25 | const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x0002); | ||
| 26 | |||
| 27 | const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x0000); | ||
| 28 | const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x0010); | ||
| 29 | const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0220); | ||
| 30 | const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0200); | ||
| 31 | const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0204); | ||
| 32 | const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x020C); | ||
| 33 | const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0224); | ||
| 34 | const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x0228); | ||
| 35 | // Sn_IMR (SOCKET n Interrupt Mask Register) | ||
| 36 | const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x0024); | ||
| 37 | // Sn_IR (SOCKET n Interrupt Register) | ||
| 38 | const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x0020); | ||
| 39 | // Sn_IRCLR (Sn_IR Clear Register) | ||
| 40 | const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x0028); | ||
| 41 | |||
| 42 | // MACRAW mode. See Page 57 of https://docs.wiznet.io/img/products/w6100/w6100_ds_v105e.pdf | ||
| 43 | // Note: Bit 7 is MAC filter. On the W5500 this is normally turned ON however the W6100 will not successfully retrieve an IP address with this enabled. Disabling for now and will have live with the extra noise. | ||
| 44 | const SOCKET_MODE_VALUE: u8 = 0b0000_0111; | ||
| 45 | |||
| 46 | const BUF_SIZE: u16 = 0x1000; | ||
| 47 | const AUTO_WRAP: bool = true; | ||
| 48 | |||
| 49 | fn rx_addr(addr: u16) -> Self::Address { | ||
| 50 | (RegisterBlock::RxBuf, addr) | ||
| 51 | } | ||
| 52 | |||
| 53 | fn tx_addr(addr: u16) -> Self::Address { | ||
| 54 | (RegisterBlock::TxBuf, addr) | ||
| 55 | } | ||
| 56 | |||
| 57 | async fn bus_read<SPI: SpiDevice>( | ||
| 58 | spi: &mut SPI, | ||
| 59 | address: Self::Address, | ||
| 60 | data: &mut [u8], | ||
| 61 | ) -> Result<(), SPI::Error> { | ||
| 62 | let address_phase = address.1.to_be_bytes(); | ||
| 63 | let control_phase = [(address.0 as u8) << 3]; | ||
| 64 | let operations = &mut [ | ||
| 65 | Operation::Write(&address_phase), | ||
| 66 | Operation::Write(&control_phase), | ||
| 67 | Operation::TransferInPlace(data), | ||
| 68 | ]; | ||
| 69 | spi.transaction(operations).await | ||
| 70 | } | ||
| 71 | |||
| 72 | async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { | ||
| 73 | let address_phase = address.1.to_be_bytes(); | ||
| 74 | let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; | ||
| 75 | let data_phase = data; | ||
| 76 | let operations = &mut [ | ||
| 77 | Operation::Write(&address_phase[..]), | ||
| 78 | Operation::Write(&control_phase), | ||
| 79 | Operation::Write(&data_phase), | ||
| 80 | ]; | ||
| 81 | spi.transaction(operations).await | ||
| 82 | } | ||
| 83 | } | ||
diff --git a/embassy-net-wiznet/src/device.rs b/embassy-net-wiznet/src/device.rs index d2b6bb0c3..8ef92b022 100644 --- a/embassy-net-wiznet/src/device.rs +++ b/embassy-net-wiznet/src/device.rs | |||
| @@ -125,7 +125,7 @@ impl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> { | |||
| 125 | 125 | ||
| 126 | async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { | 126 | async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { |
| 127 | let data = [code as u8]; | 127 | let data = [code as u8]; |
| 128 | self.bus_write(C::SOCKET_INTR, &data).await | 128 | self.bus_write(C::SOCKET_INTR_CLR, &data).await |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | async fn get_tx_write_ptr(&mut self) -> Result<u16, SPI::Error> { | 131 | async fn get_tx_write_ptr(&mut self) -> Result<u16, SPI::Error> { |
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 245626c14..61a2c858a 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml | |||
| @@ -26,6 +26,19 @@ build = [ | |||
| 26 | {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, | 26 | {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, |
| 27 | {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, | 27 | {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, |
| 28 | {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, | 28 | {target = "thumbv7em-none-eabi", features = ["defmt", "dns", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"]}, |
| 29 | # Xtensa builds | ||
| 30 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "medium-ethernet", "packet-trace"]}, | ||
| 31 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "multicast", "medium-ethernet"]}, | ||
| 32 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "medium-ethernet"]}, | ||
| 33 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "medium-ethernet", "dhcpv4-hostname"]}, | ||
| 34 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet"]}, | ||
| 35 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ieee802154"]}, | ||
| 36 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet", "medium-ieee802154"]}, | ||
| 37 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv6", "medium-ethernet"]}, | ||
| 38 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ethernet"]}, | ||
| 39 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip"]}, | ||
| 40 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip", "medium-ethernet"]}, | ||
| 41 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32-none-elf", features = ["defmt", "tcp", "udp", "dns", "proto-ipv4", "proto-ipv6", "medium-ip", "medium-ethernet", "medium-ieee802154"]}, | ||
| 29 | ] | 42 | ] |
| 30 | 43 | ||
| 31 | [package.metadata.embassy_docs] | 44 | [package.metadata.embassy_docs] |
diff --git a/embassy-net/README.md b/embassy-net/README.md index 1722ffc7b..1c5b30a9c 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md | |||
| @@ -25,6 +25,7 @@ unimplemented features of the network protocols. | |||
| 25 | - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). | 25 | - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). |
| 26 | - [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) | 26 | - [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) |
| 27 | - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. | 27 | - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. |
| 28 | - [`embassy-nrf`](https://github.com/embassy-rs/embassy/tree/main/embassy-nrf) for IEEE 802.15.4 support on nrf chips. | ||
| 28 | 29 | ||
| 29 | ## Examples | 30 | ## Examples |
| 30 | 31 | ||
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 5dc941b25..befa34ecf 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md | |||
| @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - changed: nrf54l: Disable glitch detection and enable DC/DC in init. | 11 | - changed: nrf54l: Disable glitch detection and enable DC/DC in init. |
| 12 | - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 | ||
| 12 | 13 | ||
| 13 | ## 0.7.0 - 2025-08-26 | 14 | ## 0.7.0 - 2025-08-26 |
| 14 | 15 | ||
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 2ce75cfac..4afd28fbd 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -79,6 +79,9 @@ gpiote = [] | |||
| 79 | ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz | 79 | ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz |
| 80 | time-driver-rtc1 = ["_time-driver"] | 80 | time-driver-rtc1 = ["_time-driver"] |
| 81 | 81 | ||
| 82 | ## Enable embassy-net 802.15.4 driver | ||
| 83 | net-driver = ["_net-driver"] | ||
| 84 | |||
| 82 | ## Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53) | 85 | ## Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53) |
| 83 | nfc-pins-as-gpio = [] | 86 | nfc-pins-as-gpio = [] |
| 84 | 87 | ||
| @@ -154,6 +157,8 @@ _nrf91 = [] | |||
| 154 | 157 | ||
| 155 | _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] | 158 | _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] |
| 156 | 159 | ||
| 160 | _net-driver = ["dep:embassy-net-driver-channel","dep:embassy-futures"] | ||
| 161 | |||
| 157 | # trustzone state. | 162 | # trustzone state. |
| 158 | _s = [] | 163 | _s = [] |
| 159 | _ns = [] | 164 | _ns = [] |
| @@ -177,6 +182,8 @@ embassy-sync = { version = "0.7.2", path = "../embassy-sync" } | |||
| 177 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } | 182 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } |
| 178 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal", default-features = false } | 183 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal", default-features = false } |
| 179 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } | 184 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 185 | embassy-net-driver-channel = { version = "0.3.2", path = "../embassy-net-driver-channel", optional = true} | ||
| 186 | embassy-futures = { version = "0.1.2", path = "../embassy-futures", optional = true} | ||
| 180 | 187 | ||
| 181 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 188 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 182 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 189 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
diff --git a/embassy-nrf/README.md b/embassy-nrf/README.md index 3df5f1fa5..26b1fa5ea 100644 --- a/embassy-nrf/README.md +++ b/embassy-nrf/README.md | |||
| @@ -28,6 +28,10 @@ allows running Rust code without a SPM or TF-M binary, saving flash space and si | |||
| 28 | 28 | ||
| 29 | If the `time-driver-rtc1` feature is enabled, the HAL uses the RTC peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 32768 Hz. | 29 | If the `time-driver-rtc1` feature is enabled, the HAL uses the RTC peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 32768 Hz. |
| 30 | 30 | ||
| 31 | ## Embassy-net-driver | ||
| 32 | |||
| 33 | If the board supports IEEE 802.15.4 (see `src/radio/mod.rs`) the corresponding [embassy-net-driver](https://crates.io/crates/embassy-net-driver) implementation can be enabled with the feature `net-driver`. | ||
| 34 | |||
| 31 | ## Embedded-hal | 35 | ## Embedded-hal |
| 32 | 36 | ||
| 33 | The `embassy-nrf` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async). | 37 | The `embassy-nrf` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async). |
diff --git a/embassy-nrf/src/embassy_net_802154_driver.rs b/embassy-nrf/src/embassy_net_802154_driver.rs new file mode 100644 index 000000000..8662be787 --- /dev/null +++ b/embassy-nrf/src/embassy_net_802154_driver.rs | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | //! embassy-net IEEE 802.15.4 driver | ||
| 2 | |||
| 3 | use embassy_futures::select::{select3, Either3}; | ||
| 4 | use embassy_net_driver_channel::driver::LinkState; | ||
| 5 | use embassy_net_driver_channel::{self as ch}; | ||
| 6 | use embassy_time::{Duration, Ticker}; | ||
| 7 | |||
| 8 | use crate::radio::ieee802154::{Packet, Radio}; | ||
| 9 | use crate::radio::InterruptHandler; | ||
| 10 | use crate::{self as nrf, interrupt}; | ||
| 11 | |||
| 12 | /// MTU for the nrf radio. | ||
| 13 | pub const MTU: usize = Packet::CAPACITY as usize; | ||
| 14 | |||
| 15 | /// embassy-net device for the driver. | ||
| 16 | pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; | ||
| 17 | |||
| 18 | /// Internal state for the embassy-net driver. | ||
| 19 | pub struct State<const N_RX: usize, const N_TX: usize> { | ||
| 20 | ch_state: ch::State<MTU, N_RX, N_TX>, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> { | ||
| 24 | /// Create a new `State`. | ||
| 25 | pub const fn new() -> Self { | ||
| 26 | Self { | ||
| 27 | ch_state: ch::State::new(), | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Background runner for the driver. | ||
| 33 | /// | ||
| 34 | /// You must call `.run()` in a background task for the driver to operate. | ||
| 35 | pub struct Runner<'d, T: nrf::radio::Instance> { | ||
| 36 | radio: nrf::radio::ieee802154::Radio<'d, T>, | ||
| 37 | ch: ch::Runner<'d, MTU>, | ||
| 38 | } | ||
| 39 | |||
| 40 | impl<'d, T: nrf::radio::Instance> Runner<'d, T> { | ||
| 41 | /// Drives the radio. Needs to run to use the driver. | ||
| 42 | pub async fn run(mut self) -> ! { | ||
| 43 | let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); | ||
| 44 | let mut tick = Ticker::every(Duration::from_millis(500)); | ||
| 45 | let mut packet = Packet::new(); | ||
| 46 | state_chan.set_link_state(LinkState::Up); | ||
| 47 | loop { | ||
| 48 | match select3( | ||
| 49 | async { | ||
| 50 | let rx_buf = rx_chan.rx_buf().await; | ||
| 51 | self.radio.receive(&mut packet).await.ok().map(|_| rx_buf) | ||
| 52 | }, | ||
| 53 | tx_chan.tx_buf(), | ||
| 54 | tick.next(), | ||
| 55 | ) | ||
| 56 | .await | ||
| 57 | { | ||
| 58 | Either3::First(Some(rx_buf)) => { | ||
| 59 | let len = rx_buf.len().min(packet.len() as usize); | ||
| 60 | (&mut rx_buf[..len]).copy_from_slice(&*packet); | ||
| 61 | rx_chan.rx_done(len); | ||
| 62 | } | ||
| 63 | Either3::Second(tx_buf) => { | ||
| 64 | let len = tx_buf.len().min(Packet::CAPACITY as usize); | ||
| 65 | packet.copy_from_slice(&tx_buf[..len]); | ||
| 66 | self.radio.try_send(&mut packet).await.ok().unwrap(); | ||
| 67 | tx_chan.tx_done(); | ||
| 68 | } | ||
| 69 | _ => {} | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Make sure to use `HfclkSource::ExternalXtal` as the `hfclk_source` | ||
| 76 | /// to use the radio (nrf52840 product spec v1.11 5.4.1) | ||
| 77 | /// ``` | ||
| 78 | /// # use embassy_nrf::config::*; | ||
| 79 | /// let mut config = Config::default(); | ||
| 80 | /// config.hfclk_source = HfclkSource::ExternalXtal; | ||
| 81 | /// ``` | ||
| 82 | pub async fn new<'a, const N_RX: usize, const N_TX: usize, T: nrf::radio::Instance, Irq>( | ||
| 83 | mac_addr: [u8; 8], | ||
| 84 | radio: nrf::Peri<'a, T>, | ||
| 85 | irq: Irq, | ||
| 86 | state: &'a mut State<N_RX, N_TX>, | ||
| 87 | ) -> Result<(Device<'a>, Runner<'a, T>), ()> | ||
| 88 | where | ||
| 89 | Irq: interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a, | ||
| 90 | { | ||
| 91 | let radio = Radio::new(radio, irq); | ||
| 92 | |||
| 93 | let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ieee802154(mac_addr)); | ||
| 94 | |||
| 95 | Ok((device, Runner { ch: runner, radio })) | ||
| 96 | } | ||
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index aa4801897..897e660b8 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -137,6 +137,17 @@ pub mod qspi; | |||
| 137 | #[cfg(not(feature = "_nrf54l"))] // TODO | 137 | #[cfg(not(feature = "_nrf54l"))] // TODO |
| 138 | #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] | 138 | #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] |
| 139 | pub mod radio; | 139 | pub mod radio; |
| 140 | |||
| 141 | #[cfg(any( | ||
| 142 | feature = "nrf52811", | ||
| 143 | feature = "nrf52820", | ||
| 144 | feature = "nrf52833", | ||
| 145 | feature = "nrf52840", | ||
| 146 | feature = "_nrf5340-net" | ||
| 147 | ))] | ||
| 148 | #[cfg(feature = "_net-driver")] | ||
| 149 | pub mod embassy_net_802154_driver; | ||
| 150 | |||
| 140 | #[cfg(not(feature = "_nrf54l"))] // TODO | 151 | #[cfg(not(feature = "_nrf54l"))] // TODO |
| 141 | #[cfg(feature = "_nrf5340")] | 152 | #[cfg(feature = "_nrf5340")] |
| 142 | pub mod reset; | 153 | pub mod reset; |
diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md index 7042ad14c..ab97c4185 100644 --- a/embassy-nxp/CHANGELOG.md +++ b/embassy-nxp/CHANGELOG.md | |||
| @@ -7,5 +7,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | - Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac` | |
| 11 | - First release with changelog. | 11 | - First release with changelog. |
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index ab0bfbfd7..455915f29 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml | |||
| @@ -7,7 +7,7 @@ publish = false | |||
| 7 | 7 | ||
| 8 | [package.metadata.embassy] | 8 | [package.metadata.embassy] |
| 9 | build = [ | 9 | build = [ |
| 10 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "lpc55"]}, | 10 | {target = "thumbv8m.main-none-eabihf", features = ["defmt", "lpc55-core0"]}, |
| 11 | {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1011", "rt", "time-driver-pit"]}, | 11 | {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1011", "rt", "time-driver-pit"]}, |
| 12 | {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1062", "rt", "time-driver-pit"]}, | 12 | {target = "thumbv7em-none-eabihf", features = ["defmt", "mimxrt1062", "rt", "time-driver-pit"]}, |
| 13 | ] | 13 | ] |
| @@ -18,7 +18,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/s | |||
| 18 | features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers. | 18 | features = ["defmt", "unstable-pac" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers. |
| 19 | 19 | ||
| 20 | flavors = [ | 20 | flavors = [ |
| 21 | { regex_feature = "lpc55", target = "thumbv8m.main-none-eabihf" }, | 21 | { regex_feature = "lpc55-core0", target = "thumbv8m.main-none-eabihf" }, |
| 22 | { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" }, | 22 | { regex_feature = "mimxrt.*", target = "thumbv7em-none-eabihf" }, |
| 23 | ] | 23 | ] |
| 24 | 24 | ||
| @@ -36,21 +36,20 @@ embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-ut | |||
| 36 | embedded-io = "0.6.1" | 36 | embedded-io = "0.6.1" |
| 37 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 37 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 38 | ## Chip dependencies | 38 | ## Chip dependencies |
| 39 | lpc55-pac = { version = "0.5.0", optional = true } | 39 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "b736e3038254d593024aaa1a5a7b1f95a5728538"} |
| 40 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" } | ||
| 41 | 40 | ||
| 42 | imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } | 41 | imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } |
| 43 | 42 | ||
| 44 | [build-dependencies] | 43 | [build-dependencies] |
| 45 | cfg_aliases = "0.2.1" | 44 | cfg_aliases = "0.2.1" |
| 46 | nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e", features = ["metadata"], optional = true } | 45 | nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "b736e3038254d593024aaa1a5a7b1f95a5728538", features = ["metadata"], optional = true } |
| 47 | proc-macro2 = "1.0.95" | 46 | proc-macro2 = "1.0.95" |
| 48 | quote = "1.0.15" | 47 | quote = "1.0.15" |
| 49 | 48 | ||
| 50 | [features] | 49 | [features] |
| 51 | default = ["rt"] | 50 | default = ["rt"] |
| 52 | # Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily). | 51 | # Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily). |
| 53 | rt = ["lpc55-pac?/rt", "nxp-pac?/rt"] | 52 | rt = ["nxp-pac?/rt"] |
| 54 | 53 | ||
| 55 | ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. | 54 | ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. |
| 56 | defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] | 55 | defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] |
| @@ -79,6 +78,6 @@ _rt1xxx = [] | |||
| 79 | _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] | 78 | _time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] |
| 80 | 79 | ||
| 81 | #! ### Chip selection features | 80 | #! ### Chip selection features |
| 82 | lpc55 = ["dep:lpc55-pac"] | 81 | lpc55-core0 = ["nxp-pac/lpc55s69_cm33_core0"] |
| 83 | mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] | 82 | mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"] |
| 84 | mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] | 83 | mimxrt1062 = ["nxp-pac/mimxrt1062", "_rt1xxx", "dep:imxrt-rt"] |
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs index e168ced00..711bff3e7 100644 --- a/embassy-nxp/src/chips/lpc55.rs +++ b/embassy-nxp/src/chips/lpc55.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | pub use lpc55_pac as pac; | 1 | pub use nxp_pac as pac; |
| 2 | 2 | ||
| 3 | embassy_hal_internal::peripherals! { | 3 | embassy_hal_internal::peripherals! { |
| 4 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other | 4 | // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other |
diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs index 3049cc12d..717b38d96 100644 --- a/embassy-nxp/src/gpio.rs +++ b/embassy-nxp/src/gpio.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! General purpose input/output (GPIO) driver. | 1 | //! General purpose input/output (GPIO) driver. |
| 2 | #![macro_use] | 2 | #![macro_use] |
| 3 | 3 | ||
| 4 | #[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] | 4 | #[cfg_attr(feature = "lpc55-core0", path = "./gpio/lpc55.rs")] |
| 5 | #[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] | 5 | #[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")] |
| 6 | mod inner; | 6 | mod inner; |
| 7 | pub use inner::*; | 7 | pub use inner::*; |
diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 8f407bb3a..36ea99d21 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs | |||
| @@ -1,12 +1,17 @@ | |||
| 1 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 1 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; |
| 2 | 2 | ||
| 3 | use crate::pac::iocon::vals::{PioDigimode, PioMode}; | ||
| 4 | use crate::pac::{GPIO, IOCON, SYSCON}; | ||
| 3 | use crate::{peripherals, Peri}; | 5 | use crate::{peripherals, Peri}; |
| 4 | 6 | ||
| 5 | pub(crate) fn init() { | 7 | pub(crate) fn init() { |
| 6 | // Enable clocks for GPIO, PINT, and IOCON | 8 | // Enable clocks for GPIO, PINT, and IOCON |
| 7 | syscon_reg() | 9 | SYSCON.ahbclkctrl0().modify(|w| { |
| 8 | .ahbclkctrl0 | 10 | w.set_gpio0(true); |
| 9 | .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); | 11 | w.set_gpio1(true); |
| 12 | w.set_mux(true); | ||
| 13 | w.set_iocon(true); | ||
| 14 | }); | ||
| 10 | info!("GPIO initialized"); | 15 | info!("GPIO initialized"); |
| 11 | } | 16 | } |
| 12 | 17 | ||
| @@ -59,21 +64,24 @@ impl<'d> Output<'d> { | |||
| 59 | } | 64 | } |
| 60 | 65 | ||
| 61 | pub fn set_high(&mut self) { | 66 | pub fn set_high(&mut self) { |
| 62 | gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | 67 | GPIO.set(self.pin.pin_bank() as usize) |
| 68 | .write(|w| w.set_setp(self.pin.bit())); | ||
| 63 | } | 69 | } |
| 64 | 70 | ||
| 65 | pub fn set_low(&mut self) { | 71 | pub fn set_low(&mut self) { |
| 66 | gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | 72 | GPIO.clr(self.pin.pin_bank() as usize) |
| 73 | .write(|w| w.set_clrp(self.pin.bit())); | ||
| 67 | } | 74 | } |
| 68 | 75 | ||
| 69 | pub fn toggle(&mut self) { | 76 | pub fn toggle(&mut self) { |
| 70 | gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) | 77 | GPIO.not(self.pin.pin_bank() as usize) |
| 78 | .write(|w| w.set_notp(self.pin.bit())); | ||
| 71 | } | 79 | } |
| 72 | 80 | ||
| 73 | /// Get the current output level of the pin. Note that the value returned by this function is | 81 | /// Get the current output level of the pin. Note that the value returned by this function is |
| 74 | /// the voltage level reported by the pin, not the value set by the output driver. | 82 | /// the voltage level reported by the pin, not the value set by the output driver. |
| 75 | pub fn level(&self) -> Level { | 83 | pub fn level(&self) -> Level { |
| 76 | let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); | 84 | let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port(); |
| 77 | if bits & self.pin.bit() != 0 { | 85 | if bits & self.pin.bit() != 0 { |
| 78 | Level::High | 86 | Level::High |
| 79 | } else { | 87 | } else { |
| @@ -101,18 +109,18 @@ impl<'d> Input<'d> { | |||
| 101 | 109 | ||
| 102 | /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. | 110 | /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. |
| 103 | pub fn set_pull(&mut self, pull: Pull) { | 111 | pub fn set_pull(&mut self, pull: Pull) { |
| 104 | match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { | 112 | match_iocon!(register, self.pin.pin_bank(), self.pin.pin_number(), { |
| 105 | register.modify(|_, w| match pull { | 113 | register.modify(|w| match pull { |
| 106 | Pull::None => w.mode().inactive(), | 114 | Pull::None => w.set_mode(PioMode::INACTIVE), |
| 107 | Pull::Up => w.mode().pull_up(), | 115 | Pull::Up => w.set_mode(PioMode::PULL_UP), |
| 108 | Pull::Down => w.mode().pull_down(), | 116 | Pull::Down => w.set_mode(PioMode::PULL_DOWN), |
| 109 | }); | 117 | }); |
| 110 | }); | 118 | }); |
| 111 | } | 119 | } |
| 112 | 120 | ||
| 113 | /// Get the current input level of the pin. | 121 | /// Get the current input level of the pin. |
| 114 | pub fn read(&self) -> Level { | 122 | pub fn read(&self) -> Level { |
| 115 | let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); | 123 | let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port(); |
| 116 | if bits & self.pin.bit() != 0 { | 124 | if bits & self.pin.bit() != 0 { |
| 117 | Level::High | 125 | Level::High |
| 118 | } else { | 126 | } else { |
| @@ -188,8 +196,8 @@ impl<'d> Flex<'d> { | |||
| 188 | /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default | 196 | /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default |
| 189 | /// setting for pins is (usually) non-digital. | 197 | /// setting for pins is (usually) non-digital. |
| 190 | fn set_as_digital(&mut self) { | 198 | fn set_as_digital(&mut self) { |
| 191 | match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { | 199 | match_iocon!(register, self.pin_bank(), self.pin_number(), { |
| 192 | register.modify(|_, w| w.digimode().digital()); | 200 | register.modify(|w| w.set_digimode(PioDigimode::DIGITAL)); |
| 193 | }); | 201 | }); |
| 194 | } | 202 | } |
| 195 | 203 | ||
| @@ -197,12 +205,14 @@ impl<'d> Flex<'d> { | |||
| 197 | /// function handles itself. | 205 | /// function handles itself. |
| 198 | pub fn set_as_output(&mut self) { | 206 | pub fn set_as_output(&mut self) { |
| 199 | self.set_as_digital(); | 207 | self.set_as_digital(); |
| 200 | gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) | 208 | GPIO.dirset(self.pin.pin_bank() as usize) |
| 209 | .write(|w| w.set_dirsetp(self.bit())) | ||
| 201 | } | 210 | } |
| 202 | 211 | ||
| 203 | pub fn set_as_input(&mut self) { | 212 | pub fn set_as_input(&mut self) { |
| 204 | self.set_as_digital(); | 213 | self.set_as_digital(); |
| 205 | gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) | 214 | GPIO.dirclr(self.pin.pin_bank() as usize) |
| 215 | .write(|w| w.set_dirclrp(self.bit())) | ||
| 206 | } | 216 | } |
| 207 | } | 217 | } |
| 208 | 218 | ||
| @@ -262,52 +272,6 @@ impl SealedPin for AnyPin { | |||
| 262 | } | 272 | } |
| 263 | } | 273 | } |
| 264 | 274 | ||
| 265 | /// Get the GPIO register block. This is used to configure all GPIO pins. | ||
| 266 | /// | ||
| 267 | /// # Safety | ||
| 268 | /// Due to the type system of peripherals, access to the settings of a single pin is possible only | ||
| 269 | /// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You | ||
| 270 | /// must ensure that the GPIO registers are not accessed concurrently by multiple threads. | ||
| 271 | pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock { | ||
| 272 | unsafe { &*lpc55_pac::GPIO::ptr() } | ||
| 273 | } | ||
| 274 | |||
| 275 | /// Get the IOCON register block. | ||
| 276 | /// | ||
| 277 | /// # Safety | ||
| 278 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 279 | /// registers are not accessed concurrently by multiple threads. | ||
| 280 | pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock { | ||
| 281 | unsafe { &*lpc55_pac::IOCON::ptr() } | ||
| 282 | } | ||
| 283 | |||
| 284 | /// Get the INPUTMUX register block. | ||
| 285 | /// | ||
| 286 | /// # Safety | ||
| 287 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 288 | /// registers are not accessed concurrently by multiple threads. | ||
| 289 | pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock { | ||
| 290 | unsafe { &*lpc55_pac::INPUTMUX::ptr() } | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Get the SYSCON register block. | ||
| 294 | /// | ||
| 295 | /// # Safety | ||
| 296 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 297 | /// registers are not accessed concurrently by multiple threads. | ||
| 298 | pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock { | ||
| 299 | unsafe { &*lpc55_pac::SYSCON::ptr() } | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Get the PINT register block. | ||
| 303 | /// | ||
| 304 | /// # Safety | ||
| 305 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 306 | /// registers are not accessed concurrently by multiple threads. | ||
| 307 | pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { | ||
| 308 | unsafe { &*lpc55_pac::PINT::ptr() } | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Match the pin bank and number of a pin to the corresponding IOCON register. | 275 | /// Match the pin bank and number of a pin to the corresponding IOCON register. |
| 312 | /// | 276 | /// |
| 313 | /// # Example | 277 | /// # Example |
| @@ -316,270 +280,26 @@ pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { | |||
| 316 | /// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; | 280 | /// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; |
| 317 | /// | 281 | /// |
| 318 | /// // Make pin PIO1_6 digital and set it to pull-down mode. | 282 | /// // Make pin PIO1_6 digital and set it to pull-down mode. |
| 319 | /// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, { | 283 | /// match_iocon!(register, Bank::Bank1, 6, { |
| 320 | /// register.modify(|_, w| w.mode().pull_down().digimode().digital()); | 284 | /// register.modify(|w|{ |
| 285 | /// w.set_mode(PioMode::PULL_DOWN); | ||
| 286 | /// w.set_digimode(PioDigimode::DIGITAL); | ||
| 287 | /// | ||
| 288 | /// } | ||
| 321 | /// }); | 289 | /// }); |
| 322 | /// ``` | 290 | /// ``` |
| 323 | macro_rules! match_iocon { | 291 | macro_rules! match_iocon { |
| 324 | ($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => { | 292 | ($register:ident, $pin_bank:expr, $pin_number:expr, $action:expr) => { |
| 325 | match ($pin_bank, $pin_number) { | 293 | match $pin_bank { |
| 326 | (Bank::Bank0, 0) => { | 294 | Bank::Bank0 => { |
| 327 | let $register = &($iocon_register).pio0_0; | 295 | let $register = IOCON.pio0($pin_number as usize); |
| 328 | $action; | ||
| 329 | } | ||
| 330 | (Bank::Bank0, 1) => { | ||
| 331 | let $register = &($iocon_register).pio0_1; | ||
| 332 | $action; | ||
| 333 | } | ||
| 334 | (Bank::Bank0, 2) => { | ||
| 335 | let $register = &($iocon_register).pio0_2; | ||
| 336 | $action; | ||
| 337 | } | ||
| 338 | (Bank::Bank0, 3) => { | ||
| 339 | let $register = &($iocon_register).pio0_3; | ||
| 340 | $action; | ||
| 341 | } | ||
| 342 | (Bank::Bank0, 4) => { | ||
| 343 | let $register = &($iocon_register).pio0_4; | ||
| 344 | $action; | ||
| 345 | } | ||
| 346 | (Bank::Bank0, 5) => { | ||
| 347 | let $register = &($iocon_register).pio0_5; | ||
| 348 | $action; | ||
| 349 | } | ||
| 350 | (Bank::Bank0, 6) => { | ||
| 351 | let $register = &($iocon_register).pio0_6; | ||
| 352 | $action; | ||
| 353 | } | ||
| 354 | (Bank::Bank0, 7) => { | ||
| 355 | let $register = &($iocon_register).pio0_7; | ||
| 356 | $action; | ||
| 357 | } | ||
| 358 | (Bank::Bank0, 8) => { | ||
| 359 | let $register = &($iocon_register).pio0_8; | ||
| 360 | $action; | ||
| 361 | } | ||
| 362 | (Bank::Bank0, 9) => { | ||
| 363 | let $register = &($iocon_register).pio0_9; | ||
| 364 | $action; | ||
| 365 | } | ||
| 366 | (Bank::Bank0, 10) => { | ||
| 367 | let $register = &($iocon_register).pio0_10; | ||
| 368 | $action; | 296 | $action; |
| 369 | } | 297 | } |
| 370 | (Bank::Bank0, 11) => { | 298 | |
| 371 | let $register = &($iocon_register).pio0_11; | 299 | Bank::Bank1 => { |
| 372 | $action; | 300 | let $register = IOCON.pio1($pin_number as usize); |
| 373 | } | ||
| 374 | (Bank::Bank0, 12) => { | ||
| 375 | let $register = &($iocon_register).pio0_12; | ||
| 376 | $action; | ||
| 377 | } | ||
| 378 | (Bank::Bank0, 13) => { | ||
| 379 | let $register = &($iocon_register).pio0_13; | ||
| 380 | $action; | ||
| 381 | } | ||
| 382 | (Bank::Bank0, 14) => { | ||
| 383 | let $register = &($iocon_register).pio0_14; | ||
| 384 | $action; | ||
| 385 | } | ||
| 386 | (Bank::Bank0, 15) => { | ||
| 387 | let $register = &($iocon_register).pio0_15; | ||
| 388 | $action; | ||
| 389 | } | ||
| 390 | (Bank::Bank0, 16) => { | ||
| 391 | let $register = &($iocon_register).pio0_16; | ||
| 392 | $action; | ||
| 393 | } | ||
| 394 | (Bank::Bank0, 17) => { | ||
| 395 | let $register = &($iocon_register).pio0_17; | ||
| 396 | $action; | ||
| 397 | } | ||
| 398 | (Bank::Bank0, 18) => { | ||
| 399 | let $register = &($iocon_register).pio0_18; | ||
| 400 | $action; | ||
| 401 | } | ||
| 402 | (Bank::Bank0, 19) => { | ||
| 403 | let $register = &($iocon_register).pio0_19; | ||
| 404 | $action; | ||
| 405 | } | ||
| 406 | (Bank::Bank0, 20) => { | ||
| 407 | let $register = &($iocon_register).pio0_20; | ||
| 408 | $action; | ||
| 409 | } | ||
| 410 | (Bank::Bank0, 21) => { | ||
| 411 | let $register = &($iocon_register).pio0_21; | ||
| 412 | $action; | ||
| 413 | } | ||
| 414 | (Bank::Bank0, 22) => { | ||
| 415 | let $register = &($iocon_register).pio0_22; | ||
| 416 | $action; | ||
| 417 | } | ||
| 418 | (Bank::Bank0, 23) => { | ||
| 419 | let $register = &($iocon_register).pio0_23; | ||
| 420 | $action; | ||
| 421 | } | ||
| 422 | (Bank::Bank0, 24) => { | ||
| 423 | let $register = &($iocon_register).pio0_24; | ||
| 424 | $action; | ||
| 425 | } | ||
| 426 | (Bank::Bank0, 25) => { | ||
| 427 | let $register = &($iocon_register).pio0_25; | ||
| 428 | $action; | ||
| 429 | } | ||
| 430 | (Bank::Bank0, 26) => { | ||
| 431 | let $register = &($iocon_register).pio0_26; | ||
| 432 | $action; | ||
| 433 | } | ||
| 434 | (Bank::Bank0, 27) => { | ||
| 435 | let $register = &($iocon_register).pio0_27; | ||
| 436 | $action; | ||
| 437 | } | ||
| 438 | (Bank::Bank0, 28) => { | ||
| 439 | let $register = &($iocon_register).pio0_28; | ||
| 440 | $action; | ||
| 441 | } | ||
| 442 | (Bank::Bank0, 29) => { | ||
| 443 | let $register = &($iocon_register).pio0_29; | ||
| 444 | $action; | ||
| 445 | } | ||
| 446 | (Bank::Bank0, 30) => { | ||
| 447 | let $register = &($iocon_register).pio0_30; | ||
| 448 | $action; | ||
| 449 | } | ||
| 450 | (Bank::Bank0, 31) => { | ||
| 451 | let $register = &($iocon_register).pio0_31; | ||
| 452 | $action; | ||
| 453 | } | ||
| 454 | (Bank::Bank1, 0) => { | ||
| 455 | let $register = &($iocon_register).pio1_0; | ||
| 456 | $action; | ||
| 457 | } | ||
| 458 | (Bank::Bank1, 1) => { | ||
| 459 | let $register = &($iocon_register).pio1_1; | ||
| 460 | $action; | ||
| 461 | } | ||
| 462 | (Bank::Bank1, 2) => { | ||
| 463 | let $register = &($iocon_register).pio1_2; | ||
| 464 | $action; | ||
| 465 | } | ||
| 466 | (Bank::Bank1, 3) => { | ||
| 467 | let $register = &($iocon_register).pio1_3; | ||
| 468 | $action; | ||
| 469 | } | ||
| 470 | (Bank::Bank1, 4) => { | ||
| 471 | let $register = &($iocon_register).pio1_4; | ||
| 472 | $action; | ||
| 473 | } | ||
| 474 | (Bank::Bank1, 5) => { | ||
| 475 | let $register = &($iocon_register).pio1_5; | ||
| 476 | $action; | ||
| 477 | } | ||
| 478 | (Bank::Bank1, 6) => { | ||
| 479 | let $register = &($iocon_register).pio1_6; | ||
| 480 | $action; | ||
| 481 | } | ||
| 482 | (Bank::Bank1, 7) => { | ||
| 483 | let $register = &($iocon_register).pio1_7; | ||
| 484 | $action; | ||
| 485 | } | ||
| 486 | (Bank::Bank1, 8) => { | ||
| 487 | let $register = &($iocon_register).pio1_8; | ||
| 488 | $action; | ||
| 489 | } | ||
| 490 | (Bank::Bank1, 9) => { | ||
| 491 | let $register = &($iocon_register).pio1_9; | ||
| 492 | $action; | ||
| 493 | } | ||
| 494 | (Bank::Bank1, 10) => { | ||
| 495 | let $register = &($iocon_register).pio1_10; | ||
| 496 | $action; | ||
| 497 | } | ||
| 498 | (Bank::Bank1, 11) => { | ||
| 499 | let $register = &($iocon_register).pio1_11; | ||
| 500 | $action; | ||
| 501 | } | ||
| 502 | (Bank::Bank1, 12) => { | ||
| 503 | let $register = &($iocon_register).pio1_12; | ||
| 504 | $action; | ||
| 505 | } | ||
| 506 | (Bank::Bank1, 13) => { | ||
| 507 | let $register = &($iocon_register).pio1_13; | ||
| 508 | $action; | ||
| 509 | } | ||
| 510 | (Bank::Bank1, 14) => { | ||
| 511 | let $register = &($iocon_register).pio1_14; | ||
| 512 | $action; | ||
| 513 | } | ||
| 514 | (Bank::Bank1, 15) => { | ||
| 515 | let $register = &($iocon_register).pio1_15; | ||
| 516 | $action; | ||
| 517 | } | ||
| 518 | (Bank::Bank1, 16) => { | ||
| 519 | let $register = &($iocon_register).pio1_16; | ||
| 520 | $action; | ||
| 521 | } | ||
| 522 | (Bank::Bank1, 17) => { | ||
| 523 | let $register = &($iocon_register).pio1_17; | ||
| 524 | $action; | ||
| 525 | } | ||
| 526 | (Bank::Bank1, 18) => { | ||
| 527 | let $register = &($iocon_register).pio1_18; | ||
| 528 | $action; | ||
| 529 | } | ||
| 530 | (Bank::Bank1, 19) => { | ||
| 531 | let $register = &($iocon_register).pio1_19; | ||
| 532 | $action; | ||
| 533 | } | ||
| 534 | (Bank::Bank1, 20) => { | ||
| 535 | let $register = &($iocon_register).pio1_20; | ||
| 536 | $action; | ||
| 537 | } | ||
| 538 | (Bank::Bank1, 21) => { | ||
| 539 | let $register = &($iocon_register).pio1_21; | ||
| 540 | $action; | ||
| 541 | } | ||
| 542 | (Bank::Bank1, 22) => { | ||
| 543 | let $register = &($iocon_register).pio1_22; | ||
| 544 | $action; | ||
| 545 | } | ||
| 546 | (Bank::Bank1, 23) => { | ||
| 547 | let $register = &($iocon_register).pio1_23; | ||
| 548 | $action; | ||
| 549 | } | ||
| 550 | (Bank::Bank1, 24) => { | ||
| 551 | let $register = &($iocon_register).pio1_24; | ||
| 552 | $action; | ||
| 553 | } | ||
| 554 | (Bank::Bank1, 25) => { | ||
| 555 | let $register = &($iocon_register).pio1_25; | ||
| 556 | $action; | ||
| 557 | } | ||
| 558 | (Bank::Bank1, 26) => { | ||
| 559 | let $register = &($iocon_register).pio1_26; | ||
| 560 | $action; | ||
| 561 | } | ||
| 562 | (Bank::Bank1, 27) => { | ||
| 563 | let $register = &($iocon_register).pio1_27; | ||
| 564 | $action; | ||
| 565 | } | ||
| 566 | (Bank::Bank1, 28) => { | ||
| 567 | let $register = &($iocon_register).pio1_28; | ||
| 568 | $action; | ||
| 569 | } | ||
| 570 | (Bank::Bank1, 29) => { | ||
| 571 | let $register = &($iocon_register).pio1_29; | ||
| 572 | $action; | ||
| 573 | } | ||
| 574 | (Bank::Bank1, 30) => { | ||
| 575 | let $register = &($iocon_register).pio1_30; | ||
| 576 | $action; | ||
| 577 | } | ||
| 578 | (Bank::Bank1, 31) => { | ||
| 579 | let $register = &($iocon_register).pio1_31; | ||
| 580 | $action; | 301 | $action; |
| 581 | } | 302 | } |
| 582 | _ => unreachable!(), | ||
| 583 | } | 303 | } |
| 584 | }; | 304 | }; |
| 585 | } | 305 | } |
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 3fcb14b7e..74142a10b 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs | |||
| @@ -4,9 +4,9 @@ | |||
| 4 | pub(crate) mod fmt; | 4 | pub(crate) mod fmt; |
| 5 | 5 | ||
| 6 | pub mod gpio; | 6 | pub mod gpio; |
| 7 | #[cfg(feature = "lpc55")] | 7 | #[cfg(feature = "lpc55-core0")] |
| 8 | pub mod pint; | 8 | pub mod pint; |
| 9 | #[cfg(feature = "lpc55")] | 9 | #[cfg(feature = "lpc55-core0")] |
| 10 | pub mod usart; | 10 | pub mod usart; |
| 11 | 11 | ||
| 12 | #[cfg(feature = "_time_driver")] | 12 | #[cfg(feature = "_time_driver")] |
| @@ -15,7 +15,7 @@ pub mod usart; | |||
| 15 | mod time_driver; | 15 | mod time_driver; |
| 16 | 16 | ||
| 17 | // This mod MUST go last, so that it sees all the `impl_foo!` macros | 17 | // This mod MUST go last, so that it sees all the `impl_foo!` macros |
| 18 | #[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] | 18 | #[cfg_attr(feature = "lpc55-core0", path = "chips/lpc55.rs")] |
| 19 | #[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] | 19 | #[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")] |
| 20 | #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] | 20 | #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] |
| 21 | mod chip; | 21 | mod chip; |
| @@ -83,10 +83,10 @@ pub fn init(_config: config::Config) -> Peripherals { | |||
| 83 | pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); | 83 | pac::CCM.ccgr6().modify(|v| v.set_cg0(1)); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | #[cfg(any(feature = "lpc55", rt1xxx))] | 86 | #[cfg(any(feature = "lpc55-core0", rt1xxx))] |
| 87 | gpio::init(); | 87 | gpio::init(); |
| 88 | 88 | ||
| 89 | #[cfg(feature = "lpc55")] | 89 | #[cfg(feature = "lpc55-core0")] |
| 90 | pint::init(); | 90 | pint::init(); |
| 91 | 91 | ||
| 92 | #[cfg(feature = "_time_driver")] | 92 | #[cfg(feature = "_time_driver")] |
diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index ff414b4e6..e594aaa6a 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs | |||
| @@ -5,10 +5,11 @@ use core::pin::Pin as FuturePin; | |||
| 5 | use core::task::{Context, Poll}; | 5 | use core::task::{Context, Poll}; |
| 6 | 6 | ||
| 7 | use critical_section::Mutex; | 7 | use critical_section::Mutex; |
| 8 | use embassy_hal_internal::interrupt::InterruptExt; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 9 | 10 | ||
| 10 | use crate::gpio::{self, inputmux_reg, pint_reg, syscon_reg, AnyPin, Level, SealedPin}; | 11 | use crate::gpio::{self, AnyPin, Level, SealedPin}; |
| 11 | use crate::pac::interrupt; | 12 | use crate::pac::{interrupt, INPUTMUX, PINT, SYSCON}; |
| 12 | use crate::Peri; | 13 | use crate::Peri; |
| 13 | 14 | ||
| 14 | struct PinInterrupt { | 15 | struct PinInterrupt { |
| @@ -88,18 +89,18 @@ enum InterruptOn { | |||
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | pub(crate) fn init() { | 91 | pub(crate) fn init() { |
| 91 | syscon_reg().ahbclkctrl0.modify(|_, w| w.pint().enable()); | 92 | SYSCON.ahbclkctrl0().modify(|w| w.set_pint(true)); |
| 92 | 93 | ||
| 93 | // Enable interrupts | 94 | // Enable interrupts |
| 94 | unsafe { | 95 | unsafe { |
| 95 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT0); | 96 | interrupt::PIN_INT0.enable(); |
| 96 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT1); | 97 | interrupt::PIN_INT1.enable(); |
| 97 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT2); | 98 | interrupt::PIN_INT2.enable(); |
| 98 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT3); | 99 | interrupt::PIN_INT3.enable(); |
| 99 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT4); | 100 | interrupt::PIN_INT4.enable(); |
| 100 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT5); | 101 | interrupt::PIN_INT5.enable(); |
| 101 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); | 102 | interrupt::PIN_INT6.enable(); |
| 102 | crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); | 103 | interrupt::PIN_INT7.enable(); |
| 103 | }; | 104 | }; |
| 104 | 105 | ||
| 105 | info!("Pin interrupts initialized"); | 106 | info!("Pin interrupts initialized"); |
| @@ -119,24 +120,19 @@ impl<'d> InputFuture<'d> { | |||
| 119 | let interrupt_number = next_available_interrupt()?; | 120 | let interrupt_number = next_available_interrupt()?; |
| 120 | 121 | ||
| 121 | // Clear interrupt, just in case | 122 | // Clear interrupt, just in case |
| 122 | pint_reg() | 123 | PINT.rise().write(|w| w.set_rdet(1 << interrupt_number)); |
| 123 | .rise | 124 | PINT.fall().write(|w| w.set_fdet(1 << interrupt_number)); |
| 124 | .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) }); | ||
| 125 | pint_reg() | ||
| 126 | .fall | ||
| 127 | .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) }); | ||
| 128 | 125 | ||
| 129 | // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number) | 126 | // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number) |
| 130 | inputmux_reg().pintsel[interrupt_number] | 127 | INPUTMUX |
| 131 | .write(|w| unsafe { w.intpin().bits(32 * pin.pin_bank() as u8 + pin.pin_number()) }); | 128 | .pintsel(interrupt_number as usize) |
| 129 | .write(|w| w.set_intpin(32 * pin.pin_bank() as u8 + pin.pin_number())); | ||
| 132 | 130 | ||
| 133 | match interrupt_on { | 131 | match interrupt_on { |
| 134 | InterruptOn::Level(level) => { | 132 | InterruptOn::Level(level) => { |
| 135 | // Set pin interrupt register to edge sensitive or level sensitive | 133 | // Set pin interrupt register to edge sensitive or level sensitive |
| 136 | // 0 = edge sensitive, 1 = level sensitive | 134 | // 0 = edge sensitive, 1 = level sensitive |
| 137 | pint_reg() | 135 | PINT.isel().modify(|w| w.set_pmode(w.pmode() | (1 << interrupt_number))); |
| 138 | .isel | ||
| 139 | .modify(|r, w| unsafe { w.bits(r.bits() | (1 << interrupt_number)) }); | ||
| 140 | 136 | ||
| 141 | // Enable level interrupt. | 137 | // Enable level interrupt. |
| 142 | // | 138 | // |
| @@ -144,63 +140,44 @@ impl<'d> InputFuture<'d> { | |||
| 144 | // is activated. | 140 | // is activated. |
| 145 | 141 | ||
| 146 | // 0 = no-op, 1 = enable | 142 | // 0 = no-op, 1 = enable |
| 147 | pint_reg() | 143 | PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number)); |
| 148 | .sienr | ||
| 149 | .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); | ||
| 150 | 144 | ||
| 151 | // Set active level | 145 | // Set active level |
| 152 | match level { | 146 | match level { |
| 153 | Level::Low => { | 147 | Level::Low => { |
| 154 | // 0 = no-op, 1 = select LOW | 148 | // 0 = no-op, 1 = select LOW |
| 155 | pint_reg() | 149 | PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number)); |
| 156 | .cienf | ||
| 157 | .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); | ||
| 158 | } | 150 | } |
| 159 | Level::High => { | 151 | Level::High => { |
| 160 | // 0 = no-op, 1 = select HIGH | 152 | // 0 = no-op, 1 = select HIGH |
| 161 | pint_reg() | 153 | PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number)); |
| 162 | .sienf | ||
| 163 | .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); | ||
| 164 | } | 154 | } |
| 165 | } | 155 | } |
| 166 | } | 156 | } |
| 167 | InterruptOn::Edge(edge) => { | 157 | InterruptOn::Edge(edge) => { |
| 168 | // Set pin interrupt register to edge sensitive or level sensitive | 158 | // Set pin interrupt register to edge sensitive or level sensitive |
| 169 | // 0 = edge sensitive, 1 = level sensitive | 159 | // 0 = edge sensitive, 1 = level sensitive |
| 170 | pint_reg() | 160 | PINT.isel() |
| 171 | .isel | 161 | .modify(|w| w.set_pmode(w.pmode() & !(1 << interrupt_number))); |
| 172 | .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << interrupt_number)) }); | ||
| 173 | 162 | ||
| 174 | // Enable rising/falling edge detection | 163 | // Enable rising/falling edge detection |
| 175 | match edge { | 164 | match edge { |
| 176 | Edge::Rising => { | 165 | Edge::Rising => { |
| 177 | // 0 = no-op, 1 = enable rising edge | 166 | // 0 = no-op, 1 = enable rising edge |
| 178 | pint_reg() | 167 | PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number)); |
| 179 | .sienr | ||
| 180 | .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); | ||
| 181 | // 0 = no-op, 1 = disable falling edge | 168 | // 0 = no-op, 1 = disable falling edge |
| 182 | pint_reg() | 169 | PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number)); |
| 183 | .cienf | ||
| 184 | .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); | ||
| 185 | } | 170 | } |
| 186 | Edge::Falling => { | 171 | Edge::Falling => { |
| 187 | // 0 = no-op, 1 = enable falling edge | 172 | // 0 = no-op, 1 = enable falling edge |
| 188 | pint_reg() | 173 | PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number)); |
| 189 | .sienf | ||
| 190 | .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); | ||
| 191 | // 0 = no-op, 1 = disable rising edge | 174 | // 0 = no-op, 1 = disable rising edge |
| 192 | pint_reg() | 175 | PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number)); |
| 193 | .cienr | ||
| 194 | .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) }); | ||
| 195 | } | 176 | } |
| 196 | Edge::Both => { | 177 | Edge::Both => { |
| 197 | // 0 = no-op, 1 = enable | 178 | // 0 = no-op, 1 = enable |
| 198 | pint_reg() | 179 | PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number)); |
| 199 | .sienr | 180 | PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number)); |
| 200 | .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); | ||
| 201 | pint_reg() | ||
| 202 | .sienf | ||
| 203 | .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); | ||
| 204 | } | 181 | } |
| 205 | } | 182 | } |
| 206 | } | 183 | } |
| @@ -239,12 +216,8 @@ impl<'d> Drop for InputFuture<'d> { | |||
| 239 | 216 | ||
| 240 | // Disable pin interrupt | 217 | // Disable pin interrupt |
| 241 | // 0 = no-op, 1 = disable | 218 | // 0 = no-op, 1 = disable |
| 242 | pint_reg() | 219 | PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number)); |
| 243 | .cienr | 220 | PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number)); |
| 244 | .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) }); | ||
| 245 | pint_reg() | ||
| 246 | .cienf | ||
| 247 | .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); | ||
| 248 | 221 | ||
| 249 | critical_section::with(|cs| { | 222 | critical_section::with(|cs| { |
| 250 | let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); | 223 | let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); |
| @@ -277,12 +250,8 @@ impl<'d> Future for InputFuture<'d> { | |||
| 277 | } | 250 | } |
| 278 | 251 | ||
| 279 | fn handle_interrupt(interrupt_number: usize) { | 252 | fn handle_interrupt(interrupt_number: usize) { |
| 280 | pint_reg() | 253 | PINT.rise().write(|w| w.set_rdet(1 << interrupt_number)); |
| 281 | .rise | 254 | PINT.fall().write(|w| w.set_fdet(1 << interrupt_number)); |
| 282 | .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) }); | ||
| 283 | pint_reg() | ||
| 284 | .fall | ||
| 285 | .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) }); | ||
| 286 | 255 | ||
| 287 | critical_section::with(|cs| { | 256 | critical_section::with(|cs| { |
| 288 | let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); | 257 | let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); |
diff --git a/embassy-nxp/src/time_driver/rtc.rs b/embassy-nxp/src/time_driver/rtc.rs index 94272e9c2..fb6de6a5e 100644 --- a/embassy-nxp/src/time_driver/rtc.rs +++ b/embassy-nxp/src/time_driver/rtc.rs | |||
| @@ -6,7 +6,9 @@ use embassy_hal_internal::interrupt::{InterruptExt, Priority}; | |||
| 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; | 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; |
| 7 | use embassy_time_driver::{time_driver_impl, Driver}; | 7 | use embassy_time_driver::{time_driver_impl, Driver}; |
| 8 | use embassy_time_queue_utils::Queue; | 8 | use embassy_time_queue_utils::Queue; |
| 9 | use lpc55_pac::{interrupt, PMC, RTC, SYSCON}; | 9 | |
| 10 | use crate::pac::{interrupt, pmc, rtc, PMC, RTC, SYSCON}; | ||
| 11 | |||
| 10 | struct AlarmState { | 12 | struct AlarmState { |
| 11 | timestamp: Cell<u64>, | 13 | timestamp: Cell<u64>, |
| 12 | } | 14 | } |
| @@ -32,33 +34,32 @@ time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 32 | }); | 34 | }); |
| 33 | impl RtcDriver { | 35 | impl RtcDriver { |
| 34 | fn init(&'static self) { | 36 | fn init(&'static self) { |
| 35 | let syscon = unsafe { &*SYSCON::ptr() }; | 37 | let syscon = SYSCON; |
| 36 | let pmc = unsafe { &*PMC::ptr() }; | 38 | let pmc = PMC; |
| 37 | let rtc = unsafe { &*RTC::ptr() }; | 39 | let rtc = RTC; |
| 38 | 40 | ||
| 39 | syscon.ahbclkctrl0.modify(|_, w| w.rtc().enable()); | 41 | syscon.ahbclkctrl0().modify(|w| w.set_rtc(true)); |
| 40 | 42 | ||
| 41 | // By default the RTC enters software reset. If for some reason it is | 43 | // By default the RTC enters software reset. If for some reason it is |
| 42 | // not in reset, we enter and them promptly leave.q | 44 | // not in reset, we enter and them promptly leave.q |
| 43 | rtc.ctrl.modify(|_, w| w.swreset().set_bit()); | 45 | rtc.ctrl().modify(|w| w.set_swreset(true)); |
| 44 | rtc.ctrl.modify(|_, w| w.swreset().clear_bit()); | 46 | rtc.ctrl().modify(|w| w.set_swreset(false)); |
| 45 | 47 | ||
| 46 | // Select clock source - either XTAL or FRO | 48 | // Select clock source - either XTAL or FRO |
| 47 | // pmc.rtcosc32k.write(|w| w.sel().xtal32k()); | 49 | // pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::XTAL32K)); |
| 48 | pmc.rtcosc32k.write(|w| w.sel().fro32k()); | 50 | pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::FRO32K)); |
| 49 | 51 | ||
| 50 | // Start the RTC peripheral | 52 | // Start the RTC peripheral |
| 51 | rtc.ctrl.modify(|_, w| w.rtc_osc_pd().power_up()); | 53 | rtc.ctrl().modify(|w| w.set_rtc_osc_pd(rtc::vals::RtcOscPd::POWER_UP)); |
| 52 | |||
| 53 | // rtc.ctrl.modify(|_, w| w.rtc_en().clear_bit()); // EXTRA | ||
| 54 | 54 | ||
| 55 | //reset/clear(?) counter | 55 | //reset/clear(?) counter |
| 56 | rtc.count.reset(); | 56 | rtc.count().modify(|w| w.set_val(0)); |
| 57 | //en rtc main counter | 57 | //en rtc main counter |
| 58 | rtc.ctrl.modify(|_, w| w.rtc_en().set_bit()); | 58 | rtc.ctrl().modify(|w| w.set_rtc_en(true)); |
| 59 | rtc.ctrl.modify(|_, w| w.rtc1khz_en().set_bit()); | 59 | rtc.ctrl().modify(|w| w.set_rtc1khz_en(true)); |
| 60 | // subsec counter enable | 60 | // subsec counter enable |
| 61 | rtc.ctrl.modify(|_, w| w.rtc_subsec_ena().set_bit()); | 61 | rtc.ctrl() |
| 62 | .modify(|w| w.set_rtc_subsec_ena(rtc::vals::RtcSubsecEna::POWER_UP)); | ||
| 62 | 63 | ||
| 63 | // enable irq | 64 | // enable irq |
| 64 | unsafe { | 65 | unsafe { |
| @@ -68,7 +69,7 @@ impl RtcDriver { | |||
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { | 71 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| 71 | let rtc = unsafe { &*RTC::ptr() }; | 72 | let rtc = RTC; |
| 72 | let alarm = &self.alarms.borrow(cs); | 73 | let alarm = &self.alarms.borrow(cs); |
| 73 | alarm.timestamp.set(timestamp); | 74 | alarm.timestamp.set(timestamp); |
| 74 | let now = self.now(); | 75 | let now = self.now(); |
| @@ -83,33 +84,38 @@ impl RtcDriver { | |||
| 83 | let sec = (diff / 32768) as u32; | 84 | let sec = (diff / 32768) as u32; |
| 84 | let subsec = (diff % 32768) as u32; | 85 | let subsec = (diff % 32768) as u32; |
| 85 | 86 | ||
| 86 | let current_sec = rtc.count.read().val().bits(); | 87 | let current_sec = rtc.count().read().val(); |
| 87 | let target_sec = current_sec.wrapping_add(sec as u32); | 88 | let target_sec = current_sec.wrapping_add(sec as u32); |
| 88 | 89 | ||
| 89 | rtc.match_.write(|w| unsafe { w.matval().bits(target_sec) }); | 90 | rtc.match_().write(|w| w.set_matval(target_sec)); |
| 90 | rtc.wake.write(|w| unsafe { | 91 | rtc.wake().write(|w| { |
| 91 | let ms = (subsec * 1000) / 32768; | 92 | let ms = (subsec * 1000) / 32768; |
| 92 | w.val().bits(ms as u16) | 93 | w.set_val(ms as u16) |
| 93 | }); | 94 | }); |
| 95 | |||
| 94 | if subsec > 0 { | 96 | if subsec > 0 { |
| 95 | let ms = (subsec * 1000) / 32768; | 97 | let ms = (subsec * 1000) / 32768; |
| 96 | rtc.wake.write(|w| unsafe { w.val().bits(ms as u16) }); | 98 | rtc.wake().write(|w| w.set_val(ms as u16)); |
| 97 | } | 99 | } |
| 98 | rtc.ctrl.modify(|_, w| w.alarm1hz().clear_bit().wake1khz().clear_bit()); | 100 | |
| 101 | rtc.ctrl().modify(|w| { | ||
| 102 | w.set_alarm1hz(false); | ||
| 103 | w.set_wake1khz(rtc::vals::Wake1khz::RUN) | ||
| 104 | }); | ||
| 99 | true | 105 | true |
| 100 | } | 106 | } |
| 101 | 107 | ||
| 102 | fn on_interrupt(&self) { | 108 | fn on_interrupt(&self) { |
| 103 | critical_section::with(|cs| { | 109 | critical_section::with(|cs| { |
| 104 | let rtc = unsafe { &*RTC::ptr() }; | 110 | let rtc = RTC; |
| 105 | let flags = rtc.ctrl.read(); | 111 | let flags = rtc.ctrl().read(); |
| 106 | if flags.alarm1hz().bit_is_clear() { | 112 | if flags.alarm1hz() == false { |
| 107 | rtc.ctrl.modify(|_, w| w.alarm1hz().set_bit()); | 113 | rtc.ctrl().modify(|w| w.set_alarm1hz(true)); |
| 108 | self.trigger_alarm(cs); | 114 | self.trigger_alarm(cs); |
| 109 | } | 115 | } |
| 110 | 116 | ||
| 111 | if flags.wake1khz().bit_is_clear() { | 117 | if flags.wake1khz() == rtc::vals::Wake1khz::RUN { |
| 112 | rtc.ctrl.modify(|_, w| w.wake1khz().set_bit()); | 118 | rtc.ctrl().modify(|w| w.set_wake1khz(rtc::vals::Wake1khz::TIMEOUT)); |
| 113 | self.trigger_alarm(cs); | 119 | self.trigger_alarm(cs); |
| 114 | } | 120 | } |
| 115 | }); | 121 | }); |
| @@ -135,13 +141,13 @@ impl RtcDriver { | |||
| 135 | 141 | ||
| 136 | impl Driver for RtcDriver { | 142 | impl Driver for RtcDriver { |
| 137 | fn now(&self) -> u64 { | 143 | fn now(&self) -> u64 { |
| 138 | let rtc = unsafe { &*RTC::ptr() }; | 144 | let rtc = RTC; |
| 139 | 145 | ||
| 140 | loop { | 146 | loop { |
| 141 | let sec1 = rtc.count.read().val().bits() as u64; | 147 | let sec1 = rtc.count().read().val() as u64; |
| 142 | let sub1 = rtc.subsec.read().subsec().bits() as u64; | 148 | let sub1 = rtc.subsec().read().subsec() as u64; |
| 143 | let sec2 = rtc.count.read().val().bits() as u64; | 149 | let sec2 = rtc.count().read().val() as u64; |
| 144 | let sub2 = rtc.subsec.read().subsec().bits() as u64; | 150 | let sub2 = rtc.subsec().read().subsec() as u64; |
| 145 | 151 | ||
| 146 | if sec1 == sec2 && sub1 == sub2 { | 152 | if sec1 == sec2 && sub1 == sub2 { |
| 147 | return sec1 * 32768 + sub1; | 153 | return sec1 * 32768 + sub1; |
| @@ -162,7 +168,7 @@ impl Driver for RtcDriver { | |||
| 162 | }) | 168 | }) |
| 163 | } | 169 | } |
| 164 | } | 170 | } |
| 165 | #[cortex_m_rt::interrupt] | 171 | #[interrupt] |
| 166 | fn RTC() { | 172 | fn RTC() { |
| 167 | DRIVER.on_interrupt(); | 173 | DRIVER.on_interrupt(); |
| 168 | } | 174 | } |
diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs index 009c251e2..1d8886f24 100644 --- a/embassy-nxp/src/usart.rs +++ b/embassy-nxp/src/usart.rs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. | 1 | //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. |
| 2 | #![macro_use] | ||
| 3 | 2 | ||
| 4 | #[cfg_attr(feature = "lpc55", path = "./usart/lpc55.rs")] | 3 | #[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")] |
| 5 | mod inner; | 4 | mod inner; |
| 6 | pub use inner::*; | 5 | pub use inner::*; |
diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs index 3f7456a2e..428b80c4b 100644 --- a/embassy-nxp/src/usart/lpc55.rs +++ b/embassy-nxp/src/usart/lpc55.rs | |||
| @@ -2,9 +2,12 @@ use core::marker::PhantomData; | |||
| 2 | 2 | ||
| 3 | use embassy_hal_internal::{Peri, PeripheralType}; | 3 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 4 | use embedded_io::{self, ErrorKind}; | 4 | use embedded_io::{self, ErrorKind}; |
| 5 | pub use sealed::SealedInstance; | ||
| 6 | 5 | ||
| 7 | use crate::gpio::AnyPin; | 6 | use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; |
| 7 | use crate::pac::flexcomm::Flexcomm as FlexcommReg; | ||
| 8 | use crate::pac::iocon::vals::PioFunc; | ||
| 9 | use crate::pac::usart::Usart as UsartReg; | ||
| 10 | use crate::pac::*; | ||
| 8 | use crate::{Blocking, Mode}; | 11 | use crate::{Blocking, Mode}; |
| 9 | 12 | ||
| 10 | /// Serial error | 13 | /// Serial error |
| @@ -47,16 +50,6 @@ pub enum DataBits { | |||
| 47 | DataBits9, | 50 | DataBits9, |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | impl DataBits { | ||
| 51 | fn bits(&self) -> u8 { | ||
| 52 | match self { | ||
| 53 | Self::DataBits7 => 0b00, | ||
| 54 | Self::DataBits8 => 0b01, | ||
| 55 | Self::DataBits9 => 0b10, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Parity bit. | 53 | /// Parity bit. |
| 61 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 54 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 62 | pub enum Parity { | 55 | pub enum Parity { |
| @@ -68,16 +61,6 @@ pub enum Parity { | |||
| 68 | ParityOdd, | 61 | ParityOdd, |
| 69 | } | 62 | } |
| 70 | 63 | ||
| 71 | impl Parity { | ||
| 72 | fn bits(&self) -> u8 { | ||
| 73 | match self { | ||
| 74 | Self::ParityNone => 0b00, | ||
| 75 | Self::ParityEven => 0b10, | ||
| 76 | Self::ParityOdd => 0b11, | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Stop bits. | 64 | /// Stop bits. |
| 82 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 65 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 83 | pub enum StopBits { | 66 | pub enum StopBits { |
| @@ -87,15 +70,6 @@ pub enum StopBits { | |||
| 87 | Stop2, | 70 | Stop2, |
| 88 | } | 71 | } |
| 89 | 72 | ||
| 90 | impl StopBits { | ||
| 91 | fn bits(&self) -> bool { | ||
| 92 | return match self { | ||
| 93 | Self::Stop1 => false, | ||
| 94 | Self::Stop2 => true, | ||
| 95 | }; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /// UART config. | 73 | /// UART config. |
| 100 | #[non_exhaustive] | 74 | #[non_exhaustive] |
| 101 | #[derive(Clone, Debug)] | 75 | #[derive(Clone, Debug)] |
| @@ -117,7 +91,7 @@ pub struct Config { | |||
| 117 | impl Default for Config { | 91 | impl Default for Config { |
| 118 | fn default() -> Self { | 92 | fn default() -> Self { |
| 119 | Self { | 93 | Self { |
| 120 | baudrate: 9600, | 94 | baudrate: 115200, |
| 121 | data_bits: DataBits::DataBits8, | 95 | data_bits: DataBits::DataBits8, |
| 122 | stop_bits: StopBits::Stop1, | 96 | stop_bits: StopBits::Stop1, |
| 123 | parity: Parity::ParityNone, | 97 | parity: Parity::ParityNone, |
| @@ -131,59 +105,72 @@ impl Default for Config { | |||
| 131 | /// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time | 105 | /// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time |
| 132 | /// T: the peripheral instance type allowing usage of peripheral specific registers | 106 | /// T: the peripheral instance type allowing usage of peripheral specific registers |
| 133 | /// M: the operating mode of USART peripheral | 107 | /// M: the operating mode of USART peripheral |
| 134 | pub struct Usart<'d, T: Instance, M: Mode> { | 108 | pub struct Usart<'d, M: Mode> { |
| 135 | tx: UsartTx<'d, T, M>, | 109 | tx: UsartTx<'d, M>, |
| 136 | rx: UsartRx<'d, T, M>, | 110 | rx: UsartRx<'d, M>, |
| 137 | } | 111 | } |
| 138 | 112 | ||
| 139 | pub struct UsartTx<'d, T: Instance, M: Mode> { | 113 | pub struct UsartTx<'d, M: Mode> { |
| 140 | phantom: PhantomData<(&'d (), T, M)>, | 114 | info: &'static Info, |
| 115 | phantom: PhantomData<(&'d (), M)>, | ||
| 141 | } | 116 | } |
| 142 | 117 | ||
| 143 | pub struct UsartRx<'d, T: Instance, M: Mode> { | 118 | pub struct UsartRx<'d, M: Mode> { |
| 144 | phantom: PhantomData<(&'d (), T, M)>, | 119 | info: &'static Info, |
| 120 | phantom: PhantomData<(&'d (), M)>, | ||
| 145 | } | 121 | } |
| 146 | 122 | ||
| 147 | impl<'d, T: Instance, M: Mode> UsartTx<'d, T, M> { | 123 | impl<'d, M: Mode> UsartTx<'d, M> { |
| 148 | pub fn new(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { | 124 | pub fn new<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { |
| 149 | Usart::<T, M>::init(Some(tx.into()), None, config); | 125 | Usart::<M>::init::<T>(Some(tx.into()), None, config); |
| 150 | Self::new_inner() | 126 | Self::new_inner(T::info()) |
| 151 | } | 127 | } |
| 152 | 128 | ||
| 153 | #[inline] | 129 | #[inline] |
| 154 | fn new_inner() -> Self { | 130 | fn new_inner(info: &'static Info) -> Self { |
| 155 | Self { phantom: PhantomData } | 131 | Self { |
| 132 | info, | ||
| 133 | phantom: PhantomData, | ||
| 134 | } | ||
| 156 | } | 135 | } |
| 157 | 136 | ||
| 158 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 137 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 159 | T::blocking_write(buffer) | 138 | for &b in buffer { |
| 139 | while !(self.info.usart_reg.fifostat().read().txnotfull()) {} | ||
| 140 | self.info.usart_reg.fifowr().write(|w| w.set_txdata(b as u16)); | ||
| 141 | } | ||
| 142 | Ok(()) | ||
| 160 | } | 143 | } |
| 161 | 144 | ||
| 162 | pub fn blocking_flush(&mut self) -> Result<(), Error> { | 145 | pub fn blocking_flush(&mut self) -> Result<(), Error> { |
| 163 | T::blocking_flush() | 146 | while !(self.info.usart_reg.fifostat().read().txempty()) {} |
| 147 | Ok(()) | ||
| 164 | } | 148 | } |
| 165 | 149 | ||
| 166 | pub fn tx_busy(&self) -> bool { | 150 | pub fn tx_busy(&self) -> bool { |
| 167 | T::tx_busy() | 151 | !(self.info.usart_reg.fifostat().read().txempty()) |
| 168 | } | 152 | } |
| 169 | } | 153 | } |
| 170 | 154 | ||
| 171 | impl<'d, T: Instance> UsartTx<'d, T, Blocking> { | 155 | impl<'d> UsartTx<'d, Blocking> { |
| 172 | pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { | 156 | pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { |
| 173 | Usart::<T, Blocking>::init(Some(tx.into()), None, config); | 157 | Usart::<Blocking>::init::<T>(Some(tx.into()), None, config); |
| 174 | Self::new_inner() | 158 | Self::new_inner(T::info()) |
| 175 | } | 159 | } |
| 176 | } | 160 | } |
| 177 | 161 | ||
| 178 | impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> { | 162 | impl<'d, M: Mode> UsartRx<'d, M> { |
| 179 | pub fn new(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { | 163 | pub fn new<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { |
| 180 | Usart::<T, M>::init(None, Some(rx.into()), config); | 164 | Usart::<M>::init::<T>(None, Some(rx.into()), config); |
| 181 | Self::new_inner() | 165 | Self::new_inner(T::info()) |
| 182 | } | 166 | } |
| 183 | 167 | ||
| 184 | #[inline] | 168 | #[inline] |
| 185 | fn new_inner() -> Self { | 169 | fn new_inner(info: &'static Info) -> Self { |
| 186 | Self { phantom: PhantomData } | 170 | Self { |
| 171 | info, | ||
| 172 | phantom: PhantomData, | ||
| 173 | } | ||
| 187 | } | 174 | } |
| 188 | 175 | ||
| 189 | pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { | 176 | pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { |
| @@ -201,19 +188,35 @@ impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> { | |||
| 201 | /// - Ok(n) -> read n bytes | 188 | /// - Ok(n) -> read n bytes |
| 202 | /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte | 189 | /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte |
| 203 | fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> { | 190 | fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> { |
| 204 | T::drain_fifo(buffer) | 191 | for (i, b) in buffer.iter_mut().enumerate() { |
| 192 | while !(self.info.usart_reg.fifostat().read().rxnotempty()) {} | ||
| 193 | if self.info.usart_reg.fifostat().read().rxerr() { | ||
| 194 | return Err((i, Error::Overrun)); | ||
| 195 | } else if self.info.usart_reg.fifordnopop().read().parityerr() { | ||
| 196 | return Err((i, Error::Parity)); | ||
| 197 | } else if self.info.usart_reg.fifordnopop().read().framerr() { | ||
| 198 | return Err((i, Error::Framing)); | ||
| 199 | } else if self.info.usart_reg.fifordnopop().read().rxnoise() { | ||
| 200 | return Err((i, Error::Noise)); | ||
| 201 | } else if self.info.usart_reg.intstat().read().deltarxbrk() { | ||
| 202 | return Err((i, Error::Break)); | ||
| 203 | } | ||
| 204 | let dr = self.info.usart_reg.fiford().read().rxdata() as u8; | ||
| 205 | *b = dr; | ||
| 206 | } | ||
| 207 | Ok(buffer.len()) | ||
| 205 | } | 208 | } |
| 206 | } | 209 | } |
| 207 | 210 | ||
| 208 | impl<'d, T: Instance> UsartRx<'d, T, Blocking> { | 211 | impl<'d> UsartRx<'d, Blocking> { |
| 209 | pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { | 212 | pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { |
| 210 | Usart::<T, Blocking>::init(None, Some(rx.into()), config); | 213 | Usart::<Blocking>::init::<T>(None, Some(rx.into()), config); |
| 211 | Self::new_inner() | 214 | Self::new_inner(T::info()) |
| 212 | } | 215 | } |
| 213 | } | 216 | } |
| 214 | 217 | ||
| 215 | impl<'d, T: Instance> Usart<'d, T, Blocking> { | 218 | impl<'d> Usart<'d, Blocking> { |
| 216 | pub fn new_blocking( | 219 | pub fn new_blocking<T: Instance>( |
| 217 | usart: Peri<'d, T>, | 220 | usart: Peri<'d, T>, |
| 218 | tx: Peri<'d, impl TxPin<T>>, | 221 | tx: Peri<'d, impl TxPin<T>>, |
| 219 | rx: Peri<'d, impl RxPin<T>>, | 222 | rx: Peri<'d, impl RxPin<T>>, |
| @@ -223,29 +226,70 @@ impl<'d, T: Instance> Usart<'d, T, Blocking> { | |||
| 223 | } | 226 | } |
| 224 | } | 227 | } |
| 225 | 228 | ||
| 226 | impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { | 229 | impl<'d, M: Mode> Usart<'d, M> { |
| 227 | fn new_inner(_usart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, config: Config) -> Self { | 230 | fn new_inner<T: Instance>( |
| 228 | Self::init(Some(tx.reborrow()), Some(rx.reborrow()), config); | 231 | _usart: Peri<'d, T>, |
| 232 | mut tx: Peri<'d, AnyPin>, | ||
| 233 | mut rx: Peri<'d, AnyPin>, | ||
| 234 | config: Config, | ||
| 235 | ) -> Self { | ||
| 236 | Self::init::<T>(Some(tx.reborrow()), Some(rx.reborrow()), config); | ||
| 229 | Self { | 237 | Self { |
| 230 | tx: UsartTx::new_inner(), | 238 | tx: UsartTx::new_inner(T::info()), |
| 231 | rx: UsartRx::new_inner(), | 239 | rx: UsartRx::new_inner(T::info()), |
| 232 | } | 240 | } |
| 233 | } | 241 | } |
| 234 | 242 | ||
| 235 | fn init(_tx: Option<Peri<'_, AnyPin>>, _rx: Option<Peri<'_, AnyPin>>, config: Config) { | 243 | fn init<T: Instance>(tx: Option<Peri<'_, AnyPin>>, rx: Option<Peri<'_, AnyPin>>, config: Config) { |
| 236 | T::enable_clock(); | 244 | Self::configure_flexcomm(T::info().fc_reg, T::instance_number()); |
| 237 | T::reset_flexcomm(); | 245 | Self::configure_clock::<T>(&config); |
| 238 | let source_clock: u32 = T::select_clock(config.baudrate); | 246 | Self::pin_config::<T>(tx, rx); |
| 239 | T::configure_flexcomm(); | 247 | Self::configure_usart(T::info(), &config); |
| 240 | T::tx_pin_config(); | ||
| 241 | T::rx_pin_config(); | ||
| 242 | Self::set_baudrate(source_clock, config.baudrate); | ||
| 243 | T::configure_usart(config); | ||
| 244 | T::disable_dma(); | ||
| 245 | T::enable_usart(); | ||
| 246 | } | 248 | } |
| 247 | 249 | ||
| 248 | fn set_baudrate(source_clock: u32, baudrate: u32) { | 250 | fn configure_clock<T: Instance>(config: &Config) { |
| 251 | // Select source clock | ||
| 252 | |||
| 253 | // Adaptive clock choice based on baud rate | ||
| 254 | // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled' | ||
| 255 | // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) | ||
| 256 | // and fractional divider (fractional rate divider). | ||
| 257 | |||
| 258 | // By default, oversampling rate is 16 which is an industry standard. | ||
| 259 | // That means 16 clocks are used to deliver the byte to recipient. | ||
| 260 | // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well. | ||
| 261 | |||
| 262 | // Minimum and maximum values were computed taking these formulas into account: | ||
| 263 | // For minimum value, MULT = 0, BRGVAL = 0 | ||
| 264 | // For maximum value, MULT = 255, BRGVAL = 255 | ||
| 265 | // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV) | ||
| 266 | // By default, OSRVAL = 15 (see above) | ||
| 267 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) | ||
| 268 | let source_clock = match config.baudrate { | ||
| 269 | 750_001..=6_000_000 => { | ||
| 270 | SYSCON | ||
| 271 | .fcclksel(T::instance_number()) | ||
| 272 | .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X3)); // 96 MHz | ||
| 273 | 96_000_000 | ||
| 274 | } | ||
| 275 | 1501..=750_000 => { | ||
| 276 | SYSCON | ||
| 277 | .fcclksel(T::instance_number()) | ||
| 278 | .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X2)); // 12 MHz | ||
| 279 | 12_000_000 | ||
| 280 | } | ||
| 281 | 121..=1500 => { | ||
| 282 | SYSCON | ||
| 283 | .fcclksel(T::instance_number()) | ||
| 284 | .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X4)); // 1 MHz | ||
| 285 | 1_000_000 | ||
| 286 | } | ||
| 287 | _ => { | ||
| 288 | panic!("{} baudrate is not permitted in this mode", config.baudrate); | ||
| 289 | } | ||
| 290 | }; | ||
| 291 | // Calculate MULT and BRG values based on baudrate | ||
| 292 | |||
| 249 | // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) | 293 | // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) |
| 250 | // and fractional divider (fractional rate divider). | 294 | // and fractional divider (fractional rate divider). |
| 251 | // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15 | 295 | // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15 |
| @@ -274,14 +318,167 @@ impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { | |||
| 274 | // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate. | 318 | // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate. |
| 275 | // The deduced formulas are written below. | 319 | // The deduced formulas are written below. |
| 276 | 320 | ||
| 277 | let brg_value = (source_clock / (16 * baudrate)).min(255); | 321 | let brg_value = (source_clock / (16 * config.baudrate)).min(255); |
| 278 | let raw_clock = source_clock / (16 * brg_value); | 322 | let raw_clock = source_clock / (16 * brg_value); |
| 279 | let mult_value = ((raw_clock * 256 / baudrate) - 256).min(255); | 323 | let mult_value = ((raw_clock * 256 / config.baudrate) - 256).min(255); |
| 280 | T::set_baudrate(mult_value as u8, brg_value as u8); | 324 | |
| 325 | // Write values to the registers | ||
| 326 | |||
| 327 | // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV) | ||
| 328 | // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value | ||
| 329 | // to yield a denominator vale of 256. All other values are not supported | ||
| 330 | SYSCON.flexfrgctrl(T::instance_number()).modify(|w| { | ||
| 331 | w.set_div(0xFF); | ||
| 332 | w.set_mult(mult_value as u8); | ||
| 333 | }); | ||
| 334 | |||
| 335 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) | ||
| 336 | // By default, oversampling is 16x, i.e. OSRVAL = 15 | ||
| 337 | |||
| 338 | // Typical industry standard USARTs use a 16x oversample clock to transmit and receive | ||
| 339 | // asynchronous data. This is the number of BRG clocks used for one data bit. The | ||
| 340 | // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x | ||
| 341 | // oversample clock. There is no oversampling in synchronous modes. | ||
| 342 | T::info() | ||
| 343 | .usart_reg | ||
| 344 | .brg() | ||
| 345 | .modify(|w| w.set_brgval((brg_value - 1) as u16)); | ||
| 346 | } | ||
| 347 | |||
| 348 | fn pin_config<T: Instance>(tx: Option<Peri<'_, AnyPin>>, rx: Option<Peri<'_, AnyPin>>) { | ||
| 349 | if let Some(tx_pin) = tx { | ||
| 350 | match_iocon!(register, tx_pin.pin_bank(), tx_pin.pin_number(), { | ||
| 351 | register.modify(|w| { | ||
| 352 | w.set_func(T::tx_pin_func()); | ||
| 353 | w.set_mode(iocon::vals::PioMode::INACTIVE); | ||
| 354 | w.set_slew(iocon::vals::PioSlew::STANDARD); | ||
| 355 | w.set_invert(false); | ||
| 356 | w.set_digimode(iocon::vals::PioDigimode::DIGITAL); | ||
| 357 | w.set_od(iocon::vals::PioOd::NORMAL); | ||
| 358 | }); | ||
| 359 | }) | ||
| 360 | } | ||
| 361 | |||
| 362 | if let Some(rx_pin) = rx { | ||
| 363 | match_iocon!(register, rx_pin.pin_bank(), rx_pin.pin_number(), { | ||
| 364 | register.modify(|w| { | ||
| 365 | w.set_func(T::rx_pin_func()); | ||
| 366 | w.set_mode(iocon::vals::PioMode::INACTIVE); | ||
| 367 | w.set_slew(iocon::vals::PioSlew::STANDARD); | ||
| 368 | w.set_invert(false); | ||
| 369 | w.set_digimode(iocon::vals::PioDigimode::DIGITAL); | ||
| 370 | w.set_od(iocon::vals::PioOd::NORMAL); | ||
| 371 | }); | ||
| 372 | }) | ||
| 373 | }; | ||
| 374 | } | ||
| 375 | |||
| 376 | fn configure_flexcomm(flexcomm_register: crate::pac::flexcomm::Flexcomm, instance_number: usize) { | ||
| 377 | critical_section::with(|_cs| { | ||
| 378 | if !(SYSCON.ahbclkctrl0().read().iocon()) { | ||
| 379 | SYSCON.ahbclkctrl0().modify(|w| w.set_iocon(true)); | ||
| 380 | } | ||
| 381 | }); | ||
| 382 | critical_section::with(|_cs| { | ||
| 383 | if !(SYSCON.ahbclkctrl1().read().fc(instance_number)) { | ||
| 384 | SYSCON.ahbclkctrl1().modify(|w| w.set_fc(instance_number, true)); | ||
| 385 | } | ||
| 386 | }); | ||
| 387 | SYSCON | ||
| 388 | .presetctrl1() | ||
| 389 | .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::ASSERTED)); | ||
| 390 | SYSCON | ||
| 391 | .presetctrl1() | ||
| 392 | .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED)); | ||
| 393 | flexcomm_register | ||
| 394 | .pselid() | ||
| 395 | .modify(|w| w.set_persel(flexcomm::vals::Persel::USART)); | ||
| 396 | } | ||
| 397 | |||
| 398 | fn configure_usart(info: &'static Info, config: &Config) { | ||
| 399 | let registers = info.usart_reg; | ||
| 400 | // See section 34.6.1 | ||
| 401 | registers.cfg().modify(|w| { | ||
| 402 | // LIN break mode enable | ||
| 403 | // Disabled. Break detect and generate is configured for normal operation. | ||
| 404 | w.set_linmode(false); | ||
| 405 | //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the | ||
| 406 | //input pin, or from the USART’s own RTS if loopback mode is enabled. | ||
| 407 | // No flow control. The transmitter does not receive any automatic flow control signal. | ||
| 408 | w.set_ctsen(false); | ||
| 409 | // Selects synchronous or asynchronous operation. | ||
| 410 | w.set_syncen(usart::vals::Syncen::ASYNCHRONOUS_MODE); | ||
| 411 | // Selects the clock polarity and sampling edge of received data in synchronous mode. | ||
| 412 | w.set_clkpol(usart::vals::Clkpol::RISING_EDGE); | ||
| 413 | // Synchronous mode Master select. | ||
| 414 | // When synchronous mode is enabled, the USART is a master. | ||
| 415 | w.set_syncmst(usart::vals::Syncmst::MASTER); | ||
| 416 | // Selects data loopback mode | ||
| 417 | w.set_loop_(usart::vals::Loop::NORMAL); | ||
| 418 | // Output Enable Turnaround time enable for RS-485 operation. | ||
| 419 | // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of | ||
| 420 | // the last stop bit of a transmission. | ||
| 421 | w.set_oeta(false); | ||
| 422 | // Output enable select. | ||
| 423 | // Standard. The RTS signal is used as the standard flow control function. | ||
| 424 | w.set_oesel(usart::vals::Oesel::STANDARD); | ||
| 425 | // Automatic address matching enable. | ||
| 426 | // Disabled. When addressing is enabled by ADDRDET, address matching is done by | ||
| 427 | // software. This provides the possibility of versatile addressing (e.g. respond to more | ||
| 428 | // than one address) | ||
| 429 | w.set_autoaddr(false); | ||
| 430 | // Output enable polarity. | ||
| 431 | // Low. If selected by OESEL, the output enable is active low. | ||
| 432 | w.set_oepol(usart::vals::Oepol::LOW); | ||
| 433 | }); | ||
| 434 | |||
| 435 | // Configurations based on the config written by a user | ||
| 436 | registers.cfg().modify(|w| { | ||
| 437 | w.set_datalen(match config.data_bits { | ||
| 438 | DataBits::DataBits7 => usart::vals::Datalen::BIT_7, | ||
| 439 | DataBits::DataBits8 => usart::vals::Datalen::BIT_8, | ||
| 440 | DataBits::DataBits9 => usart::vals::Datalen::BIT_9, | ||
| 441 | }); | ||
| 442 | w.set_paritysel(match config.parity { | ||
| 443 | Parity::ParityNone => usart::vals::Paritysel::NO_PARITY, | ||
| 444 | Parity::ParityEven => usart::vals::Paritysel::EVEN_PARITY, | ||
| 445 | Parity::ParityOdd => usart::vals::Paritysel::ODD_PARITY, | ||
| 446 | }); | ||
| 447 | w.set_stoplen(match config.stop_bits { | ||
| 448 | StopBits::Stop1 => usart::vals::Stoplen::BIT_1, | ||
| 449 | StopBits::Stop2 => usart::vals::Stoplen::BITS_2, | ||
| 450 | }); | ||
| 451 | w.set_rxpol(match config.invert_rx { | ||
| 452 | false => usart::vals::Rxpol::STANDARD, | ||
| 453 | true => usart::vals::Rxpol::INVERTED, | ||
| 454 | }); | ||
| 455 | w.set_txpol(match config.invert_tx { | ||
| 456 | false => usart::vals::Txpol::STANDARD, | ||
| 457 | true => usart::vals::Txpol::INVERTED, | ||
| 458 | }); | ||
| 459 | }); | ||
| 460 | |||
| 461 | // DMA-related settings | ||
| 462 | registers.fifocfg().modify(|w| { | ||
| 463 | w.set_dmatx(false); | ||
| 464 | w.set_dmatx(false); | ||
| 465 | }); | ||
| 466 | |||
| 467 | // Enabling USART | ||
| 468 | registers.fifocfg().modify(|w| { | ||
| 469 | w.set_enabletx(true); | ||
| 470 | w.set_enablerx(true); | ||
| 471 | }); | ||
| 472 | registers.cfg().modify(|w| w.set_enable(true)); | ||
| 473 | |||
| 474 | // Drain RX FIFO in case it still has some unrelevant data | ||
| 475 | while registers.fifostat().read().rxnotempty() { | ||
| 476 | let _ = registers.fiford().read().0; | ||
| 477 | } | ||
| 281 | } | 478 | } |
| 282 | } | 479 | } |
| 283 | 480 | ||
| 284 | impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { | 481 | impl<'d, M: Mode> Usart<'d, M> { |
| 285 | /// Transmit the provided buffer blocking execution until done. | 482 | /// Transmit the provided buffer blocking execution until done. |
| 286 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 483 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 287 | self.tx.blocking_write(buffer) | 484 | self.tx.blocking_write(buffer) |
| @@ -304,19 +501,19 @@ impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { | |||
| 304 | 501 | ||
| 305 | /// Split the Usart into a transmitter and receiver, which is particularly | 502 | /// Split the Usart into a transmitter and receiver, which is particularly |
| 306 | /// useful when having two tasks correlating to transmitting and receiving. | 503 | /// useful when having two tasks correlating to transmitting and receiving. |
| 307 | pub fn split(self) -> (UsartTx<'d, T, M>, UsartRx<'d, T, M>) { | 504 | pub fn split(self) -> (UsartTx<'d, M>, UsartRx<'d, M>) { |
| 308 | (self.tx, self.rx) | 505 | (self.tx, self.rx) |
| 309 | } | 506 | } |
| 310 | 507 | ||
| 311 | /// Split the Usart into a transmitter and receiver by mutable reference, | 508 | /// Split the Usart into a transmitter and receiver by mutable reference, |
| 312 | /// which is particularly useful when having two tasks correlating to | 509 | /// which is particularly useful when having two tasks correlating to |
| 313 | /// transmitting and receiving. | 510 | /// transmitting and receiving. |
| 314 | pub fn split_ref(&mut self) -> (&mut UsartTx<'d, T, M>, &mut UsartRx<'d, T, M>) { | 511 | pub fn split_ref(&mut self) -> (&mut UsartTx<'d, M>, &mut UsartRx<'d, M>) { |
| 315 | (&mut self.tx, &mut self.rx) | 512 | (&mut self.tx, &mut self.rx) |
| 316 | } | 513 | } |
| 317 | } | 514 | } |
| 318 | 515 | ||
| 319 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, T, M> { | 516 | impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> { |
| 320 | type Error = Error; | 517 | type Error = Error; |
| 321 | 518 | ||
| 322 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | 519 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { |
| @@ -328,7 +525,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for | |||
| 328 | } | 525 | } |
| 329 | } | 526 | } |
| 330 | 527 | ||
| 331 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Usart<'d, T, M> { | 528 | impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Usart<'d, M> { |
| 332 | type Error = Error; | 529 | type Error = Error; |
| 333 | 530 | ||
| 334 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | 531 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { |
| @@ -340,11 +537,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for | |||
| 340 | } | 537 | } |
| 341 | } | 538 | } |
| 342 | 539 | ||
| 343 | impl<'d, T: Instance> embedded_io::ErrorType for UsartTx<'d, T, Blocking> { | 540 | impl<'d> embedded_io::ErrorType for UsartTx<'d, Blocking> { |
| 344 | type Error = Error; | 541 | type Error = Error; |
| 345 | } | 542 | } |
| 346 | 543 | ||
| 347 | impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> { | 544 | impl<'d> embedded_io::Write for UsartTx<'d, Blocking> { |
| 348 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | 545 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { |
| 349 | self.blocking_write(buf).map(|_| buf.len()) | 546 | self.blocking_write(buf).map(|_| buf.len()) |
| 350 | } | 547 | } |
| @@ -354,21 +551,21 @@ impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> { | |||
| 354 | } | 551 | } |
| 355 | } | 552 | } |
| 356 | 553 | ||
| 357 | impl<'d, T: Instance> embedded_io::ErrorType for UsartRx<'d, T, Blocking> { | 554 | impl<'d> embedded_io::ErrorType for UsartRx<'d, Blocking> { |
| 358 | type Error = Error; | 555 | type Error = Error; |
| 359 | } | 556 | } |
| 360 | 557 | ||
| 361 | impl<'d, T: Instance> embedded_io::Read for UsartRx<'d, T, Blocking> { | 558 | impl<'d> embedded_io::Read for UsartRx<'d, Blocking> { |
| 362 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | 559 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { |
| 363 | self.blocking_read(buf).map(|_| buf.len()) | 560 | self.blocking_read(buf).map(|_| buf.len()) |
| 364 | } | 561 | } |
| 365 | } | 562 | } |
| 366 | 563 | ||
| 367 | impl<'d, T: Instance> embedded_io::ErrorType for Usart<'d, T, Blocking> { | 564 | impl<'d> embedded_io::ErrorType for Usart<'d, Blocking> { |
| 368 | type Error = Error; | 565 | type Error = Error; |
| 369 | } | 566 | } |
| 370 | 567 | ||
| 371 | impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> { | 568 | impl<'d> embedded_io::Write for Usart<'d, Blocking> { |
| 372 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | 569 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { |
| 373 | self.blocking_write(buf).map(|_| buf.len()) | 570 | self.blocking_write(buf).map(|_| buf.len()) |
| 374 | } | 571 | } |
| @@ -378,468 +575,69 @@ impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> { | |||
| 378 | } | 575 | } |
| 379 | } | 576 | } |
| 380 | 577 | ||
| 381 | impl<'d, T: Instance> embedded_io::Read for Usart<'d, T, Blocking> { | 578 | impl<'d> embedded_io::Read for Usart<'d, Blocking> { |
| 382 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | 579 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { |
| 383 | self.blocking_read(buf).map(|_| buf.len()) | 580 | self.blocking_read(buf).map(|_| buf.len()) |
| 384 | } | 581 | } |
| 385 | } | 582 | } |
| 386 | 583 | ||
| 387 | type UsartRegBlock = crate::pac::usart0::RegisterBlock; | 584 | struct Info { |
| 388 | 585 | usart_reg: UsartReg, | |
| 389 | mod sealed { | 586 | fc_reg: FlexcommReg, |
| 390 | use crate::usart::inner::UsartRegBlock; | 587 | } |
| 391 | use crate::usart::{Config, Error}; | ||
| 392 | pub trait SealedInstance { | ||
| 393 | fn usart_reg() -> &'static UsartRegBlock; | ||
| 394 | fn enable_clock(); | ||
| 395 | fn select_clock(baudrate: u32) -> u32; | ||
| 396 | fn configure_flexcomm(); | ||
| 397 | fn set_baudrate(mult_value: u8, brg_value: u8); | ||
| 398 | fn reset_flexcomm(); | ||
| 399 | fn tx_pin_config(); | ||
| 400 | fn rx_pin_config(); | ||
| 401 | |||
| 402 | fn configure_usart(config: Config) { | ||
| 403 | // See section 34.6.1 | ||
| 404 | Self::usart_reg().cfg.modify(|_, w| { | ||
| 405 | // LIN break mode enable | ||
| 406 | w.linmode() | ||
| 407 | // Disabled. Break detect and generate is configured for normal operation. | ||
| 408 | .disabled() | ||
| 409 | //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the | ||
| 410 | //input pin, or from the USART’s own RTS if loopback mode is enabled. | ||
| 411 | .ctsen() | ||
| 412 | // No flow control. The transmitter does not receive any automatic flow control signal. | ||
| 413 | .disabled() | ||
| 414 | // Selects synchronous or asynchronous operation. | ||
| 415 | .syncen() | ||
| 416 | .asynchronous_mode() | ||
| 417 | // Selects the clock polarity and sampling edge of received data in synchronous mode. | ||
| 418 | .clkpol() | ||
| 419 | .rising_edge() | ||
| 420 | // Synchronous mode Master select. | ||
| 421 | .syncmst() | ||
| 422 | // When synchronous mode is enabled, the USART is a master. | ||
| 423 | .master() | ||
| 424 | // Selects data loopback mode | ||
| 425 | .loop_() | ||
| 426 | // Normal operation | ||
| 427 | .normal() | ||
| 428 | // Output Enable Turnaround time enable for RS-485 operation. | ||
| 429 | .oeta() | ||
| 430 | // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of | ||
| 431 | // the last stop bit of a transmission. | ||
| 432 | .disabled() | ||
| 433 | // Output enable select. | ||
| 434 | .oesel() | ||
| 435 | // Standard. The RTS signal is used as the standard flow control function. | ||
| 436 | .standard() | ||
| 437 | // Automatic address matching enable. | ||
| 438 | .autoaddr() | ||
| 439 | // Disabled. When addressing is enabled by ADDRDET, address matching is done by | ||
| 440 | // software. This provides the possibility of versatile addressing (e.g. respond to more | ||
| 441 | // than one address) | ||
| 442 | .disabled() | ||
| 443 | // Output enable polarity. | ||
| 444 | .oepol() | ||
| 445 | // Low. If selected by OESEL, the output enable is active low. | ||
| 446 | .low() | ||
| 447 | }); | ||
| 448 | 588 | ||
| 449 | Self::usart_reg().cfg.modify(|_, w| unsafe { | 589 | trait SealedInstance { |
| 450 | w.datalen() | 590 | fn info() -> &'static Info; |
| 451 | .bits(config.data_bits.bits()) | 591 | fn instance_number() -> usize; |
| 452 | .paritysel() | 592 | fn tx_pin_func() -> PioFunc; |
| 453 | .bits(config.parity.bits()) | 593 | fn rx_pin_func() -> PioFunc; |
| 454 | .stoplen() | ||
| 455 | .bit(config.stop_bits.bits()) | ||
| 456 | .rxpol() | ||
| 457 | .bit(config.invert_rx) | ||
| 458 | .txpol() | ||
| 459 | .bit(config.invert_tx) | ||
| 460 | }); | ||
| 461 | } | ||
| 462 | fn disable_dma() { | ||
| 463 | Self::usart_reg() | ||
| 464 | .fifocfg | ||
| 465 | .modify(|_, w| w.dmatx().disabled().dmarx().disabled()); | ||
| 466 | } | ||
| 467 | fn enable_usart() { | ||
| 468 | Self::usart_reg() | ||
| 469 | .fifocfg | ||
| 470 | .modify(|_, w| w.enabletx().enabled().enablerx().enabled()); | ||
| 471 | Self::usart_reg().cfg.modify(|_, w| w.enable().enabled()); | ||
| 472 | while Self::usart_reg().fifostat.read().rxnotempty().bit_is_set() { | ||
| 473 | let _ = Self::usart_reg().fiford.read().bits(); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | fn blocking_write(buffer: &[u8]) -> Result<(), Error> { | ||
| 477 | for &b in buffer { | ||
| 478 | while Self::usart_reg().fifostat.read().txnotfull().bit_is_clear() {} | ||
| 479 | Self::usart_reg() | ||
| 480 | .fifowr | ||
| 481 | .modify(|_, w| unsafe { w.txdata().bits(b as u16) }); | ||
| 482 | } | ||
| 483 | Ok(()) | ||
| 484 | } | ||
| 485 | fn blocking_flush() -> Result<(), Error> { | ||
| 486 | while Self::usart_reg().fifostat.read().txempty().bit_is_clear() {} | ||
| 487 | Ok(()) | ||
| 488 | } | ||
| 489 | fn tx_busy() -> bool { | ||
| 490 | Self::usart_reg().fifostat.read().txempty().bit_is_clear() | ||
| 491 | } | ||
| 492 | fn drain_fifo(buffer: &mut [u8]) -> Result<usize, (usize, Error)> { | ||
| 493 | for (i, b) in buffer.iter_mut().enumerate() { | ||
| 494 | while Self::usart_reg().fifostat.read().rxnotempty().bit_is_clear() {} | ||
| 495 | |||
| 496 | if Self::usart_reg().fifostat.read().rxerr().bit_is_set() { | ||
| 497 | return Err((i, Error::Overrun)); | ||
| 498 | } else if Self::usart_reg().fifordnopop.read().parityerr().bit_is_set() { | ||
| 499 | return Err((i, Error::Parity)); | ||
| 500 | } else if Self::usart_reg().fifordnopop.read().framerr().bit_is_set() { | ||
| 501 | return Err((i, Error::Framing)); | ||
| 502 | } else if Self::usart_reg().fifordnopop.read().rxnoise().bit_is_set() { | ||
| 503 | return Err((i, Error::Noise)); | ||
| 504 | } else if Self::usart_reg().intstat.read().deltarxbrk().bit_is_set() { | ||
| 505 | return Err((i, Error::Break)); | ||
| 506 | } | ||
| 507 | let dr = Self::usart_reg().fiford.read().bits() as u8; | ||
| 508 | *b = dr; | ||
| 509 | } | ||
| 510 | Ok(buffer.len()) | ||
| 511 | } | ||
| 512 | } | ||
| 513 | } | 594 | } |
| 514 | 595 | ||
| 515 | /// UART instance. | 596 | /// UART instance. |
| 516 | #[allow(private_bounds)] | 597 | #[allow(private_bounds)] |
| 517 | pub trait Instance: sealed::SealedInstance + PeripheralType {} | 598 | pub trait Instance: SealedInstance + PeripheralType {} |
| 518 | 599 | ||
| 519 | #[macro_export] | ||
| 520 | macro_rules! impl_instance { | 600 | macro_rules! impl_instance { |
| 521 | ( | 601 | ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => { |
| 522 | $inst:ident, | 602 | impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst { |
| 523 | usart_peripheral: $USARTX:ident, | 603 | fn info() -> &'static Info { |
| 524 | usart_crate: $usartX:ident, | 604 | static INFO: Info = Info { |
| 525 | 605 | usart_reg: crate::pac::$inst, | |
| 526 | flexcomm: { | 606 | fc_reg: crate::pac::$fc, |
| 527 | field: $FLEXCOMM_FIELD:ident, | ||
| 528 | clock_field: $FLEXCOMM_CLK_FIELD:ident | ||
| 529 | }, | ||
| 530 | |||
| 531 | reset: { | ||
| 532 | bit: $RESET_BIT:ident | ||
| 533 | }, | ||
| 534 | |||
| 535 | clock: { | ||
| 536 | sel_field: $CLKSEL_FIELD:ident, | ||
| 537 | frg_field: $FRG_FIELD:ident | ||
| 538 | }, | ||
| 539 | |||
| 540 | pins: { | ||
| 541 | tx: $TX_IOCON:ident => $TX_FUNC:expr, | ||
| 542 | rx: $RX_IOCON:ident => $RX_FUNC:expr | ||
| 543 | } | ||
| 544 | |||
| 545 | ) => { | ||
| 546 | impl $crate::usart::SealedInstance for $crate::peripherals::$inst { | ||
| 547 | fn usart_reg() -> &'static UsartRegBlock { | ||
| 548 | unsafe { &*$crate::pac::$USARTX::ptr() } | ||
| 549 | } | ||
| 550 | |||
| 551 | fn enable_clock() { | ||
| 552 | critical_section::with(|_cs| { | ||
| 553 | if syscon_reg().ahbclkctrl0.read().iocon().is_disable() { | ||
| 554 | syscon_reg().ahbclkctrl0.modify(|_, w| w.iocon().enable()); | ||
| 555 | } | ||
| 556 | if syscon_reg().ahbclkctrl1.read().$FLEXCOMM_CLK_FIELD().is_disable() { | ||
| 557 | syscon_reg() | ||
| 558 | .ahbclkctrl1 | ||
| 559 | .modify(|_, w| w.$FLEXCOMM_CLK_FIELD().enable()); | ||
| 560 | } | ||
| 561 | }); | ||
| 562 | } | ||
| 563 | |||
| 564 | fn configure_flexcomm() { | ||
| 565 | let flexcomm = unsafe { &*$crate::pac::$FLEXCOMM_FIELD::ptr() }; | ||
| 566 | flexcomm.pselid.modify(|_, w| w.persel().usart()); | ||
| 567 | } | ||
| 568 | |||
| 569 | fn reset_flexcomm() { | ||
| 570 | syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().set_bit()); | ||
| 571 | syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().clear_bit()); | ||
| 572 | } | ||
| 573 | |||
| 574 | fn select_clock(baudrate: u32) -> u32 { | ||
| 575 | // Adaptive clock choice based on baud rate | ||
| 576 | // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled' | ||
| 577 | // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) | ||
| 578 | // and fractional divider (fractional rate divider). | ||
| 579 | |||
| 580 | // By default, oversampling rate is 16 which is an industry standard. | ||
| 581 | // That means 16 clocks are used to deliver the byte to recipient. | ||
| 582 | // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well. | ||
| 583 | |||
| 584 | // Minimum and maximum values were computed taking these formulas into account: | ||
| 585 | // For minimum value, MULT = 0, BRGVAL = 0 | ||
| 586 | // For maximum value, MULT = 255, BRGVAL = 255 | ||
| 587 | // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV) | ||
| 588 | // By default, OSRVAL = 15 (see above) | ||
| 589 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) | ||
| 590 | return match baudrate { | ||
| 591 | 750_001..=6000000 => { | ||
| 592 | syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x3()); // 96 MHz | ||
| 593 | 96_000_000 | ||
| 594 | } | ||
| 595 | 1501..=750_000 => { | ||
| 596 | syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x2()); // 12 MHz | ||
| 597 | 12_000_000 | ||
| 598 | } | ||
| 599 | 121..=1500 => { | ||
| 600 | syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x4()); // 1 MHz | ||
| 601 | 1_000_000 | ||
| 602 | } | ||
| 603 | _ => { | ||
| 604 | panic!("{} baudrate is not permitted in this mode", baudrate); | ||
| 605 | } | ||
| 606 | }; | 607 | }; |
| 608 | &INFO | ||
| 607 | } | 609 | } |
| 608 | 610 | #[inline] | |
| 609 | fn set_baudrate(mult_value: u8, brg_value: u8) { | 611 | fn instance_number() -> usize { |
| 610 | // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV) | 612 | $fc_num |
| 611 | // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value | ||
| 612 | // to yield a denominator vale of 256. All other values are not supported | ||
| 613 | syscon_reg() | ||
| 614 | .$FRG_FIELD() | ||
| 615 | .modify(|_, w| unsafe { w.div().bits(0xFF).mult().bits(mult_value as u8) }); | ||
| 616 | |||
| 617 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) | ||
| 618 | // By default, oversampling is 16x, i.e. OSRVAL = 15 | ||
| 619 | |||
| 620 | // Typical industry standard USARTs use a 16x oversample clock to transmit and receive | ||
| 621 | // asynchronous data. This is the number of BRG clocks used for one data bit. The | ||
| 622 | // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x | ||
| 623 | // oversample clock. There is no oversampling in synchronous modes. | ||
| 624 | Self::usart_reg() | ||
| 625 | .brg | ||
| 626 | .modify(|_, w| unsafe { w.brgval().bits((brg_value - 1) as u16) }); | ||
| 627 | } | 613 | } |
| 628 | 614 | #[inline] | |
| 629 | fn tx_pin_config() { | 615 | fn tx_pin_func() -> PioFunc { |
| 630 | iocon_reg().$TX_IOCON.modify(|_, w| unsafe { | 616 | PioFunc::$tx_pin |
| 631 | w.func() | ||
| 632 | .bits($TX_FUNC) | ||
| 633 | .digimode() | ||
| 634 | .digital() | ||
| 635 | .slew() | ||
| 636 | .standard() | ||
| 637 | .mode() | ||
| 638 | .inactive() | ||
| 639 | .invert() | ||
| 640 | .disabled() | ||
| 641 | .od() | ||
| 642 | .normal() | ||
| 643 | }); | ||
| 644 | } | 617 | } |
| 645 | 618 | #[inline] | |
| 646 | fn rx_pin_config() { | 619 | fn rx_pin_func() -> PioFunc { |
| 647 | iocon_reg().$RX_IOCON.modify(|_, w| unsafe { | 620 | PioFunc::$rx_pin |
| 648 | w.func() | ||
| 649 | .bits($RX_FUNC) | ||
| 650 | .digimode() | ||
| 651 | .digital() | ||
| 652 | .slew() | ||
| 653 | .standard() | ||
| 654 | .mode() | ||
| 655 | .inactive() | ||
| 656 | .invert() | ||
| 657 | .disabled() | ||
| 658 | .od() | ||
| 659 | .normal() | ||
| 660 | }); | ||
| 661 | } | 621 | } |
| 662 | } | 622 | } |
| 663 | |||
| 664 | impl $crate::usart::Instance for $crate::peripherals::$inst {} | 623 | impl $crate::usart::Instance for $crate::peripherals::$inst {} |
| 665 | }; | 624 | }; |
| 666 | } | 625 | } |
| 667 | 626 | ||
| 668 | impl_instance!(USART0, usart_peripheral: USART0, usart_crate: usart0, | 627 | impl_instance!(USART0, FLEXCOMM0, ALT1, ALT1, 0); |
| 669 | flexcomm: { | 628 | impl_instance!(USART1, FLEXCOMM1, ALT2, ALT2, 1); |
| 670 | field: FLEXCOMM0, | 629 | impl_instance!(USART2, FLEXCOMM2, ALT1, ALT1, 2); |
| 671 | clock_field: fc0 | 630 | impl_instance!(USART3, FLEXCOMM3, ALT1, ALT1, 3); |
| 672 | }, | 631 | impl_instance!(USART4, FLEXCOMM4, ALT1, ALT2, 4); |
| 673 | 632 | impl_instance!(USART5, FLEXCOMM5, ALT3, ALT3, 5); | |
| 674 | reset: { | 633 | impl_instance!(USART6, FLEXCOMM6, ALT2, ALT2, 6); |
| 675 | bit: fc0_rst | 634 | impl_instance!(USART7, FLEXCOMM7, ALT7, ALT7, 7); |
| 676 | }, | ||
| 677 | |||
| 678 | clock: { | ||
| 679 | sel_field: fcclksel0, | ||
| 680 | frg_field: flexfrg0ctrl | ||
| 681 | }, | ||
| 682 | |||
| 683 | pins: { | ||
| 684 | tx: pio1_6 => 1, | ||
| 685 | rx: pio1_5 => 1 | ||
| 686 | } | ||
| 687 | ); | ||
| 688 | |||
| 689 | impl_instance!(USART1, usart_peripheral: USART1, usart_crate: usart1, | ||
| 690 | flexcomm: { | ||
| 691 | field: FLEXCOMM1, | ||
| 692 | clock_field: fc1 | ||
| 693 | }, | ||
| 694 | |||
| 695 | reset: { | ||
| 696 | bit: fc1_rst | ||
| 697 | }, | ||
| 698 | |||
| 699 | clock: { | ||
| 700 | sel_field: fcclksel1, | ||
| 701 | frg_field: flexfrg1ctrl | ||
| 702 | }, | ||
| 703 | |||
| 704 | pins: { | ||
| 705 | tx: pio1_11 => 2, | ||
| 706 | rx: pio1_10 => 2 | ||
| 707 | } | ||
| 708 | ); | ||
| 709 | |||
| 710 | impl_instance!(USART2, usart_peripheral: USART2, usart_crate: usart2, | ||
| 711 | flexcomm: { | ||
| 712 | field: FLEXCOMM2, | ||
| 713 | clock_field: fc2 | ||
| 714 | }, | ||
| 715 | |||
| 716 | reset: { | ||
| 717 | bit: fc2_rst | ||
| 718 | }, | ||
| 719 | |||
| 720 | clock: { | ||
| 721 | sel_field: fcclksel2, | ||
| 722 | frg_field: flexfrg2ctrl | ||
| 723 | }, | ||
| 724 | |||
| 725 | pins: { | ||
| 726 | tx: pio0_27 => 1, | ||
| 727 | rx: pio1_24 => 1 | ||
| 728 | } | ||
| 729 | ); | ||
| 730 | |||
| 731 | impl_instance!(USART3, usart_peripheral: USART3, usart_crate: usart3, | ||
| 732 | flexcomm: { | ||
| 733 | field: FLEXCOMM3, | ||
| 734 | clock_field: fc3 | ||
| 735 | }, | ||
| 736 | |||
| 737 | reset: { | ||
| 738 | bit: fc3_rst | ||
| 739 | }, | ||
| 740 | |||
| 741 | clock: { | ||
| 742 | sel_field: fcclksel3, | ||
| 743 | frg_field: flexfrg3ctrl | ||
| 744 | }, | ||
| 745 | |||
| 746 | pins: { | ||
| 747 | tx: pio0_2 => 1, | ||
| 748 | rx: pio0_3 => 1 | ||
| 749 | } | ||
| 750 | ); | ||
| 751 | |||
| 752 | impl_instance!(USART4, usart_peripheral: USART4, usart_crate: usart4, | ||
| 753 | flexcomm: { | ||
| 754 | field: FLEXCOMM4, | ||
| 755 | clock_field: fc4 | ||
| 756 | }, | ||
| 757 | |||
| 758 | reset: { | ||
| 759 | bit: fc4_rst | ||
| 760 | }, | ||
| 761 | |||
| 762 | clock: { | ||
| 763 | sel_field: fcclksel4, | ||
| 764 | frg_field: flexfrg4ctrl | ||
| 765 | }, | ||
| 766 | |||
| 767 | pins: { | ||
| 768 | tx: pio0_16 => 1, | ||
| 769 | rx: pio0_5 => 2 | ||
| 770 | } | ||
| 771 | ); | ||
| 772 | |||
| 773 | impl_instance!(USART5, usart_peripheral: USART5, usart_crate: usart5, | ||
| 774 | flexcomm: { | ||
| 775 | field: FLEXCOMM5, | ||
| 776 | clock_field: fc5 | ||
| 777 | }, | ||
| 778 | |||
| 779 | reset: { | ||
| 780 | bit: fc5_rst | ||
| 781 | }, | ||
| 782 | |||
| 783 | clock: { | ||
| 784 | sel_field: fcclksel5, | ||
| 785 | frg_field: flexfrg5ctrl | ||
| 786 | }, | ||
| 787 | |||
| 788 | pins: { | ||
| 789 | tx: pio0_9 => 3, | ||
| 790 | rx: pio0_8 => 3 | ||
| 791 | } | ||
| 792 | ); | ||
| 793 | |||
| 794 | impl_instance!(USART6, usart_peripheral: USART6, usart_crate: usart6, | ||
| 795 | flexcomm: { | ||
| 796 | field: FLEXCOMM6, | ||
| 797 | clock_field: fc6 | ||
| 798 | }, | ||
| 799 | |||
| 800 | reset: { | ||
| 801 | bit: fc6_rst | ||
| 802 | }, | ||
| 803 | |||
| 804 | clock: { | ||
| 805 | sel_field: fcclksel6, | ||
| 806 | frg_field: flexfrg6ctrl | ||
| 807 | }, | ||
| 808 | |||
| 809 | pins: { | ||
| 810 | tx: pio1_16 => 2, | ||
| 811 | rx: pio1_13 => 2 | ||
| 812 | } | ||
| 813 | ); | ||
| 814 | |||
| 815 | impl_instance!(USART7, usart_peripheral: USART7, usart_crate: usart7, | ||
| 816 | flexcomm: { | ||
| 817 | field: FLEXCOMM7, | ||
| 818 | clock_field: fc7 | ||
| 819 | }, | ||
| 820 | |||
| 821 | reset: { | ||
| 822 | bit: fc7_rst | ||
| 823 | }, | ||
| 824 | |||
| 825 | clock: { | ||
| 826 | sel_field: fcclksel7, | ||
| 827 | frg_field: flexfrg7ctrl | ||
| 828 | }, | ||
| 829 | |||
| 830 | pins: { | ||
| 831 | tx: pio0_19 => 7, | ||
| 832 | rx: pio0_20 => 7 | ||
| 833 | } | ||
| 834 | ); | ||
| 835 | 635 | ||
| 836 | /// Trait for TX pins. | 636 | /// Trait for TX pins. |
| 837 | pub trait TxPin<T: Instance>: crate::gpio::Pin {} | 637 | pub trait TxPin<T: Instance>: crate::gpio::Pin {} |
| 838 | /// Trait for RX pins. | 638 | /// Trait for RX pins. |
| 839 | pub trait RxPin<T: Instance>: crate::gpio::Pin {} | 639 | pub trait RxPin<T: Instance>: crate::gpio::Pin {} |
| 840 | 640 | ||
| 841 | // TODO: Add RTS, CTS and CLK pin traits | ||
| 842 | |||
| 843 | macro_rules! impl_pin { | 641 | macro_rules! impl_pin { |
| 844 | ($pin:ident, $instance:ident, Tx) => { | 642 | ($pin:ident, $instance:ident, Tx) => { |
| 845 | impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} | 643 | impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} |
| @@ -849,37 +647,19 @@ macro_rules! impl_pin { | |||
| 849 | }; | 647 | }; |
| 850 | } | 648 | } |
| 851 | 649 | ||
| 852 | impl_pin!(PIO1_5, USART0, Rx); | ||
| 853 | impl_pin!(PIO1_6, USART0, Tx); | 650 | impl_pin!(PIO1_6, USART0, Tx); |
| 854 | impl_pin!(PIO1_10, USART1, Rx); | 651 | impl_pin!(PIO1_5, USART0, Rx); |
| 855 | impl_pin!(PIO1_11, USART1, Tx); | 652 | impl_pin!(PIO1_11, USART1, Tx); |
| 653 | impl_pin!(PIO1_10, USART1, Rx); | ||
| 856 | impl_pin!(PIO0_27, USART2, Tx); | 654 | impl_pin!(PIO0_27, USART2, Tx); |
| 857 | impl_pin!(PIO1_24, USART2, Rx); | 655 | impl_pin!(PIO1_24, USART2, Rx); |
| 858 | impl_pin!(PIO0_2, USART3, Tx); | 656 | impl_pin!(PIO0_2, USART3, Tx); |
| 859 | impl_pin!(PIO0_3, USART3, Rx); | 657 | impl_pin!(PIO0_3, USART3, Rx); |
| 860 | impl_pin!(PIO0_16, USART4, Tx); | 658 | impl_pin!(PIO0_16, USART4, Tx); |
| 861 | impl_pin!(PIO0_5, USART4, Rx); | 659 | impl_pin!(PIO0_5, USART4, Rx); |
| 862 | impl_pin!(PIO0_8, USART5, Rx); | ||
| 863 | impl_pin!(PIO0_9, USART5, Tx); | 660 | impl_pin!(PIO0_9, USART5, Tx); |
| 661 | impl_pin!(PIO0_8, USART5, Rx); | ||
| 864 | impl_pin!(PIO1_16, USART6, Tx); | 662 | impl_pin!(PIO1_16, USART6, Tx); |
| 865 | impl_pin!(PIO1_13, USART6, Rx); | 663 | impl_pin!(PIO1_13, USART6, Rx); |
| 866 | impl_pin!(PIO0_20, USART7, Rx); | ||
| 867 | impl_pin!(PIO0_19, USART7, Tx); | 664 | impl_pin!(PIO0_19, USART7, Tx); |
| 868 | 665 | impl_pin!(PIO0_20, USART7, Rx); | |
| 869 | /// Get the SYSCON register block. | ||
| 870 | /// | ||
| 871 | /// # Safety | ||
| 872 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 873 | /// registers are not accessed concurrently by multiple threads. | ||
| 874 | pub(crate) fn syscon_reg() -> &'static crate::pac::syscon::RegisterBlock { | ||
| 875 | unsafe { &*crate::pac::SYSCON::ptr() } | ||
| 876 | } | ||
| 877 | |||
| 878 | /// Get the IOCON register block. | ||
| 879 | /// | ||
| 880 | /// # Safety | ||
| 881 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 882 | /// registers are not accessed concurrently by multiple threads. | ||
| 883 | pub(crate) fn iocon_reg() -> &'static crate::pac::iocon::RegisterBlock { | ||
| 884 | unsafe { &*crate::pac::IOCON::ptr() } | ||
| 885 | } | ||
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index ebdc3e1c8..d1265ffc4 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md | |||
| @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Add PIO SPI | ||
| 12 | - Add PIO I2S input | ||
| 13 | - Add PIO onewire parasite power strong pullup | ||
| 14 | - add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216)) | ||
| 15 | |||
| 11 | ## 0.8.0 - 2025-08-26 | 16 | ## 0.8.0 - 2025-08-26 |
| 12 | 17 | ||
| 13 | ## 0.7.1 - 2025-08-26 | 18 | ## 0.7.1 - 2025-08-26 |
| @@ -51,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 51 | 56 | ||
| 52 | ## 0.4.0 - 2025-03-09 | 57 | ## 0.4.0 - 2025-03-09 |
| 53 | 58 | ||
| 54 | - Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857)) | 59 | - Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857)) |
| 55 | The functions added in this change are `get_addr` `get_tx_threshold`, `set_tx_threshold`, `get_rx_threshold`, `set_rx_threshold`, `set_thresholds`. | 60 | The functions added in this change are `get_addr` `get_tx_threshold`, `set_tx_threshold`, `get_rx_threshold`, `set_rx_threshold`, `set_thresholds`. |
| 56 | - Expose the watchdog reset reason. ([#3877](https://github.com/embassy-rs/embassy/pull/3877)) | 61 | - Expose the watchdog reset reason. ([#3877](https://github.com/embassy-rs/embassy/pull/3877)) |
| 57 | - Update pio-rs, reexport, move instr methods to SM. ([#3865](https://github.com/embassy-rs/embassy/pull/3865)) | 62 | - Update pio-rs, reexport, move instr methods to SM. ([#3865](https://github.com/embassy-rs/embassy/pull/3865)) |
| @@ -92,7 +97,7 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended. | |||
| 92 | - Add Clone and Copy to Error types | 97 | - Add Clone and Copy to Error types |
| 93 | - fix spinlocks staying locked after reset. | 98 | - fix spinlocks staying locked after reset. |
| 94 | - wait until read matches for PSM accesses. | 99 | - wait until read matches for PSM accesses. |
| 95 | - Remove generics | 100 | - Remove generics |
| 96 | - fix drop implementation of BufferedUartRx and BufferedUartTx | 101 | - fix drop implementation of BufferedUartRx and BufferedUartTx |
| 97 | - implement `embedded_storage_async::nor_flash::MultiwriteNorFlash` | 102 | - implement `embedded_storage_async::nor_flash::MultiwriteNorFlash` |
| 98 | - rp usb: wake ep-wakers after stalling | 103 | - rp usb: wake ep-wakers after stalling |
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index eb497de1a..6fb680b34 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -35,7 +35,11 @@ pub mod multicore; | |||
| 35 | #[cfg(feature = "_rp235x")] | 35 | #[cfg(feature = "_rp235x")] |
| 36 | pub mod otp; | 36 | pub mod otp; |
| 37 | pub mod pio_programs; | 37 | pub mod pio_programs; |
| 38 | #[cfg(feature = "_rp235x")] | ||
| 39 | pub mod psram; | ||
| 38 | pub mod pwm; | 40 | pub mod pwm; |
| 41 | #[cfg(feature = "_rp235x")] | ||
| 42 | pub mod qmi_cs1; | ||
| 39 | mod reset; | 43 | mod reset; |
| 40 | pub mod rom_data; | 44 | pub mod rom_data; |
| 41 | #[cfg(feature = "rp2040")] | 45 | #[cfg(feature = "rp2040")] |
| @@ -381,6 +385,8 @@ embassy_hal_internal::peripherals! { | |||
| 381 | SPI0, | 385 | SPI0, |
| 382 | SPI1, | 386 | SPI1, |
| 383 | 387 | ||
| 388 | QMI_CS1, | ||
| 389 | |||
| 384 | I2C0, | 390 | I2C0, |
| 385 | I2C1, | 391 | I2C1, |
| 386 | 392 | ||
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 0d8a94776..5f554dfe3 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs | |||
| @@ -12,7 +12,7 @@ use fixed::types::extra::U8; | |||
| 12 | use fixed::FixedU32; | 12 | use fixed::FixedU32; |
| 13 | use pio::{Program, SideSet, Wrap}; | 13 | use pio::{Program, SideSet, Wrap}; |
| 14 | 14 | ||
| 15 | use crate::dma::{Channel, Transfer, Word}; | 15 | use crate::dma::{self, Channel, Transfer, Word}; |
| 16 | use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; | 16 | use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; |
| 17 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; | 17 | use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; |
| 18 | use crate::relocate::RelocatedProgram; | 18 | use crate::relocate::RelocatedProgram; |
| @@ -281,6 +281,18 @@ impl<'l, PIO: Instance> Pin<'l, PIO> { | |||
| 281 | }); | 281 | }); |
| 282 | } | 282 | } |
| 283 | 283 | ||
| 284 | /// Configure the output logic inversion of this pin. | ||
| 285 | #[inline] | ||
| 286 | pub fn set_output_inversion(&mut self, invert: bool) { | ||
| 287 | self.pin.gpio().ctrl().modify(|w| { | ||
| 288 | w.set_outover(if invert { | ||
| 289 | pac::io::vals::Outover::INVERT | ||
| 290 | } else { | ||
| 291 | pac::io::vals::Outover::NORMAL | ||
| 292 | }) | ||
| 293 | }); | ||
| 294 | } | ||
| 295 | |||
| 284 | /// Set the pin's input sync bypass. | 296 | /// Set the pin's input sync bypass. |
| 285 | pub fn set_input_sync_bypass(&mut self, bypass: bool) { | 297 | pub fn set_input_sync_bypass(&mut self, bypass: bool) { |
| 286 | let mask = 1 << self.pin(); | 298 | let mask = 1 << self.pin(); |
| @@ -360,6 +372,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { | |||
| 360 | FifoInFuture::new(self) | 372 | FifoInFuture::new(self) |
| 361 | } | 373 | } |
| 362 | 374 | ||
| 375 | fn dreq() -> crate::pac::dma::vals::TreqSel { | ||
| 376 | crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4) | ||
| 377 | } | ||
| 378 | |||
| 363 | /// Prepare DMA transfer from RX FIFO. | 379 | /// Prepare DMA transfer from RX FIFO. |
| 364 | pub fn dma_pull<'a, C: Channel, W: Word>( | 380 | pub fn dma_pull<'a, C: Channel, W: Word>( |
| 365 | &'a mut self, | 381 | &'a mut self, |
| @@ -367,7 +383,6 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { | |||
| 367 | data: &'a mut [W], | 383 | data: &'a mut [W], |
| 368 | bswap: bool, | 384 | bswap: bool, |
| 369 | ) -> Transfer<'a, C> { | 385 | ) -> Transfer<'a, C> { |
| 370 | let pio_no = PIO::PIO_NO; | ||
| 371 | let p = ch.regs(); | 386 | let p = ch.regs(); |
| 372 | p.write_addr().write_value(data.as_ptr() as u32); | 387 | p.write_addr().write_value(data.as_ptr() as u32); |
| 373 | p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); | 388 | p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); |
| @@ -377,8 +392,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { | |||
| 377 | p.trans_count().write(|w| w.set_count(data.len() as u32)); | 392 | p.trans_count().write(|w| w.set_count(data.len() as u32)); |
| 378 | compiler_fence(Ordering::SeqCst); | 393 | compiler_fence(Ordering::SeqCst); |
| 379 | p.ctrl_trig().write(|w| { | 394 | p.ctrl_trig().write(|w| { |
| 380 | // Set RX DREQ for this statemachine | 395 | w.set_treq_sel(Self::dreq()); |
| 381 | w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8 + 4)); | ||
| 382 | w.set_data_size(W::size()); | 396 | w.set_data_size(W::size()); |
| 383 | w.set_chain_to(ch.number()); | 397 | w.set_chain_to(ch.number()); |
| 384 | w.set_incr_read(false); | 398 | w.set_incr_read(false); |
| @@ -389,6 +403,36 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { | |||
| 389 | compiler_fence(Ordering::SeqCst); | 403 | compiler_fence(Ordering::SeqCst); |
| 390 | Transfer::new(ch) | 404 | Transfer::new(ch) |
| 391 | } | 405 | } |
| 406 | |||
| 407 | /// Prepare a repeated DMA transfer from RX FIFO. | ||
| 408 | pub fn dma_pull_repeated<'a, C: Channel, W: Word>(&'a mut self, ch: Peri<'a, C>, len: usize) -> Transfer<'a, C> { | ||
| 409 | // This is the read version of dma::write_repeated. This allows us to | ||
| 410 | // discard reads from the RX FIFO through DMA. | ||
| 411 | |||
| 412 | // static mut so it gets allocated in RAM | ||
| 413 | static mut DUMMY: u32 = 0; | ||
| 414 | |||
| 415 | let p = ch.regs(); | ||
| 416 | p.write_addr().write_value(core::ptr::addr_of_mut!(DUMMY) as u32); | ||
| 417 | p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); | ||
| 418 | |||
| 419 | #[cfg(feature = "rp2040")] | ||
| 420 | p.trans_count().write(|w| *w = len as u32); | ||
| 421 | #[cfg(feature = "_rp235x")] | ||
| 422 | p.trans_count().write(|w| w.set_count(len as u32)); | ||
| 423 | |||
| 424 | compiler_fence(Ordering::SeqCst); | ||
| 425 | p.ctrl_trig().write(|w| { | ||
| 426 | w.set_treq_sel(Self::dreq()); | ||
| 427 | w.set_data_size(W::size()); | ||
| 428 | w.set_chain_to(ch.number()); | ||
| 429 | w.set_incr_read(false); | ||
| 430 | w.set_incr_write(false); | ||
| 431 | w.set_en(true); | ||
| 432 | }); | ||
| 433 | compiler_fence(Ordering::SeqCst); | ||
| 434 | Transfer::new(ch) | ||
| 435 | } | ||
| 392 | } | 436 | } |
| 393 | 437 | ||
| 394 | /// Type representing a state machine TX FIFO. | 438 | /// Type representing a state machine TX FIFO. |
| @@ -412,7 +456,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { | |||
| 412 | (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f | 456 | (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f |
| 413 | } | 457 | } |
| 414 | 458 | ||
| 415 | /// Check state machine has stalled on empty TX FIFO. | 459 | /// Check if state machine has stalled on empty TX FIFO. |
| 416 | pub fn stalled(&self) -> bool { | 460 | pub fn stalled(&self) -> bool { |
| 417 | let fdebug = PIO::PIO.fdebug(); | 461 | let fdebug = PIO::PIO.fdebug(); |
| 418 | let ret = fdebug.read().txstall() & (1 << SM) != 0; | 462 | let ret = fdebug.read().txstall() & (1 << SM) != 0; |
| @@ -451,6 +495,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { | |||
| 451 | FifoOutFuture::new(self, value) | 495 | FifoOutFuture::new(self, value) |
| 452 | } | 496 | } |
| 453 | 497 | ||
| 498 | fn dreq() -> crate::pac::dma::vals::TreqSel { | ||
| 499 | crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8) | ||
| 500 | } | ||
| 501 | |||
| 454 | /// Prepare a DMA transfer to TX FIFO. | 502 | /// Prepare a DMA transfer to TX FIFO. |
| 455 | pub fn dma_push<'a, C: Channel, W: Word>( | 503 | pub fn dma_push<'a, C: Channel, W: Word>( |
| 456 | &'a mut self, | 504 | &'a mut self, |
| @@ -458,7 +506,6 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { | |||
| 458 | data: &'a [W], | 506 | data: &'a [W], |
| 459 | bswap: bool, | 507 | bswap: bool, |
| 460 | ) -> Transfer<'a, C> { | 508 | ) -> Transfer<'a, C> { |
| 461 | let pio_no = PIO::PIO_NO; | ||
| 462 | let p = ch.regs(); | 509 | let p = ch.regs(); |
| 463 | p.read_addr().write_value(data.as_ptr() as u32); | 510 | p.read_addr().write_value(data.as_ptr() as u32); |
| 464 | p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32); | 511 | p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32); |
| @@ -468,8 +515,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { | |||
| 468 | p.trans_count().write(|w| w.set_count(data.len() as u32)); | 515 | p.trans_count().write(|w| w.set_count(data.len() as u32)); |
| 469 | compiler_fence(Ordering::SeqCst); | 516 | compiler_fence(Ordering::SeqCst); |
| 470 | p.ctrl_trig().write(|w| { | 517 | p.ctrl_trig().write(|w| { |
| 471 | // Set TX DREQ for this statemachine | 518 | w.set_treq_sel(Self::dreq()); |
| 472 | w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8)); | ||
| 473 | w.set_data_size(W::size()); | 519 | w.set_data_size(W::size()); |
| 474 | w.set_chain_to(ch.number()); | 520 | w.set_chain_to(ch.number()); |
| 475 | w.set_incr_read(true); | 521 | w.set_incr_read(true); |
| @@ -480,6 +526,11 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { | |||
| 480 | compiler_fence(Ordering::SeqCst); | 526 | compiler_fence(Ordering::SeqCst); |
| 481 | Transfer::new(ch) | 527 | Transfer::new(ch) |
| 482 | } | 528 | } |
| 529 | |||
| 530 | /// Prepare a repeated DMA transfer to TX FIFO. | ||
| 531 | pub fn dma_push_repeated<'a, C: Channel, W: Word>(&'a mut self, ch: Peri<'a, C>, len: usize) -> Transfer<'a, C> { | ||
| 532 | unsafe { dma::write_repeated(ch, PIO::PIO.txf(SM).as_ptr(), len, Self::dreq()) } | ||
| 533 | } | ||
| 483 | } | 534 | } |
| 484 | 535 | ||
| 485 | /// A type representing a single PIO state machine. | 536 | /// A type representing a single PIO state machine. |
| @@ -926,13 +977,27 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 926 | self.set_enable(enabled); | 977 | self.set_enable(enabled); |
| 927 | } | 978 | } |
| 928 | 979 | ||
| 980 | #[cfg(feature = "rp2040")] | ||
| 981 | fn pin_base() -> u8 { | ||
| 982 | 0 | ||
| 983 | } | ||
| 984 | |||
| 985 | #[cfg(feature = "_rp235x")] | ||
| 986 | fn pin_base() -> u8 { | ||
| 987 | if PIO::PIO.gpiobase().read().gpiobase() { | ||
| 988 | 16 | ||
| 989 | } else { | ||
| 990 | 0 | ||
| 991 | } | ||
| 992 | } | ||
| 993 | |||
| 929 | /// Sets pin directions. This pauses the current state machine to run `SET` commands | 994 | /// Sets pin directions. This pauses the current state machine to run `SET` commands |
| 930 | /// and temporarily unsets the `OUT_STICKY` bit. | 995 | /// and temporarily unsets the `OUT_STICKY` bit. |
| 931 | pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { | 996 | pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { |
| 932 | self.with_paused(|sm| { | 997 | self.with_paused(|sm| { |
| 933 | for pin in pins { | 998 | for pin in pins { |
| 934 | Self::this_sm().pinctrl().write(|w| { | 999 | Self::this_sm().pinctrl().write(|w| { |
| 935 | w.set_set_base(pin.pin()); | 1000 | w.set_set_base(pin.pin() - Self::pin_base()); |
| 936 | w.set_set_count(1); | 1001 | w.set_set_count(1); |
| 937 | }); | 1002 | }); |
| 938 | // SET PINDIRS, (dir) | 1003 | // SET PINDIRS, (dir) |
| @@ -947,7 +1012,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 947 | self.with_paused(|sm| { | 1012 | self.with_paused(|sm| { |
| 948 | for pin in pins { | 1013 | for pin in pins { |
| 949 | Self::this_sm().pinctrl().write(|w| { | 1014 | Self::this_sm().pinctrl().write(|w| { |
| 950 | w.set_set_base(pin.pin()); | 1015 | w.set_set_base(pin.pin() - Self::pin_base()); |
| 951 | w.set_set_count(1); | 1016 | w.set_set_count(1); |
| 952 | }); | 1017 | }); |
| 953 | // SET PINS, (dir) | 1018 | // SET PINS, (dir) |
| @@ -1310,6 +1375,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { | |||
| 1310 | PIO::state().users.store(5, Ordering::Release); | 1375 | PIO::state().users.store(5, Ordering::Release); |
| 1311 | PIO::state().used_pins.store(0, Ordering::Release); | 1376 | PIO::state().used_pins.store(0, Ordering::Release); |
| 1312 | PIO::Interrupt::unpend(); | 1377 | PIO::Interrupt::unpend(); |
| 1378 | |||
| 1313 | unsafe { PIO::Interrupt::enable() }; | 1379 | unsafe { PIO::Interrupt::enable() }; |
| 1314 | Self { | 1380 | Self { |
| 1315 | common: Common { | 1381 | common: Common { |
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs index 7ceed3fa6..2382a3f9f 100644 --- a/embassy-rp/src/pio_programs/i2s.rs +++ b/embassy-rp/src/pio_programs/i2s.rs | |||
| @@ -1,13 +1,101 @@ | |||
| 1 | //! Pio backed I2s output | 1 | //! Pio backed I2s output and output drivers |
| 2 | 2 | ||
| 3 | use fixed::traits::ToFixed; | 3 | use fixed::traits::ToFixed; |
| 4 | 4 | ||
| 5 | use crate::dma::{AnyChannel, Channel, Transfer}; | 5 | use crate::dma::{AnyChannel, Channel, Transfer}; |
| 6 | use crate::gpio::Pull; | ||
| 6 | use crate::pio::{ | 7 | use crate::pio::{ |
| 7 | Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, | 8 | Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, |
| 8 | }; | 9 | }; |
| 9 | use crate::Peri; | 10 | use crate::Peri; |
| 10 | 11 | ||
| 12 | /// This struct represents an i2s receiver & controller driver program | ||
| 13 | pub struct PioI2sInProgram<'d, PIO: Instance> { | ||
| 14 | prg: LoadedProgram<'d, PIO>, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<'d, PIO: Instance> PioI2sInProgram<'d, PIO> { | ||
| 18 | /// Load the input program into the given pio | ||
| 19 | pub fn new(common: &mut Common<'d, PIO>) -> Self { | ||
| 20 | let prg = pio::pio_asm! { | ||
| 21 | ".side_set 2", | ||
| 22 | " set x, 14 side 0b01", | ||
| 23 | "left_data:", | ||
| 24 | " in pins, 1 side 0b00", // read one left-channel bit from SD | ||
| 25 | " jmp x-- left_data side 0b01", | ||
| 26 | " in pins, 1 side 0b10", // ws changes 1 clock before MSB | ||
| 27 | " set x, 14 side 0b11", | ||
| 28 | "right_data:", | ||
| 29 | " in pins, 1 side 0b10", | ||
| 30 | " jmp x-- right_data side 0b11", | ||
| 31 | " in pins, 1 side 0b00" // ws changes 1 clock before ms | ||
| 32 | }; | ||
| 33 | let prg = common.load_program(&prg.program); | ||
| 34 | Self { prg } | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Pio backed I2s input driver | ||
| 39 | pub struct PioI2sIn<'d, P: Instance, const S: usize> { | ||
| 40 | dma: Peri<'d, AnyChannel>, | ||
| 41 | sm: StateMachine<'d, P, S>, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> { | ||
| 45 | /// Configure a state machine to act as both the controller (provider of SCK and WS) and receiver (of SD) for an I2S signal | ||
| 46 | pub fn new( | ||
| 47 | common: &mut Common<'d, P>, | ||
| 48 | mut sm: StateMachine<'d, P, S>, | ||
| 49 | dma: Peri<'d, impl Channel>, | ||
| 50 | // Whether or not to use the MCU's internal pull-down resistor, as the | ||
| 51 | // Pico 2 is known to have problems with the inbuilt pulldowns, many | ||
| 52 | // opt to just use an external pull down resistor to meet requirements of common | ||
| 53 | // i2s microphones such as the INMP441 | ||
| 54 | data_pulldown: bool, | ||
| 55 | data_pin: Peri<'d, impl PioPin>, | ||
| 56 | bit_clock_pin: Peri<'d, impl PioPin>, | ||
| 57 | lr_clock_pin: Peri<'d, impl PioPin>, | ||
| 58 | sample_rate: u32, | ||
| 59 | bit_depth: u32, | ||
| 60 | channels: u32, | ||
| 61 | program: &PioI2sInProgram<'d, P>, | ||
| 62 | ) -> Self { | ||
| 63 | let mut data_pin = common.make_pio_pin(data_pin); | ||
| 64 | if data_pulldown { | ||
| 65 | data_pin.set_pull(Pull::Down); | ||
| 66 | } | ||
| 67 | let bit_clock_pin = common.make_pio_pin(bit_clock_pin); | ||
| 68 | let left_right_clock_pin = common.make_pio_pin(lr_clock_pin); | ||
| 69 | |||
| 70 | let cfg = { | ||
| 71 | let mut cfg = Config::default(); | ||
| 72 | cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]); | ||
| 73 | cfg.set_in_pins(&[&data_pin]); | ||
| 74 | let clock_frequency = sample_rate * bit_depth * channels; | ||
| 75 | cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed(); | ||
| 76 | cfg.shift_in = ShiftConfig { | ||
| 77 | threshold: 32, | ||
| 78 | direction: ShiftDirection::Left, | ||
| 79 | auto_fill: true, | ||
| 80 | }; | ||
| 81 | // join fifos to have twice the time to start the next dma transfer | ||
| 82 | cfg.fifo_join = FifoJoin::RxOnly; // both control signals are sent via side-setting | ||
| 83 | cfg | ||
| 84 | }; | ||
| 85 | sm.set_config(&cfg); | ||
| 86 | sm.set_pin_dirs(Direction::In, &[&data_pin]); | ||
| 87 | sm.set_pin_dirs(Direction::Out, &[&left_right_clock_pin, &bit_clock_pin]); | ||
| 88 | sm.set_enable(true); | ||
| 89 | |||
| 90 | Self { dma: dma.into(), sm } | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer. | ||
| 94 | pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b, AnyChannel> { | ||
| 95 | self.sm.rx().dma_pull(self.dma.reborrow(), buff, false) | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 11 | /// This struct represents an i2s output driver program | 99 | /// This struct represents an i2s output driver program |
| 12 | /// | 100 | /// |
| 13 | /// The sample bit-depth is set through scratch register `Y`. | 101 | /// The sample bit-depth is set through scratch register `Y`. |
| @@ -26,12 +114,12 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> { | |||
| 26 | "left_data:", | 114 | "left_data:", |
| 27 | " out pins, 1 side 0b00", | 115 | " out pins, 1 side 0b00", |
| 28 | " jmp x-- left_data side 0b01", | 116 | " jmp x-- left_data side 0b01", |
| 29 | " out pins 1 side 0b10", | 117 | " out pins, 1 side 0b10", |
| 30 | " mov x, y side 0b11", | 118 | " mov x, y side 0b11", |
| 31 | "right_data:", | 119 | "right_data:", |
| 32 | " out pins 1 side 0b10", | 120 | " out pins, 1 side 0b10", |
| 33 | " jmp x-- right_data side 0b11", | 121 | " jmp x-- right_data side 0b11", |
| 34 | " out pins 1 side 0b00", | 122 | " out pins, 1 side 0b00", |
| 35 | ); | 123 | ); |
| 36 | 124 | ||
| 37 | let prg = common.load_program(&prg.program); | 125 | let prg = common.load_program(&prg.program); |
diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs index 8eac328b3..d05ba3884 100644 --- a/embassy-rp/src/pio_programs/mod.rs +++ b/embassy-rp/src/pio_programs/mod.rs | |||
| @@ -6,6 +6,7 @@ pub mod i2s; | |||
| 6 | pub mod onewire; | 6 | pub mod onewire; |
| 7 | pub mod pwm; | 7 | pub mod pwm; |
| 8 | pub mod rotary_encoder; | 8 | pub mod rotary_encoder; |
| 9 | pub mod spi; | ||
| 9 | pub mod stepper; | 10 | pub mod stepper; |
| 10 | pub mod uart; | 11 | pub mod uart; |
| 11 | pub mod ws2812; | 12 | pub mod ws2812; |
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs index 287ddab41..980d0fe5f 100644 --- a/embassy-rp/src/pio_programs/onewire.rs +++ b/embassy-rp/src/pio_programs/onewire.rs | |||
| @@ -52,7 +52,8 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | |||
| 52 | 52 | ||
| 53 | ; The low pulse was already done, we only need to delay and poll the bit in case we are reading | 53 | ; The low pulse was already done, we only need to delay and poll the bit in case we are reading |
| 54 | write_1: | 54 | write_1: |
| 55 | nop side 0 [( 6 / CLK) - 1] ; Delay before sampling the input pin | 55 | jmp y--, continue_1 side 0 [( 6 / CLK) - 1] ; Delay before sampling input. Always decrement y |
| 56 | continue_1: | ||
| 56 | in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR | 57 | in pins, 1 side 0 [(48 / CLK) - 1] ; This writes the state of the pin into the ISR |
| 57 | ; Fallthrough | 58 | ; Fallthrough |
| 58 | 59 | ||
| @@ -61,9 +62,24 @@ impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { | |||
| 61 | .wrap_target | 62 | .wrap_target |
| 62 | out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR | 63 | out x, 1 side 0 [(12 / CLK) - 1] ; Stalls if no data available in TX FIFO and OSR |
| 63 | jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit | 64 | jmp x--, write_1 side 1 [( 6 / CLK) - 1] ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit |
| 64 | in null, 1 side 1 [(54 / CLK) - 1] ; Do the remainder of the low part of a 0 bit | 65 | jmp y--, continue_0 side 1 [(48 / CLK) - 1] ; Do the remainder of the low part of a 0 bit |
| 65 | ; This writes 0 into the ISR so that the shift count stays in sync | 66 | jmp pullup side 1 [( 6 / CLK) - 1] ; Remain low while jumping |
| 67 | continue_0: | ||
| 68 | in null, 1 side 1 [( 6 / CLK) - 1] ; This writes 0 into the ISR so that the shift count stays in sync | ||
| 66 | .wrap | 69 | .wrap |
| 70 | |||
| 71 | ; Assume that strong pullup commands always have MSB (the last bit) = 0, | ||
| 72 | ; since the rising edge can be used to start the operation. | ||
| 73 | ; That's the case for DS18B20 (44h and 48h). | ||
| 74 | pullup: | ||
| 75 | set pins, 1 side 1[( 6 / CLK) - 1] ; Drive pin high output immediately. | ||
| 76 | ; Strong pullup must be within 10us of rise. | ||
| 77 | in null, 1 side 1[( 6 / CLK) - 1] ; Keep ISR in sync. Must occur after the y--. | ||
| 78 | out null, 8 side 1[( 6 / CLK) - 1] ; Wait for write_bytes_pullup() delay to complete. | ||
| 79 | ; The delay is hundreds of ms, so done externally. | ||
| 80 | set pins, 0 side 0[( 6 / CLK) - 1] ; Back to open drain, pin low when driven | ||
| 81 | in null, 8 side 1[( 6 / CLK) - 1] ; Inform write_bytes_pullup() it's ready | ||
| 82 | jmp next_bit side 0[( 6 / CLK) - 1] ; Continue | ||
| 67 | "# | 83 | "# |
| 68 | ); | 84 | ); |
| 69 | 85 | ||
| @@ -98,6 +114,7 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 98 | let mut cfg = Config::default(); | 114 | let mut cfg = Config::default(); |
| 99 | cfg.use_program(&program.prg, &[&pin]); | 115 | cfg.use_program(&program.prg, &[&pin]); |
| 100 | cfg.set_in_pins(&[&pin]); | 116 | cfg.set_in_pins(&[&pin]); |
| 117 | cfg.set_set_pins(&[&pin]); | ||
| 101 | 118 | ||
| 102 | let shift_cfg = ShiftConfig { | 119 | let shift_cfg = ShiftConfig { |
| 103 | auto_fill: true, | 120 | auto_fill: true, |
| @@ -146,6 +163,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 146 | 163 | ||
| 147 | /// Write bytes to the onewire bus | 164 | /// Write bytes to the onewire bus |
| 148 | pub async fn write_bytes(&mut self, data: &[u8]) { | 165 | pub async fn write_bytes(&mut self, data: &[u8]) { |
| 166 | unsafe { self.sm.set_y(u32::MAX as u32) }; | ||
| 167 | let (rx, tx) = self.sm.rx_tx(); | ||
| 168 | for b in data { | ||
| 169 | tx.wait_push(*b as u32).await; | ||
| 170 | |||
| 171 | // Empty the buffer that is being filled with every write | ||
| 172 | let _ = rx.wait_pull().await; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Write bytes to the onewire bus, then apply a strong pullup | ||
| 177 | pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) { | ||
| 178 | unsafe { self.sm.set_y(data.len() as u32 * 8 - 1) }; | ||
| 149 | let (rx, tx) = self.sm.rx_tx(); | 179 | let (rx, tx) = self.sm.rx_tx(); |
| 150 | for b in data { | 180 | for b in data { |
| 151 | tx.wait_push(*b as u32).await; | 181 | tx.wait_push(*b as u32).await; |
| @@ -153,10 +183,19 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> { | |||
| 153 | // Empty the buffer that is being filled with every write | 183 | // Empty the buffer that is being filled with every write |
| 154 | let _ = rx.wait_pull().await; | 184 | let _ = rx.wait_pull().await; |
| 155 | } | 185 | } |
| 186 | |||
| 187 | // Perform the delay, usually hundreds of ms. | ||
| 188 | embassy_time::Timer::after(pullup_time).await; | ||
| 189 | |||
| 190 | // Signal that delay has completed | ||
| 191 | tx.wait_push(0 as u32).await; | ||
| 192 | // Wait until it's back at 0 low, open drain | ||
| 193 | let _ = rx.wait_pull().await; | ||
| 156 | } | 194 | } |
| 157 | 195 | ||
| 158 | /// Read bytes from the onewire bus | 196 | /// Read bytes from the onewire bus |
| 159 | pub async fn read_bytes(&mut self, data: &mut [u8]) { | 197 | pub async fn read_bytes(&mut self, data: &mut [u8]) { |
| 198 | unsafe { self.sm.set_y(u32::MAX as u32) }; | ||
| 160 | let (rx, tx) = self.sm.rx_tx(); | 199 | let (rx, tx) = self.sm.rx_tx(); |
| 161 | for b in data { | 200 | for b in data { |
| 162 | // Write all 1's so that we can read what the device responds | 201 | // Write all 1's so that we can read what the device responds |
diff --git a/embassy-rp/src/pio_programs/spi.rs b/embassy-rp/src/pio_programs/spi.rs new file mode 100644 index 000000000..b10fc6628 --- /dev/null +++ b/embassy-rp/src/pio_programs/spi.rs | |||
| @@ -0,0 +1,474 @@ | |||
| 1 | //! PIO backed SPi drivers | ||
| 2 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_futures::join::join; | ||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | use embedded_hal_02::spi::{Phase, Polarity}; | ||
| 8 | use fixed::traits::ToFixed; | ||
| 9 | use fixed::types::extra::U8; | ||
| 10 | |||
| 11 | use crate::clocks::clk_sys_freq; | ||
| 12 | use crate::dma::{AnyChannel, Channel}; | ||
| 13 | use crate::gpio::Level; | ||
| 14 | use crate::pio::{Common, Direction, Instance, LoadedProgram, Pin, PioPin, ShiftDirection, StateMachine}; | ||
| 15 | use crate::spi::{Async, Blocking, Config, Mode}; | ||
| 16 | |||
| 17 | /// This struct represents an SPI program loaded into pio instruction memory. | ||
| 18 | struct PioSpiProgram<'d, PIO: Instance> { | ||
| 19 | prg: LoadedProgram<'d, PIO>, | ||
| 20 | phase: Phase, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<'d, PIO: Instance> PioSpiProgram<'d, PIO> { | ||
| 24 | /// Load the spi program into the given pio | ||
| 25 | pub fn new(common: &mut Common<'d, PIO>, phase: Phase) -> Self { | ||
| 26 | // These PIO programs are taken straight from the datasheet (3.6.1 in | ||
| 27 | // RP2040 datasheet, 11.6.1 in RP2350 datasheet) | ||
| 28 | |||
| 29 | // Pin assignments: | ||
| 30 | // - SCK is side-set pin 0 | ||
| 31 | // - MOSI is OUT pin 0 | ||
| 32 | // - MISO is IN pin 0 | ||
| 33 | // | ||
| 34 | // Auto-push and auto-pull must be enabled, and the serial frame size is set by | ||
| 35 | // configuring the push/pull threshold. Shift left/right is fine, but you must | ||
| 36 | // justify the data yourself. This is done most conveniently for frame sizes of | ||
| 37 | // 8 or 16 bits by using the narrow store replication and narrow load byte | ||
| 38 | // picking behavior of RP2040's IO fabric. | ||
| 39 | |||
| 40 | let prg = match phase { | ||
| 41 | Phase::CaptureOnFirstTransition => { | ||
| 42 | let prg = pio::pio_asm!( | ||
| 43 | r#" | ||
| 44 | .side_set 1 | ||
| 45 | |||
| 46 | ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and | ||
| 47 | ; transitions on the trailing edge, or some time before the first leading edge. | ||
| 48 | |||
| 49 | out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if | ||
| 50 | in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low) | ||
| 51 | "# | ||
| 52 | ); | ||
| 53 | |||
| 54 | common.load_program(&prg.program) | ||
| 55 | } | ||
| 56 | Phase::CaptureOnSecondTransition => { | ||
| 57 | let prg = pio::pio_asm!( | ||
| 58 | r#" | ||
| 59 | .side_set 1 | ||
| 60 | |||
| 61 | ; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and | ||
| 62 | ; is captured on the trailing edge. | ||
| 63 | |||
| 64 | out x, 1 side 0 ; Stall here on empty (keep SCK de-asserted) | ||
| 65 | mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping) | ||
| 66 | in pins, 1 side 0 ; Input data, de-assert SCK | ||
| 67 | "# | ||
| 68 | ); | ||
| 69 | |||
| 70 | common.load_program(&prg.program) | ||
| 71 | } | ||
| 72 | }; | ||
| 73 | |||
| 74 | Self { prg, phase } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// PIO SPI errors. | ||
| 79 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 80 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 81 | #[non_exhaustive] | ||
| 82 | pub enum Error { | ||
| 83 | // No errors for now | ||
| 84 | } | ||
| 85 | |||
| 86 | /// PIO based Spi driver. | ||
| 87 | /// Unlike other PIO programs, the PIO SPI driver owns and holds a reference to | ||
| 88 | /// the PIO memory it uses. This is so that it can be reconfigured at runtime if | ||
| 89 | /// desired. | ||
| 90 | pub struct Spi<'d, PIO: Instance, const SM: usize, M: Mode> { | ||
| 91 | sm: StateMachine<'d, PIO, SM>, | ||
| 92 | cfg: crate::pio::Config<'d, PIO>, | ||
| 93 | program: Option<PioSpiProgram<'d, PIO>>, | ||
| 94 | clk_pin: Pin<'d, PIO>, | ||
| 95 | tx_dma: Option<Peri<'d, AnyChannel>>, | ||
| 96 | rx_dma: Option<Peri<'d, AnyChannel>>, | ||
| 97 | phantom: PhantomData<M>, | ||
| 98 | } | ||
| 99 | |||
| 100 | impl<'d, PIO: Instance, const SM: usize, M: Mode> Spi<'d, PIO, SM, M> { | ||
| 101 | #[allow(clippy::too_many_arguments)] | ||
| 102 | fn new_inner( | ||
| 103 | pio: &mut Common<'d, PIO>, | ||
| 104 | mut sm: StateMachine<'d, PIO, SM>, | ||
| 105 | clk_pin: Peri<'d, impl PioPin>, | ||
| 106 | mosi_pin: Peri<'d, impl PioPin>, | ||
| 107 | miso_pin: Peri<'d, impl PioPin>, | ||
| 108 | tx_dma: Option<Peri<'d, AnyChannel>>, | ||
| 109 | rx_dma: Option<Peri<'d, AnyChannel>>, | ||
| 110 | config: Config, | ||
| 111 | ) -> Self { | ||
| 112 | let program = PioSpiProgram::new(pio, config.phase); | ||
| 113 | |||
| 114 | let mut clk_pin = pio.make_pio_pin(clk_pin); | ||
| 115 | let mosi_pin = pio.make_pio_pin(mosi_pin); | ||
| 116 | let miso_pin = pio.make_pio_pin(miso_pin); | ||
| 117 | |||
| 118 | if let Polarity::IdleHigh = config.polarity { | ||
| 119 | clk_pin.set_output_inversion(true); | ||
| 120 | } else { | ||
| 121 | clk_pin.set_output_inversion(false); | ||
| 122 | } | ||
| 123 | |||
| 124 | let mut cfg = crate::pio::Config::default(); | ||
| 125 | |||
| 126 | cfg.use_program(&program.prg, &[&clk_pin]); | ||
| 127 | cfg.set_out_pins(&[&mosi_pin]); | ||
| 128 | cfg.set_in_pins(&[&miso_pin]); | ||
| 129 | |||
| 130 | cfg.shift_in.auto_fill = true; | ||
| 131 | cfg.shift_in.direction = ShiftDirection::Left; | ||
| 132 | cfg.shift_in.threshold = 8; | ||
| 133 | |||
| 134 | cfg.shift_out.auto_fill = true; | ||
| 135 | cfg.shift_out.direction = ShiftDirection::Left; | ||
| 136 | cfg.shift_out.threshold = 8; | ||
| 137 | |||
| 138 | cfg.clock_divider = calculate_clock_divider(config.frequency); | ||
| 139 | |||
| 140 | sm.set_config(&cfg); | ||
| 141 | |||
| 142 | sm.set_pins(Level::Low, &[&clk_pin, &mosi_pin]); | ||
| 143 | sm.set_pin_dirs(Direction::Out, &[&clk_pin, &mosi_pin]); | ||
| 144 | sm.set_pin_dirs(Direction::In, &[&miso_pin]); | ||
| 145 | |||
| 146 | sm.set_enable(true); | ||
| 147 | |||
| 148 | Self { | ||
| 149 | sm, | ||
| 150 | program: Some(program), | ||
| 151 | cfg, | ||
| 152 | clk_pin, | ||
| 153 | tx_dma, | ||
| 154 | rx_dma, | ||
| 155 | phantom: PhantomData, | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | fn blocking_read_u8(&mut self) -> Result<u8, Error> { | ||
| 160 | while self.sm.rx().empty() {} | ||
| 161 | let value = self.sm.rx().pull() as u8; | ||
| 162 | |||
| 163 | Ok(value) | ||
| 164 | } | ||
| 165 | |||
| 166 | fn blocking_write_u8(&mut self, v: u8) -> Result<(), Error> { | ||
| 167 | let value = u32::from_be_bytes([v, 0, 0, 0]); | ||
| 168 | |||
| 169 | while !self.sm.tx().try_push(value) {} | ||
| 170 | |||
| 171 | // need to clear here for flush to work correctly | ||
| 172 | self.sm.tx().stalled(); | ||
| 173 | |||
| 174 | Ok(()) | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Read data from SPI blocking execution until done. | ||
| 178 | pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { | ||
| 179 | for v in data { | ||
| 180 | self.blocking_write_u8(0)?; | ||
| 181 | *v = self.blocking_read_u8()?; | ||
| 182 | } | ||
| 183 | self.flush()?; | ||
| 184 | Ok(()) | ||
| 185 | } | ||
| 186 | |||
| 187 | /// Write data to SPI blocking execution until done. | ||
| 188 | pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { | ||
| 189 | for v in data { | ||
| 190 | self.blocking_write_u8(*v)?; | ||
| 191 | let _ = self.blocking_read_u8()?; | ||
| 192 | } | ||
| 193 | self.flush()?; | ||
| 194 | Ok(()) | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Transfer data to SPI blocking execution until done. | ||
| 198 | pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { | ||
| 199 | let len = read.len().max(write.len()); | ||
| 200 | for i in 0..len { | ||
| 201 | let wb = write.get(i).copied().unwrap_or(0); | ||
| 202 | self.blocking_write_u8(wb)?; | ||
| 203 | |||
| 204 | let rb = self.blocking_read_u8()?; | ||
| 205 | if let Some(r) = read.get_mut(i) { | ||
| 206 | *r = rb; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | self.flush()?; | ||
| 210 | Ok(()) | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Transfer data in place to SPI blocking execution until done. | ||
| 214 | pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { | ||
| 215 | for v in data { | ||
| 216 | self.blocking_write_u8(*v)?; | ||
| 217 | *v = self.blocking_read_u8()?; | ||
| 218 | } | ||
| 219 | self.flush()?; | ||
| 220 | Ok(()) | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Block execution until SPI is done. | ||
| 224 | pub fn flush(&mut self) -> Result<(), Error> { | ||
| 225 | // Wait for all words in the FIFO to have been pulled by the SM | ||
| 226 | while !self.sm.tx().empty() {} | ||
| 227 | |||
| 228 | // Wait for last value to be written out to the wire | ||
| 229 | while !self.sm.tx().stalled() {} | ||
| 230 | |||
| 231 | Ok(()) | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Set SPI frequency. | ||
| 235 | pub fn set_frequency(&mut self, freq: u32) { | ||
| 236 | self.sm.set_enable(false); | ||
| 237 | |||
| 238 | let divider = calculate_clock_divider(freq); | ||
| 239 | |||
| 240 | // save into the config for later but dont use sm.set_config() since | ||
| 241 | // that operation is relatively more expensive than just setting the | ||
| 242 | // clock divider | ||
| 243 | self.cfg.clock_divider = divider; | ||
| 244 | self.sm.set_clock_divider(divider); | ||
| 245 | |||
| 246 | self.sm.set_enable(true); | ||
| 247 | } | ||
| 248 | |||
| 249 | /// Set SPI config. | ||
| 250 | /// | ||
| 251 | /// This operation will panic if the PIO program needs to be reloaded and | ||
| 252 | /// there is insufficient room. This is unlikely since the programs for each | ||
| 253 | /// phase only differ in size by a single instruction. | ||
| 254 | pub fn set_config(&mut self, pio: &mut Common<'d, PIO>, config: &Config) { | ||
| 255 | self.sm.set_enable(false); | ||
| 256 | |||
| 257 | self.cfg.clock_divider = calculate_clock_divider(config.frequency); | ||
| 258 | |||
| 259 | if let Polarity::IdleHigh = config.polarity { | ||
| 260 | self.clk_pin.set_output_inversion(true); | ||
| 261 | } else { | ||
| 262 | self.clk_pin.set_output_inversion(false); | ||
| 263 | } | ||
| 264 | |||
| 265 | if self.program.as_ref().unwrap().phase != config.phase { | ||
| 266 | let old_program = self.program.take().unwrap(); | ||
| 267 | |||
| 268 | // SAFETY: the state machine is disabled while this happens | ||
| 269 | unsafe { pio.free_instr(old_program.prg.used_memory) }; | ||
| 270 | |||
| 271 | let new_program = PioSpiProgram::new(pio, config.phase); | ||
| 272 | |||
| 273 | self.cfg.use_program(&new_program.prg, &[&self.clk_pin]); | ||
| 274 | self.program = Some(new_program); | ||
| 275 | } | ||
| 276 | |||
| 277 | self.sm.set_config(&self.cfg); | ||
| 278 | self.sm.restart(); | ||
| 279 | |||
| 280 | self.sm.set_enable(true); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | fn calculate_clock_divider(frequency_hz: u32) -> fixed::FixedU32<U8> { | ||
| 285 | // we multiply by 4 since each clock period is equal to 4 instructions | ||
| 286 | |||
| 287 | let sys_freq = clk_sys_freq().to_fixed::<fixed::FixedU64<U8>>(); | ||
| 288 | let target_freq = (frequency_hz * 4).to_fixed::<fixed::FixedU64<U8>>(); | ||
| 289 | (sys_freq / target_freq).to_fixed() | ||
| 290 | } | ||
| 291 | |||
| 292 | impl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Blocking> { | ||
| 293 | /// Create an SPI driver in blocking mode. | ||
| 294 | pub fn new_blocking( | ||
| 295 | pio: &mut Common<'d, PIO>, | ||
| 296 | sm: StateMachine<'d, PIO, SM>, | ||
| 297 | clk: Peri<'d, impl PioPin>, | ||
| 298 | mosi: Peri<'d, impl PioPin>, | ||
| 299 | miso: Peri<'d, impl PioPin>, | ||
| 300 | config: Config, | ||
| 301 | ) -> Self { | ||
| 302 | Self::new_inner(pio, sm, clk, mosi, miso, None, None, config) | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | impl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Async> { | ||
| 307 | /// Create an SPI driver in async mode supporting DMA operations. | ||
| 308 | #[allow(clippy::too_many_arguments)] | ||
| 309 | pub fn new( | ||
| 310 | pio: &mut Common<'d, PIO>, | ||
| 311 | sm: StateMachine<'d, PIO, SM>, | ||
| 312 | clk: Peri<'d, impl PioPin>, | ||
| 313 | mosi: Peri<'d, impl PioPin>, | ||
| 314 | miso: Peri<'d, impl PioPin>, | ||
| 315 | tx_dma: Peri<'d, impl Channel>, | ||
| 316 | rx_dma: Peri<'d, impl Channel>, | ||
| 317 | config: Config, | ||
| 318 | ) -> Self { | ||
| 319 | Self::new_inner( | ||
| 320 | pio, | ||
| 321 | sm, | ||
| 322 | clk, | ||
| 323 | mosi, | ||
| 324 | miso, | ||
| 325 | Some(tx_dma.into()), | ||
| 326 | Some(rx_dma.into()), | ||
| 327 | config, | ||
| 328 | ) | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Read data from SPI using DMA. | ||
| 332 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | ||
| 333 | let (rx, tx) = self.sm.rx_tx(); | ||
| 334 | |||
| 335 | let len = buffer.len(); | ||
| 336 | |||
| 337 | let rx_ch = self.rx_dma.as_mut().unwrap().reborrow(); | ||
| 338 | let rx_transfer = rx.dma_pull(rx_ch, buffer, false); | ||
| 339 | |||
| 340 | let tx_ch = self.tx_dma.as_mut().unwrap().reborrow(); | ||
| 341 | let tx_transfer = tx.dma_push_repeated::<_, u8>(tx_ch, len); | ||
| 342 | |||
| 343 | join(tx_transfer, rx_transfer).await; | ||
| 344 | |||
| 345 | Ok(()) | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Write data to SPI using DMA. | ||
| 349 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 350 | let (rx, tx) = self.sm.rx_tx(); | ||
| 351 | |||
| 352 | let rx_ch = self.rx_dma.as_mut().unwrap().reborrow(); | ||
| 353 | let rx_transfer = rx.dma_pull_repeated::<_, u8>(rx_ch, buffer.len()); | ||
| 354 | |||
| 355 | let tx_ch = self.tx_dma.as_mut().unwrap().reborrow(); | ||
| 356 | let tx_transfer = tx.dma_push(tx_ch, buffer, false); | ||
| 357 | |||
| 358 | join(tx_transfer, rx_transfer).await; | ||
| 359 | |||
| 360 | Ok(()) | ||
| 361 | } | ||
| 362 | |||
| 363 | /// Transfer data to SPI using DMA. | ||
| 364 | pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> { | ||
| 365 | self.transfer_inner(rx_buffer, tx_buffer).await | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Transfer data in place to SPI using DMA. | ||
| 369 | pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { | ||
| 370 | self.transfer_inner(words, words).await | ||
| 371 | } | ||
| 372 | |||
| 373 | async fn transfer_inner(&mut self, rx_buffer: *mut [u8], tx_buffer: *const [u8]) -> Result<(), Error> { | ||
| 374 | let (rx, tx) = self.sm.rx_tx(); | ||
| 375 | |||
| 376 | let mut rx_ch = self.rx_dma.as_mut().unwrap().reborrow(); | ||
| 377 | let rx_transfer = async { | ||
| 378 | rx.dma_pull(rx_ch.reborrow(), unsafe { &mut *rx_buffer }, false).await; | ||
| 379 | |||
| 380 | if tx_buffer.len() > rx_buffer.len() { | ||
| 381 | let read_bytes_len = tx_buffer.len() - rx_buffer.len(); | ||
| 382 | |||
| 383 | rx.dma_pull_repeated::<_, u8>(rx_ch, read_bytes_len).await; | ||
| 384 | } | ||
| 385 | }; | ||
| 386 | |||
| 387 | let mut tx_ch = self.tx_dma.as_mut().unwrap().reborrow(); | ||
| 388 | let tx_transfer = async { | ||
| 389 | tx.dma_push(tx_ch.reborrow(), unsafe { &*tx_buffer }, false).await; | ||
| 390 | |||
| 391 | if rx_buffer.len() > tx_buffer.len() { | ||
| 392 | let write_bytes_len = rx_buffer.len() - tx_buffer.len(); | ||
| 393 | |||
| 394 | tx.dma_push_repeated::<_, u8>(tx_ch, write_bytes_len).await; | ||
| 395 | } | ||
| 396 | }; | ||
| 397 | |||
| 398 | join(tx_transfer, rx_transfer).await; | ||
| 399 | |||
| 400 | Ok(()) | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | // ==================== | ||
| 405 | |||
| 406 | impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, PIO, SM, M> { | ||
| 407 | type Error = Error; | ||
| 408 | fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { | ||
| 409 | self.blocking_transfer_in_place(words)?; | ||
| 410 | Ok(words) | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, PIO, SM, M> { | ||
| 415 | type Error = Error; | ||
| 416 | |||
| 417 | fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { | ||
| 418 | self.blocking_write(words) | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | impl embedded_hal_1::spi::Error for Error { | ||
| 423 | fn kind(&self) -> embedded_hal_1::spi::ErrorKind { | ||
| 424 | match *self {} | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, PIO, SM, M> { | ||
| 429 | type Error = Error; | ||
| 430 | } | ||
| 431 | |||
| 432 | impl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, PIO, SM, M> { | ||
| 433 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 434 | Ok(()) | ||
| 435 | } | ||
| 436 | |||
| 437 | fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { | ||
| 438 | self.blocking_transfer(words, &[]) | ||
| 439 | } | ||
| 440 | |||
| 441 | fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { | ||
| 442 | self.blocking_write(words) | ||
| 443 | } | ||
| 444 | |||
| 445 | fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { | ||
| 446 | self.blocking_transfer(read, write) | ||
| 447 | } | ||
| 448 | |||
| 449 | fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { | ||
| 450 | self.blocking_transfer_in_place(words) | ||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 454 | impl<'d, PIO: Instance, const SM: usize> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, PIO, SM, Async> { | ||
| 455 | async fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 456 | Ok(()) | ||
| 457 | } | ||
| 458 | |||
| 459 | async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { | ||
| 460 | self.write(words).await | ||
| 461 | } | ||
| 462 | |||
| 463 | async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { | ||
| 464 | self.read(words).await | ||
| 465 | } | ||
| 466 | |||
| 467 | async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { | ||
| 468 | self.transfer(read, write).await | ||
| 469 | } | ||
| 470 | |||
| 471 | async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { | ||
| 472 | self.transfer_in_place(words).await | ||
| 473 | } | ||
| 474 | } | ||
diff --git a/embassy-rp/src/psram.rs b/embassy-rp/src/psram.rs new file mode 100644 index 000000000..ae43dd5aa --- /dev/null +++ b/embassy-rp/src/psram.rs | |||
| @@ -0,0 +1,682 @@ | |||
| 1 | //! PSRAM driver for APS6404L and compatible devices | ||
| 2 | //! | ||
| 3 | //! This driver provides support for PSRAM (Pseudo-Static RAM) devices connected via QMI CS1. | ||
| 4 | //! It handles device verification, initialization, and memory-mapped access configuration. | ||
| 5 | //! | ||
| 6 | //! This driver is only available on RP235x chips as it requires the QMI CS1 peripheral. | ||
| 7 | |||
| 8 | // Credit: Initially based on https://github.com/Altaflux/gb-rp2350 (also licensed Apache 2.0 + MIT). | ||
| 9 | // Copyright (c) Altaflux | ||
| 10 | |||
| 11 | #![cfg(feature = "_rp235x")] | ||
| 12 | |||
| 13 | use critical_section::{acquire, release, CriticalSection, RestoreState}; | ||
| 14 | |||
| 15 | use crate::pac; | ||
| 16 | use crate::qmi_cs1::QmiCs1; | ||
| 17 | |||
| 18 | /// PSRAM errors. | ||
| 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 21 | #[non_exhaustive] | ||
| 22 | pub enum Error { | ||
| 23 | /// PSRAM device is not detected or not supported | ||
| 24 | DeviceNotFound, | ||
| 25 | /// Invalid configuration | ||
| 26 | InvalidConfig, | ||
| 27 | /// Detected PSRAM size does not match the expected size | ||
| 28 | SizeMismatch, | ||
| 29 | } | ||
| 30 | |||
| 31 | /// PSRAM device verification type. | ||
| 32 | #[derive(Clone, Copy)] | ||
| 33 | pub enum VerificationType { | ||
| 34 | /// Skip device verification | ||
| 35 | None, | ||
| 36 | /// Verify as APS6404L device | ||
| 37 | Aps6404l, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Memory configuration. | ||
| 41 | #[derive(Clone)] | ||
| 42 | pub struct Config { | ||
| 43 | /// System clock frequency in Hz | ||
| 44 | pub clock_hz: u32, | ||
| 45 | /// Maximum memory operating frequency in Hz | ||
| 46 | pub max_mem_freq: u32, | ||
| 47 | /// Maximum CS assert time in microseconds (must be <= 8 us) | ||
| 48 | pub max_select_us: u32, | ||
| 49 | /// Minimum CS deassert time in nanoseconds (must be >= 18 ns) | ||
| 50 | pub min_deselect_ns: u32, | ||
| 51 | /// Cooldown period between operations (in SCLK cycles) | ||
| 52 | pub cooldown: u8, | ||
| 53 | /// Page break size for memory operations | ||
| 54 | pub page_break: PageBreak, | ||
| 55 | /// Clock divisor for direct mode operations during initialization | ||
| 56 | pub init_clkdiv: u8, | ||
| 57 | /// Enter Quad Mode command | ||
| 58 | pub enter_quad_cmd: Option<u8>, | ||
| 59 | /// Quad Read command (fast read with 4-bit data) | ||
| 60 | pub quad_read_cmd: u8, | ||
| 61 | /// Quad Write command (page program with 4-bit data) | ||
| 62 | pub quad_write_cmd: Option<u8>, | ||
| 63 | /// Number of dummy cycles for quad read operations | ||
| 64 | pub dummy_cycles: u8, | ||
| 65 | /// Read format configuration | ||
| 66 | pub read_format: FormatConfig, | ||
| 67 | /// Write format configuration | ||
| 68 | pub write_format: Option<FormatConfig>, | ||
| 69 | /// Expected memory size in bytes | ||
| 70 | pub mem_size: usize, | ||
| 71 | /// Device verification type | ||
| 72 | pub verification_type: VerificationType, | ||
| 73 | /// Whether the memory is writable via XIP (e.g., PSRAM vs. read-only flash) | ||
| 74 | pub xip_writable: bool, | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Page break configuration for memory window operations. | ||
| 78 | #[derive(Clone, Copy)] | ||
| 79 | pub enum PageBreak { | ||
| 80 | /// No page breaks | ||
| 81 | None, | ||
| 82 | /// Break at 256-byte boundaries | ||
| 83 | _256, | ||
| 84 | /// Break at 1024-byte boundaries | ||
| 85 | _1024, | ||
| 86 | /// Break at 4096-byte boundaries | ||
| 87 | _4096, | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Format configuration for read/write operations. | ||
| 91 | #[derive(Clone)] | ||
| 92 | pub struct FormatConfig { | ||
| 93 | /// Width of command prefix phase | ||
| 94 | pub prefix_width: Width, | ||
| 95 | /// Width of address phase | ||
| 96 | pub addr_width: Width, | ||
| 97 | /// Width of command suffix phase | ||
| 98 | pub suffix_width: Width, | ||
| 99 | /// Width of dummy/turnaround phase | ||
| 100 | pub dummy_width: Width, | ||
| 101 | /// Width of data phase | ||
| 102 | pub data_width: Width, | ||
| 103 | /// Length of prefix (None or 8 bits) | ||
| 104 | pub prefix_len: bool, | ||
| 105 | /// Length of suffix (None or 8 bits) | ||
| 106 | pub suffix_len: bool, | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Interface width for different phases of SPI transfer. | ||
| 110 | #[derive(Clone, Copy)] | ||
| 111 | pub enum Width { | ||
| 112 | /// Single-bit (standard SPI) | ||
| 113 | Single, | ||
| 114 | /// Dual-bit (2 data lines) | ||
| 115 | Dual, | ||
| 116 | /// Quad-bit (4 data lines) | ||
| 117 | Quad, | ||
| 118 | } | ||
| 119 | |||
| 120 | impl Default for Config { | ||
| 121 | fn default() -> Self { | ||
| 122 | Self::aps6404l() | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl Config { | ||
| 127 | /// Create configuration for APS6404L PSRAM. | ||
| 128 | pub fn aps6404l() -> Self { | ||
| 129 | Self { | ||
| 130 | clock_hz: 125_000_000, // Default to 125MHz | ||
| 131 | max_mem_freq: 133_000_000, // APS6404L max frequency | ||
| 132 | max_select_us: 8, // 8 microseconds max CS assert | ||
| 133 | min_deselect_ns: 18, // 18 nanoseconds min CS deassert | ||
| 134 | cooldown: 1, // 1 SCLK cycle cooldown | ||
| 135 | page_break: PageBreak::_1024, // 1024-byte page boundaries | ||
| 136 | init_clkdiv: 10, // Medium clock for initialization | ||
| 137 | enter_quad_cmd: Some(0x35), // Enter Quad Mode | ||
| 138 | quad_read_cmd: 0xEB, // Fast Quad Read | ||
| 139 | quad_write_cmd: Some(0x38), // Quad Page Program | ||
| 140 | dummy_cycles: 24, // 24 dummy cycles for quad read | ||
| 141 | read_format: FormatConfig { | ||
| 142 | prefix_width: Width::Quad, | ||
| 143 | addr_width: Width::Quad, | ||
| 144 | suffix_width: Width::Quad, | ||
| 145 | dummy_width: Width::Quad, | ||
| 146 | data_width: Width::Quad, | ||
| 147 | prefix_len: true, // 8-bit prefix | ||
| 148 | suffix_len: false, // No suffix | ||
| 149 | }, | ||
| 150 | write_format: Some(FormatConfig { | ||
| 151 | prefix_width: Width::Quad, | ||
| 152 | addr_width: Width::Quad, | ||
| 153 | suffix_width: Width::Quad, | ||
| 154 | dummy_width: Width::Quad, | ||
| 155 | data_width: Width::Quad, | ||
| 156 | prefix_len: true, // 8-bit prefix | ||
| 157 | suffix_len: false, // No suffix | ||
| 158 | }), | ||
| 159 | mem_size: 8 * 1024 * 1024, // 8MB for APS6404L | ||
| 160 | verification_type: VerificationType::Aps6404l, | ||
| 161 | xip_writable: true, // PSRAM is writable | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Create a custom memory configuration. | ||
| 166 | pub fn custom( | ||
| 167 | clock_hz: u32, | ||
| 168 | max_mem_freq: u32, | ||
| 169 | max_select_us: u32, | ||
| 170 | min_deselect_ns: u32, | ||
| 171 | cooldown: u8, | ||
| 172 | page_break: PageBreak, | ||
| 173 | init_clkdiv: u8, | ||
| 174 | enter_quad_cmd: Option<u8>, | ||
| 175 | quad_read_cmd: u8, | ||
| 176 | quad_write_cmd: Option<u8>, | ||
| 177 | dummy_cycles: u8, | ||
| 178 | read_format: FormatConfig, | ||
| 179 | write_format: Option<FormatConfig>, | ||
| 180 | mem_size: usize, | ||
| 181 | verification_type: VerificationType, | ||
| 182 | xip_writable: bool, | ||
| 183 | ) -> Self { | ||
| 184 | Self { | ||
| 185 | clock_hz, | ||
| 186 | max_mem_freq, | ||
| 187 | max_select_us, | ||
| 188 | min_deselect_ns, | ||
| 189 | cooldown, | ||
| 190 | page_break, | ||
| 191 | init_clkdiv, | ||
| 192 | enter_quad_cmd, | ||
| 193 | quad_read_cmd, | ||
| 194 | quad_write_cmd, | ||
| 195 | dummy_cycles, | ||
| 196 | read_format, | ||
| 197 | write_format, | ||
| 198 | mem_size, | ||
| 199 | verification_type, | ||
| 200 | xip_writable, | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | /// PSRAM driver. | ||
| 206 | pub struct Psram<'d> { | ||
| 207 | #[allow(dead_code)] | ||
| 208 | qmi_cs1: QmiCs1<'d>, | ||
| 209 | size: usize, | ||
| 210 | } | ||
| 211 | |||
| 212 | impl<'d> Psram<'d> { | ||
| 213 | /// Create a new PSRAM driver instance. | ||
| 214 | /// | ||
| 215 | /// This will detect the PSRAM device and configure it for memory-mapped access. | ||
| 216 | pub fn new(qmi_cs1: QmiCs1<'d>, config: Config) -> Result<Self, Error> { | ||
| 217 | let qmi = pac::QMI; | ||
| 218 | let xip = pac::XIP_CTRL; | ||
| 219 | |||
| 220 | // Verify PSRAM device if requested | ||
| 221 | match config.verification_type { | ||
| 222 | VerificationType::Aps6404l => { | ||
| 223 | Self::verify_aps6404l(&qmi, config.mem_size)?; | ||
| 224 | debug!("APS6404L PSRAM verified, size: {:#x}", config.mem_size); | ||
| 225 | } | ||
| 226 | VerificationType::None => { | ||
| 227 | debug!("Skipping PSRAM verification, assuming size: {:#x}", config.mem_size); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | // Initialize PSRAM with proper timing | ||
| 232 | Self::init_psram(&qmi, &xip, &config)?; | ||
| 233 | |||
| 234 | Ok(Self { | ||
| 235 | qmi_cs1, | ||
| 236 | size: config.mem_size, | ||
| 237 | }) | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Get the detected PSRAM size in bytes. | ||
| 241 | pub fn size(&self) -> usize { | ||
| 242 | self.size | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Get the base address for memory-mapped access. | ||
| 246 | /// | ||
| 247 | /// After initialization, PSRAM can be accessed directly through memory mapping. | ||
| 248 | /// The base address for CS1 is typically 0x11000000. | ||
| 249 | pub fn base_address(&self) -> *mut u8 { | ||
| 250 | 0x1100_0000 as *mut u8 | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Verify APS6404L PSRAM device matches expected configuration. | ||
| 254 | #[link_section = ".data.ram_func"] | ||
| 255 | #[inline(never)] | ||
| 256 | fn verify_aps6404l(qmi: &pac::qmi::Qmi, expected_size: usize) -> Result<(), Error> { | ||
| 257 | // APS6404L-specific constants | ||
| 258 | const EXPECTED_KGD: u8 = 0x5D; | ||
| 259 | crate::multicore::pause_core1(); | ||
| 260 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); | ||
| 261 | |||
| 262 | { | ||
| 263 | // Helper for making sure `release` is called even if `f` panics. | ||
| 264 | struct Guard { | ||
| 265 | state: RestoreState, | ||
| 266 | } | ||
| 267 | |||
| 268 | impl Drop for Guard { | ||
| 269 | #[inline(always)] | ||
| 270 | fn drop(&mut self) { | ||
| 271 | unsafe { release(self.state) } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | let state = unsafe { acquire() }; | ||
| 276 | let _guard = Guard { state }; | ||
| 277 | |||
| 278 | let _cs = unsafe { CriticalSection::new() }; | ||
| 279 | |||
| 280 | let (kgd, eid) = unsafe { Self::read_aps6404l_kgd_eid(qmi) }; | ||
| 281 | |||
| 282 | let mut detected_size: u32 = 0; | ||
| 283 | if kgd == EXPECTED_KGD as u32 { | ||
| 284 | detected_size = 1024 * 1024; | ||
| 285 | let size_id = eid >> 5; | ||
| 286 | if eid == 0x26 || size_id == 2 { | ||
| 287 | // APS6404L-3SQR-SN or 8MB variants | ||
| 288 | detected_size *= 8; | ||
| 289 | } else if size_id == 0 { | ||
| 290 | detected_size *= 2; | ||
| 291 | } else if size_id == 1 { | ||
| 292 | detected_size *= 4; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | // Verify the detected size matches the expected size | ||
| 297 | if detected_size as usize != expected_size { | ||
| 298 | return Err(Error::SizeMismatch); | ||
| 299 | } | ||
| 300 | |||
| 301 | Ok(()) | ||
| 302 | }?; | ||
| 303 | |||
| 304 | crate::multicore::resume_core1(); | ||
| 305 | |||
| 306 | Ok(()) | ||
| 307 | } | ||
| 308 | |||
| 309 | #[link_section = ".data.ram_func"] | ||
| 310 | #[inline(never)] | ||
| 311 | unsafe fn read_aps6404l_kgd_eid(qmi: &pac::qmi::Qmi) -> (u32, u32) { | ||
| 312 | const RESET_ENABLE_CMD: u8 = 0xf5; | ||
| 313 | const READ_ID_CMD: u8 = 0x9f; | ||
| 314 | |||
| 315 | #[allow(unused_assignments)] | ||
| 316 | let mut kgd: u32 = 0; | ||
| 317 | #[allow(unused_assignments)] | ||
| 318 | let mut eid: u32 = 0; | ||
| 319 | |||
| 320 | let qmi_base = qmi.as_ptr() as usize; | ||
| 321 | |||
| 322 | #[cfg(target_arch = "arm")] | ||
| 323 | core::arch::asm!( | ||
| 324 | // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0) | ||
| 325 | "movs {temp}, #30", | ||
| 326 | "lsls {temp}, {temp}, #22", | ||
| 327 | "orr {temp}, {temp}, #1", // Set EN bit | ||
| 328 | "str {temp}, [{qmi_base}]", | ||
| 329 | |||
| 330 | // Poll for BUSY to clear before first operation | ||
| 331 | "1:", | ||
| 332 | "ldr {temp}, [{qmi_base}]", | ||
| 333 | "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position | ||
| 334 | "bmi 1b", // Branch if negative (BUSY = 1) | ||
| 335 | |||
| 336 | // Assert CS1N (bit 3) | ||
| 337 | "ldr {temp}, [{qmi_base}]", | ||
| 338 | "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit (bit 3) | ||
| 339 | "str {temp}, [{qmi_base}]", | ||
| 340 | |||
| 341 | // Transmit RESET_ENABLE_CMD as quad | ||
| 342 | // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD | ||
| 343 | "movs {temp}, {reset_enable_cmd}", | ||
| 344 | "orr {temp}, {temp}, #0x80000", // Set OE (bit 19) | ||
| 345 | "orr {temp}, {temp}, #0x20000", // Set IWIDTH=2 (quad, bits 17:16) | ||
| 346 | "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX | ||
| 347 | |||
| 348 | // Wait for BUSY to clear | ||
| 349 | "2:", | ||
| 350 | "ldr {temp}, [{qmi_base}]", | ||
| 351 | "lsls {temp}, {temp}, #30", | ||
| 352 | "bmi 2b", | ||
| 353 | |||
| 354 | // Read and discard RX data | ||
| 355 | "ldr {temp}, [{qmi_base}, #8]", | ||
| 356 | |||
| 357 | // Deassert CS1N | ||
| 358 | "ldr {temp}, [{qmi_base}]", | ||
| 359 | "bic {temp}, {temp}, #8", // Clear ASSERT_CS1N bit | ||
| 360 | "str {temp}, [{qmi_base}]", | ||
| 361 | |||
| 362 | // Assert CS1N again | ||
| 363 | "ldr {temp}, [{qmi_base}]", | ||
| 364 | "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit | ||
| 365 | "str {temp}, [{qmi_base}]", | ||
| 366 | |||
| 367 | // Read ID loop (7 iterations) | ||
| 368 | "movs {counter}, #0", // Initialize counter | ||
| 369 | |||
| 370 | "3:", // Loop start | ||
| 371 | "cmp {counter}, #0", | ||
| 372 | "bne 4f", // If not first iteration, send 0xFF | ||
| 373 | |||
| 374 | // First iteration: send READ_ID_CMD | ||
| 375 | "movs {temp}, {read_id_cmd}", | ||
| 376 | "b 5f", | ||
| 377 | "4:", // Other iterations: send 0xFF | ||
| 378 | "movs {temp}, #0xFF", | ||
| 379 | "5:", | ||
| 380 | "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX | ||
| 381 | |||
| 382 | // Wait for TXEMPTY | ||
| 383 | "6:", | ||
| 384 | "ldr {temp}, [{qmi_base}]", | ||
| 385 | "lsls {temp}, {temp}, #20", // Shift TXEMPTY (bit 11) to bit 31 | ||
| 386 | "bpl 6b", // Branch if positive (TXEMPTY = 0) | ||
| 387 | |||
| 388 | // Wait for BUSY to clear | ||
| 389 | "7:", | ||
| 390 | "ldr {temp}, [{qmi_base}]", | ||
| 391 | "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position | ||
| 392 | "bmi 7b", // Branch if negative (BUSY = 1) | ||
| 393 | |||
| 394 | // Read RX data | ||
| 395 | "ldr {temp}, [{qmi_base}, #8]", | ||
| 396 | "uxth {temp}, {temp}", // Extract lower 16 bits | ||
| 397 | |||
| 398 | // Store KGD or EID based on iteration | ||
| 399 | "cmp {counter}, #5", | ||
| 400 | "bne 8f", | ||
| 401 | "mov {kgd}, {temp}", // Store KGD | ||
| 402 | "b 9f", | ||
| 403 | "8:", | ||
| 404 | "cmp {counter}, #6", | ||
| 405 | "bne 9f", | ||
| 406 | "mov {eid}, {temp}", // Store EID | ||
| 407 | |||
| 408 | "9:", | ||
| 409 | "adds {counter}, #1", | ||
| 410 | "cmp {counter}, #7", | ||
| 411 | "blt 3b", // Continue loop if counter < 7 | ||
| 412 | |||
| 413 | // Disable direct mode: clear EN and ASSERT_CS1N | ||
| 414 | "movs {temp}, #0", | ||
| 415 | "str {temp}, [{qmi_base}]", | ||
| 416 | |||
| 417 | // Memory barriers | ||
| 418 | "dmb", | ||
| 419 | "dsb", | ||
| 420 | "isb", | ||
| 421 | qmi_base = in(reg) qmi_base, | ||
| 422 | temp = out(reg) _, | ||
| 423 | counter = out(reg) _, | ||
| 424 | kgd = out(reg) kgd, | ||
| 425 | eid = out(reg) eid, | ||
| 426 | reset_enable_cmd = const RESET_ENABLE_CMD as u32, | ||
| 427 | read_id_cmd = const READ_ID_CMD as u32, | ||
| 428 | options(nostack), | ||
| 429 | ); | ||
| 430 | |||
| 431 | #[cfg(target_arch = "riscv32")] | ||
| 432 | unimplemented!("APS6404L PSRAM verification not implemented for RISC-V"); | ||
| 433 | |||
| 434 | (kgd, eid) | ||
| 435 | } | ||
| 436 | |||
| 437 | /// Initialize PSRAM with proper timing. | ||
| 438 | #[link_section = ".data.ram_func"] | ||
| 439 | #[inline(never)] | ||
| 440 | fn init_psram(qmi: &pac::qmi::Qmi, xip_ctrl: &pac::xip_ctrl::XipCtrl, config: &Config) -> Result<(), Error> { | ||
| 441 | // Set PSRAM timing for APS6404 | ||
| 442 | // | ||
| 443 | // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133 MHz. | ||
| 444 | // So: don't allow running at divisor 1 above 100 MHz (because delay of 2 would be too late), | ||
| 445 | // and add an extra 1 to the rxdelay if the divided clock is > 100 MHz (i.e., sys clock > 200 MHz). | ||
| 446 | let clock_hz = config.clock_hz; | ||
| 447 | let max_psram_freq = config.max_mem_freq; | ||
| 448 | |||
| 449 | let mut divisor: u32 = (clock_hz + max_psram_freq - 1) / max_psram_freq; | ||
| 450 | if divisor == 1 && clock_hz > 100_000_000 { | ||
| 451 | divisor = 2; | ||
| 452 | } | ||
| 453 | let mut rxdelay: u32 = divisor; | ||
| 454 | if clock_hz / divisor > 100_000_000 { | ||
| 455 | rxdelay += 1; | ||
| 456 | } | ||
| 457 | |||
| 458 | // - Max select must be <= 8 us. The value is given in multiples of 64 system clocks. | ||
| 459 | // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2). | ||
| 460 | let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz); | ||
| 461 | let max_select: u8 = ((config.max_select_us as u64 * 1_000_000) / clock_period_fs) as u8; | ||
| 462 | let min_deselect: u32 = ((config.min_deselect_ns as u64 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs | ||
| 463 | - u64::from(divisor + 1) / 2) as u32; | ||
| 464 | |||
| 465 | crate::multicore::pause_core1(); | ||
| 466 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); | ||
| 467 | |||
| 468 | if let Some(enter_quad_cmd) = config.enter_quad_cmd { | ||
| 469 | // Helper for making sure `release` is called even if `f` panics. | ||
| 470 | struct Guard { | ||
| 471 | state: RestoreState, | ||
| 472 | } | ||
| 473 | |||
| 474 | impl Drop for Guard { | ||
| 475 | #[inline(always)] | ||
| 476 | fn drop(&mut self) { | ||
| 477 | unsafe { release(self.state) } | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | let state = unsafe { acquire() }; | ||
| 482 | let _guard = Guard { state }; | ||
| 483 | |||
| 484 | let _cs = unsafe { CriticalSection::new() }; | ||
| 485 | |||
| 486 | unsafe { Self::direct_csr_send_init_command(config, enter_quad_cmd) }; | ||
| 487 | |||
| 488 | qmi.mem(1).timing().write(|w| { | ||
| 489 | w.set_cooldown(config.cooldown); | ||
| 490 | w.set_pagebreak(match config.page_break { | ||
| 491 | PageBreak::None => pac::qmi::vals::Pagebreak::NONE, | ||
| 492 | PageBreak::_256 => pac::qmi::vals::Pagebreak::_256, | ||
| 493 | PageBreak::_1024 => pac::qmi::vals::Pagebreak::_1024, | ||
| 494 | PageBreak::_4096 => pac::qmi::vals::Pagebreak::_4096, | ||
| 495 | }); | ||
| 496 | w.set_max_select(max_select); | ||
| 497 | w.set_min_deselect(min_deselect as u8); | ||
| 498 | w.set_rxdelay(rxdelay as u8); | ||
| 499 | w.set_clkdiv(divisor as u8); | ||
| 500 | }); | ||
| 501 | |||
| 502 | // Set PSRAM commands and formats | ||
| 503 | qmi.mem(1).rfmt().write(|w| { | ||
| 504 | let width_to_pac = |w: Width| match w { | ||
| 505 | Width::Single => pac::qmi::vals::PrefixWidth::S, | ||
| 506 | Width::Dual => pac::qmi::vals::PrefixWidth::D, | ||
| 507 | Width::Quad => pac::qmi::vals::PrefixWidth::Q, | ||
| 508 | }; | ||
| 509 | |||
| 510 | w.set_prefix_width(width_to_pac(config.read_format.prefix_width)); | ||
| 511 | w.set_addr_width(match config.read_format.addr_width { | ||
| 512 | Width::Single => pac::qmi::vals::AddrWidth::S, | ||
| 513 | Width::Dual => pac::qmi::vals::AddrWidth::D, | ||
| 514 | Width::Quad => pac::qmi::vals::AddrWidth::Q, | ||
| 515 | }); | ||
| 516 | w.set_suffix_width(match config.read_format.suffix_width { | ||
| 517 | Width::Single => pac::qmi::vals::SuffixWidth::S, | ||
| 518 | Width::Dual => pac::qmi::vals::SuffixWidth::D, | ||
| 519 | Width::Quad => pac::qmi::vals::SuffixWidth::Q, | ||
| 520 | }); | ||
| 521 | w.set_dummy_width(match config.read_format.dummy_width { | ||
| 522 | Width::Single => pac::qmi::vals::DummyWidth::S, | ||
| 523 | Width::Dual => pac::qmi::vals::DummyWidth::D, | ||
| 524 | Width::Quad => pac::qmi::vals::DummyWidth::Q, | ||
| 525 | }); | ||
| 526 | w.set_data_width(match config.read_format.data_width { | ||
| 527 | Width::Single => pac::qmi::vals::DataWidth::S, | ||
| 528 | Width::Dual => pac::qmi::vals::DataWidth::D, | ||
| 529 | Width::Quad => pac::qmi::vals::DataWidth::Q, | ||
| 530 | }); | ||
| 531 | w.set_prefix_len(if config.read_format.prefix_len { | ||
| 532 | pac::qmi::vals::PrefixLen::_8 | ||
| 533 | } else { | ||
| 534 | pac::qmi::vals::PrefixLen::NONE | ||
| 535 | }); | ||
| 536 | w.set_suffix_len(if config.read_format.suffix_len { | ||
| 537 | pac::qmi::vals::SuffixLen::_8 | ||
| 538 | } else { | ||
| 539 | pac::qmi::vals::SuffixLen::NONE | ||
| 540 | }); | ||
| 541 | w.set_dummy_len(match config.dummy_cycles { | ||
| 542 | 0 => pac::qmi::vals::DummyLen::NONE, | ||
| 543 | 4 => pac::qmi::vals::DummyLen::_4, | ||
| 544 | 8 => pac::qmi::vals::DummyLen::_8, | ||
| 545 | 12 => pac::qmi::vals::DummyLen::_12, | ||
| 546 | 16 => pac::qmi::vals::DummyLen::_16, | ||
| 547 | 20 => pac::qmi::vals::DummyLen::_20, | ||
| 548 | 24 => pac::qmi::vals::DummyLen::_24, | ||
| 549 | 28 => pac::qmi::vals::DummyLen::_28, | ||
| 550 | _ => pac::qmi::vals::DummyLen::_24, // Default to 24 | ||
| 551 | }); | ||
| 552 | }); | ||
| 553 | |||
| 554 | qmi.mem(1).rcmd().write(|w| w.set_prefix(config.quad_read_cmd)); | ||
| 555 | |||
| 556 | if let Some(ref write_format) = config.write_format { | ||
| 557 | qmi.mem(1).wfmt().write(|w| { | ||
| 558 | w.set_prefix_width(match write_format.prefix_width { | ||
| 559 | Width::Single => pac::qmi::vals::PrefixWidth::S, | ||
| 560 | Width::Dual => pac::qmi::vals::PrefixWidth::D, | ||
| 561 | Width::Quad => pac::qmi::vals::PrefixWidth::Q, | ||
| 562 | }); | ||
| 563 | w.set_addr_width(match write_format.addr_width { | ||
| 564 | Width::Single => pac::qmi::vals::AddrWidth::S, | ||
| 565 | Width::Dual => pac::qmi::vals::AddrWidth::D, | ||
| 566 | Width::Quad => pac::qmi::vals::AddrWidth::Q, | ||
| 567 | }); | ||
| 568 | w.set_suffix_width(match write_format.suffix_width { | ||
| 569 | Width::Single => pac::qmi::vals::SuffixWidth::S, | ||
| 570 | Width::Dual => pac::qmi::vals::SuffixWidth::D, | ||
| 571 | Width::Quad => pac::qmi::vals::SuffixWidth::Q, | ||
| 572 | }); | ||
| 573 | w.set_dummy_width(match write_format.dummy_width { | ||
| 574 | Width::Single => pac::qmi::vals::DummyWidth::S, | ||
| 575 | Width::Dual => pac::qmi::vals::DummyWidth::D, | ||
| 576 | Width::Quad => pac::qmi::vals::DummyWidth::Q, | ||
| 577 | }); | ||
| 578 | w.set_data_width(match write_format.data_width { | ||
| 579 | Width::Single => pac::qmi::vals::DataWidth::S, | ||
| 580 | Width::Dual => pac::qmi::vals::DataWidth::D, | ||
| 581 | Width::Quad => pac::qmi::vals::DataWidth::Q, | ||
| 582 | }); | ||
| 583 | w.set_prefix_len(if write_format.prefix_len { | ||
| 584 | pac::qmi::vals::PrefixLen::_8 | ||
| 585 | } else { | ||
| 586 | pac::qmi::vals::PrefixLen::NONE | ||
| 587 | }); | ||
| 588 | w.set_suffix_len(if write_format.suffix_len { | ||
| 589 | pac::qmi::vals::SuffixLen::_8 | ||
| 590 | } else { | ||
| 591 | pac::qmi::vals::SuffixLen::NONE | ||
| 592 | }); | ||
| 593 | }); | ||
| 594 | } | ||
| 595 | |||
| 596 | if let Some(quad_write_cmd) = config.quad_write_cmd { | ||
| 597 | qmi.mem(1).wcmd().write(|w| w.set_prefix(quad_write_cmd)); | ||
| 598 | } | ||
| 599 | |||
| 600 | if config.xip_writable { | ||
| 601 | // Enable XIP writable mode for PSRAM | ||
| 602 | xip_ctrl.ctrl().modify(|w| w.set_writable_m1(true)); | ||
| 603 | } else { | ||
| 604 | // Disable XIP writable mode | ||
| 605 | xip_ctrl.ctrl().modify(|w| w.set_writable_m1(false)); | ||
| 606 | } | ||
| 607 | } | ||
| 608 | crate::multicore::resume_core1(); | ||
| 609 | |||
| 610 | Ok(()) | ||
| 611 | } | ||
| 612 | |||
| 613 | #[link_section = ".data.ram_func"] | ||
| 614 | #[inline(never)] | ||
| 615 | unsafe fn direct_csr_send_init_command(config: &Config, init_cmd: u8) { | ||
| 616 | #[cfg(target_arch = "arm")] | ||
| 617 | core::arch::asm!( | ||
| 618 | // Full memory barrier | ||
| 619 | "dmb", | ||
| 620 | "dsb", | ||
| 621 | "isb", | ||
| 622 | |||
| 623 | // Configure QMI Direct CSR register | ||
| 624 | // Load base address of QMI (0x400D0000) | ||
| 625 | "movw {base}, #0x0000", | ||
| 626 | "movt {base}, #0x400D", | ||
| 627 | |||
| 628 | // Load init_clkdiv and shift to bits 29:22 | ||
| 629 | "lsl {temp}, {clkdiv}, #22", | ||
| 630 | |||
| 631 | // OR with EN (bit 0) and AUTO_CS1N (bit 7) | ||
| 632 | "orr {temp}, {temp}, #0x81", | ||
| 633 | |||
| 634 | // Store to DIRECT_CSR register | ||
| 635 | "str {temp}, [{base}, #0]", | ||
| 636 | |||
| 637 | // Memory barrier | ||
| 638 | "dmb", | ||
| 639 | |||
| 640 | // First busy wait loop | ||
| 641 | "1:", | ||
| 642 | "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR | ||
| 643 | "tst {temp}, #0x2", // Test BUSY bit (bit 1) | ||
| 644 | "bne 1b", // Branch if busy | ||
| 645 | |||
| 646 | // Write to Direct TX register | ||
| 647 | "mov {temp}, {enter_quad_cmd}", | ||
| 648 | |||
| 649 | // OR with NOPUSH (bit 20) | ||
| 650 | "orr {temp}, {temp}, #0x100000", | ||
| 651 | |||
| 652 | // Store to DIRECT_TX register (offset 0x4) | ||
| 653 | "str {temp}, [{base}, #4]", | ||
| 654 | |||
| 655 | // Memory barrier | ||
| 656 | "dmb", | ||
| 657 | |||
| 658 | // Second busy wait loop | ||
| 659 | "2:", | ||
| 660 | "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR | ||
| 661 | "tst {temp}, #0x2", // Test BUSY bit (bit 1) | ||
| 662 | "bne 2b", // Branch if busy | ||
| 663 | |||
| 664 | // Disable Direct CSR | ||
| 665 | "mov {temp}, #0", | ||
| 666 | "str {temp}, [{base}, #0]", // Clear DIRECT_CSR register | ||
| 667 | |||
| 668 | // Full memory barrier to ensure no prefetching | ||
| 669 | "dmb", | ||
| 670 | "dsb", | ||
| 671 | "isb", | ||
| 672 | base = out(reg) _, | ||
| 673 | temp = out(reg) _, | ||
| 674 | clkdiv = in(reg) config.init_clkdiv as u32, | ||
| 675 | enter_quad_cmd = in(reg) u32::from(init_cmd), | ||
| 676 | options(nostack), | ||
| 677 | ); | ||
| 678 | |||
| 679 | #[cfg(target_arch = "riscv32")] | ||
| 680 | unimplemented!("Direct CSR command sending is not implemented for RISC-V yet"); | ||
| 681 | } | ||
| 682 | } | ||
diff --git a/embassy-rp/src/qmi_cs1.rs b/embassy-rp/src/qmi_cs1.rs new file mode 100644 index 000000000..b8ae41e35 --- /dev/null +++ b/embassy-rp/src/qmi_cs1.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | //! QMI CS1 peripheral for RP235x | ||
| 2 | //! | ||
| 3 | //! This module provides access to the QMI CS1 functionality for use with external memory devices | ||
| 4 | //! such as PSRAM. The QMI (Quad SPI) controller supports CS1 as a second chip select signal. | ||
| 5 | //! | ||
| 6 | //! This peripheral is only available on RP235x chips. | ||
| 7 | |||
| 8 | #![cfg(feature = "_rp235x")] | ||
| 9 | |||
| 10 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 11 | |||
| 12 | use crate::gpio::Pin as GpioPin; | ||
| 13 | use crate::{pac, peripherals}; | ||
| 14 | |||
| 15 | /// QMI CS1 driver. | ||
| 16 | pub struct QmiCs1<'d> { | ||
| 17 | _inner: Peri<'d, peripherals::QMI_CS1>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'d> QmiCs1<'d> { | ||
| 21 | /// Create a new QMI CS1 instance. | ||
| 22 | pub fn new(qmi_cs1: Peri<'d, peripherals::QMI_CS1>, cs1: Peri<'d, impl QmiCs1Pin>) -> Self { | ||
| 23 | // Configure CS1 pin for QMI function (funcsel = 9) | ||
| 24 | cs1.gpio().ctrl().write(|w| w.set_funcsel(9)); | ||
| 25 | |||
| 26 | // Configure pad settings for high-speed operation | ||
| 27 | cs1.pad_ctrl().write(|w| { | ||
| 28 | #[cfg(feature = "_rp235x")] | ||
| 29 | w.set_iso(false); | ||
| 30 | w.set_ie(true); | ||
| 31 | w.set_drive(pac::pads::vals::Drive::_12M_A); | ||
| 32 | w.set_slewfast(true); | ||
| 33 | }); | ||
| 34 | |||
| 35 | Self { _inner: qmi_cs1 } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | trait SealedInstance {} | ||
| 40 | |||
| 41 | /// QMI CS1 instance trait. | ||
| 42 | #[allow(private_bounds)] | ||
| 43 | pub trait Instance: SealedInstance + PeripheralType {} | ||
| 44 | |||
| 45 | impl SealedInstance for peripherals::QMI_CS1 {} | ||
| 46 | |||
| 47 | impl Instance for peripherals::QMI_CS1 {} | ||
| 48 | |||
| 49 | /// CS1 pin trait for QMI. | ||
| 50 | pub trait QmiCs1Pin: GpioPin {} | ||
| 51 | |||
| 52 | // Implement pin traits for CS1-capable GPIO pins | ||
| 53 | impl QmiCs1Pin for peripherals::PIN_0 {} | ||
| 54 | impl QmiCs1Pin for peripherals::PIN_8 {} | ||
| 55 | impl QmiCs1Pin for peripherals::PIN_19 {} | ||
| 56 | #[cfg(feature = "rp235xb")] | ||
| 57 | impl QmiCs1Pin for peripherals::PIN_47 {} | ||
diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs index 5de00e6b4..77d4a3055 100644 --- a/embassy-rp/src/rtc/datetime_no_deps.rs +++ b/embassy-rp/src/rtc/datetime_no_deps.rs | |||
| @@ -46,6 +46,7 @@ pub struct DateTime { | |||
| 46 | /// A day of the week | 46 | /// A day of the week |
| 47 | #[repr(u8)] | 47 | #[repr(u8)] |
| 48 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] | 48 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] |
| 49 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 49 | #[allow(missing_docs)] | 50 | #[allow(missing_docs)] |
| 50 | pub enum DayOfWeek { | 51 | pub enum DayOfWeek { |
| 51 | Sunday = 0, | 52 | Sunday = 0, |
diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs index d4a3bab2f..433053613 100644 --- a/embassy-rp/src/rtc/filter.rs +++ b/embassy-rp/src/rtc/filter.rs | |||
| @@ -4,7 +4,8 @@ use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1}; | |||
| 4 | /// A filter used for [`RealTimeClock::schedule_alarm`]. | 4 | /// A filter used for [`RealTimeClock::schedule_alarm`]. |
| 5 | /// | 5 | /// |
| 6 | /// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm | 6 | /// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm |
| 7 | #[derive(Default)] | 7 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] |
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct DateTimeFilter { | 9 | pub struct DateTimeFilter { |
| 9 | /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. | 10 | /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. |
| 10 | pub year: Option<u16>, | 11 | pub year: Option<u16>, |
diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 63cf91d28..8b0deed21 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs | |||
| @@ -1,7 +1,12 @@ | |||
| 1 | //! RTC driver. | 1 | //! RTC driver. |
| 2 | mod filter; | 2 | mod filter; |
| 3 | 3 | ||
| 4 | use core::future::poll_fn; | ||
| 5 | use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | 8 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 5 | 10 | ||
| 6 | pub use self::filter::DateTimeFilter; | 11 | pub use self::filter::DateTimeFilter; |
| 7 | 12 | ||
| @@ -11,6 +16,13 @@ mod datetime; | |||
| 11 | 16 | ||
| 12 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | 17 | pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; |
| 13 | use crate::clocks::clk_rtc_freq; | 18 | use crate::clocks::clk_rtc_freq; |
| 19 | use crate::interrupt::typelevel::Binding; | ||
| 20 | use crate::interrupt::{self, InterruptExt}; | ||
| 21 | |||
| 22 | // Static waker for the interrupt handler | ||
| 23 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 24 | // Static flag to indicate if an alarm has occurred | ||
| 25 | static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false); | ||
| 14 | 26 | ||
| 15 | /// A reference to the real time clock of the system | 27 | /// A reference to the real time clock of the system |
| 16 | pub struct Rtc<'d, T: Instance> { | 28 | pub struct Rtc<'d, T: Instance> { |
| @@ -23,10 +35,15 @@ impl<'d, T: Instance> Rtc<'d, T> { | |||
| 23 | /// # Errors | 35 | /// # Errors |
| 24 | /// | 36 | /// |
| 25 | /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. | 37 | /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. |
| 26 | pub fn new(inner: Peri<'d, T>) -> Self { | 38 | pub fn new(inner: Peri<'d, T>, _irq: impl Binding<interrupt::typelevel::RTC_IRQ, InterruptHandler>) -> Self { |
| 27 | // Set the RTC divider | 39 | // Set the RTC divider |
| 28 | inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); | 40 | inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); |
| 29 | 41 | ||
| 42 | // Setup the IRQ | ||
| 43 | // Clear any pending interrupts from the RTC_IRQ interrupt and enable it, so we do not have unexpected interrupts after initialization | ||
| 44 | interrupt::RTC_IRQ.unpend(); | ||
| 45 | unsafe { interrupt::RTC_IRQ.enable() }; | ||
| 46 | |||
| 30 | Self { inner } | 47 | Self { inner } |
| 31 | } | 48 | } |
| 32 | 49 | ||
| @@ -174,6 +191,110 @@ impl<'d, T: Instance> Rtc<'d, T> { | |||
| 174 | pub fn clear_interrupt(&mut self) { | 191 | pub fn clear_interrupt(&mut self) { |
| 175 | self.disable_alarm(); | 192 | self.disable_alarm(); |
| 176 | } | 193 | } |
| 194 | |||
| 195 | /// Check if an alarm is scheduled. | ||
| 196 | /// | ||
| 197 | /// This function checks if the RTC alarm is currently active. If it is, it returns the alarm configuration | ||
| 198 | /// as a [`DateTimeFilter`]. Otherwise, it returns `None`. | ||
| 199 | pub fn alarm_scheduled(&self) -> Option<DateTimeFilter> { | ||
| 200 | // Check if alarm is active | ||
| 201 | if !self.inner.regs().irq_setup_0().read().match_active() { | ||
| 202 | return None; | ||
| 203 | } | ||
| 204 | |||
| 205 | // Get values from both alarm registers | ||
| 206 | let irq_0 = self.inner.regs().irq_setup_0().read(); | ||
| 207 | let irq_1 = self.inner.regs().irq_setup_1().read(); | ||
| 208 | |||
| 209 | // Create a DateTimeFilter and populate it based on which fields are enabled | ||
| 210 | let mut filter = DateTimeFilter::default(); | ||
| 211 | |||
| 212 | if irq_0.year_ena() { | ||
| 213 | filter.year = Some(irq_0.year()); | ||
| 214 | } | ||
| 215 | |||
| 216 | if irq_0.month_ena() { | ||
| 217 | filter.month = Some(irq_0.month()); | ||
| 218 | } | ||
| 219 | |||
| 220 | if irq_0.day_ena() { | ||
| 221 | filter.day = Some(irq_0.day()); | ||
| 222 | } | ||
| 223 | |||
| 224 | if irq_1.dotw_ena() { | ||
| 225 | // Convert day of week value to DayOfWeek enum | ||
| 226 | let day_of_week = match irq_1.dotw() { | ||
| 227 | 0 => DayOfWeek::Sunday, | ||
| 228 | 1 => DayOfWeek::Monday, | ||
| 229 | 2 => DayOfWeek::Tuesday, | ||
| 230 | 3 => DayOfWeek::Wednesday, | ||
| 231 | 4 => DayOfWeek::Thursday, | ||
| 232 | 5 => DayOfWeek::Friday, | ||
| 233 | 6 => DayOfWeek::Saturday, | ||
| 234 | _ => return None, // Invalid day of week | ||
| 235 | }; | ||
| 236 | filter.day_of_week = Some(day_of_week); | ||
| 237 | } | ||
| 238 | |||
| 239 | if irq_1.hour_ena() { | ||
| 240 | filter.hour = Some(irq_1.hour()); | ||
| 241 | } | ||
| 242 | |||
| 243 | if irq_1.min_ena() { | ||
| 244 | filter.minute = Some(irq_1.min()); | ||
| 245 | } | ||
| 246 | |||
| 247 | if irq_1.sec_ena() { | ||
| 248 | filter.second = Some(irq_1.sec()); | ||
| 249 | } | ||
| 250 | |||
| 251 | Some(filter) | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Wait for an RTC alarm to trigger. | ||
| 255 | /// | ||
| 256 | /// This function will wait until the RTC alarm is triggered. If the alarm is already triggered, it will return immediately. | ||
| 257 | /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered. | ||
| 258 | pub async fn wait_for_alarm(&mut self) { | ||
| 259 | poll_fn(|cx| { | ||
| 260 | WAKER.register(cx.waker()); | ||
| 261 | |||
| 262 | // Atomically check and clear the alarm occurred flag to prevent race conditions | ||
| 263 | if critical_section::with(|_| { | ||
| 264 | let occurred = ALARM_OCCURRED.load(Ordering::SeqCst); | ||
| 265 | if occurred { | ||
| 266 | ALARM_OCCURRED.store(false, Ordering::SeqCst); | ||
| 267 | } | ||
| 268 | occurred | ||
| 269 | }) { | ||
| 270 | // Clear the interrupt and disable the alarm | ||
| 271 | self.clear_interrupt(); | ||
| 272 | |||
| 273 | compiler_fence(Ordering::SeqCst); | ||
| 274 | return Poll::Ready(()); | ||
| 275 | } else { | ||
| 276 | return Poll::Pending; | ||
| 277 | } | ||
| 278 | }) | ||
| 279 | .await; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Interrupt handler. | ||
| 284 | pub struct InterruptHandler { | ||
| 285 | _empty: (), | ||
| 286 | } | ||
| 287 | |||
| 288 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC_IRQ> for InterruptHandler { | ||
| 289 | unsafe fn on_interrupt() { | ||
| 290 | // Disable the alarm first thing, to prevent unexpected re-entry | ||
| 291 | let rtc = crate::pac::RTC; | ||
| 292 | rtc.irq_setup_0().modify(|w| w.set_match_ena(false)); | ||
| 293 | |||
| 294 | // Set the alarm occurred flag and wake the waker | ||
| 295 | ALARM_OCCURRED.store(true, Ordering::SeqCst); | ||
| 296 | WAKER.wake(); | ||
| 297 | } | ||
| 177 | } | 298 | } |
| 178 | 299 | ||
| 179 | /// Errors that can occur on methods on [Rtc] | 300 | /// Errors that can occur on methods on [Rtc] |
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index dfb8ca066..190e68d6d 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - fix: Fixed STM32H5 builds requiring time feature | 11 | - fix: Fixed STM32H5 builds requiring time feature |
| 12 | - feat: Derive Clone, Copy for QSPI Config | ||
| 13 | - fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received | ||
| 14 | - feat: stm32/timer: add set_polarity functions for main and complementary outputs in complementary_pwm | ||
| 15 | - Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS | ||
| 16 | - fix: STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577)) | ||
| 17 | - feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581)) | ||
| 18 | - fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648)) | ||
| 19 | - fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes | ||
| 20 | - feat: Configurable gpio speed for QSPI | ||
| 21 | - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs | ||
| 22 | - fix: handle address and data-length errors in OSPI | ||
| 23 | - feat: Allow OSPI DMA writes larger than 64kB using chunking | ||
| 24 | - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times | ||
| 12 | 25 | ||
| 13 | ## 0.4.0 - 2025-08-26 | 26 | ## 0.4.0 - 2025-08-26 |
| 14 | 27 | ||
| @@ -22,6 +35,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 22 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels | 35 | - feat: stm32/adc/v3: allow DMA reads to loop through enable channels |
| 23 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled | 36 | - fix: Fix XSPI not disabling alternate bytes when they were previously enabled |
| 24 | - fix: Fix stm32h7rs init when using external flash via XSPI | 37 | - fix: Fix stm32h7rs init when using external flash via XSPI |
| 38 | - feat: Add Adc::new_with_clock() to configure analog clock | ||
| 39 | - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) | ||
| 40 | - feat: Added support for STM32F1 peripheral pin remapping (AFIO) ([#4430](https://github.com/embassy-rs/embassy/pull/4430)) | ||
| 25 | 41 | ||
| 26 | ## 0.3.0 - 2025-08-12 | 42 | ## 0.3.0 - 2025-08-12 |
| 27 | 43 | ||
| @@ -132,7 +148,7 @@ GPIO: | |||
| 132 | - Refactor AfType ([#3031](https://github.com/embassy-rs/embassy/pull/3031)) | 148 | - Refactor AfType ([#3031](https://github.com/embassy-rs/embassy/pull/3031)) |
| 133 | - Gpiov1: Do not call set_speed for AFType::Input ([#2996](https://github.com/embassy-rs/embassy/pull/2996)) | 149 | - Gpiov1: Do not call set_speed for AFType::Input ([#2996](https://github.com/embassy-rs/embassy/pull/2996)) |
| 134 | 150 | ||
| 135 | UART: | 151 | UART: |
| 136 | - Add embedded-io impls ([#2739](https://github.com/embassy-rs/embassy/pull/2739)) | 152 | - Add embedded-io impls ([#2739](https://github.com/embassy-rs/embassy/pull/2739)) |
| 137 | - Add support for changing baud rate ([#3512](https://github.com/embassy-rs/embassy/pull/3512)) | 153 | - Add support for changing baud rate ([#3512](https://github.com/embassy-rs/embassy/pull/3512)) |
| 138 | - Add split_ref ([#3500](https://github.com/embassy-rs/embassy/pull/3500)) | 154 | - Add split_ref ([#3500](https://github.com/embassy-rs/embassy/pull/3500)) |
| @@ -156,7 +172,7 @@ UART: | |||
| 156 | - Wake receive task for each received byte ([#2722](https://github.com/embassy-rs/embassy/pull/2722)) | 172 | - Wake receive task for each received byte ([#2722](https://github.com/embassy-rs/embassy/pull/2722)) |
| 157 | - Fix dma and idle line detection in ringbuffereduartrx ([#3319](https://github.com/embassy-rs/embassy/pull/3319)) | 173 | - Fix dma and idle line detection in ringbuffereduartrx ([#3319](https://github.com/embassy-rs/embassy/pull/3319)) |
| 158 | 174 | ||
| 159 | SPI: | 175 | SPI: |
| 160 | - Add MISO pullup configuration option ([#2943](https://github.com/embassy-rs/embassy/pull/2943)) | 176 | - Add MISO pullup configuration option ([#2943](https://github.com/embassy-rs/embassy/pull/2943)) |
| 161 | - Add slew rate configuration options ([#3669](https://github.com/embassy-rs/embassy/pull/3669)) | 177 | - Add slew rate configuration options ([#3669](https://github.com/embassy-rs/embassy/pull/3669)) |
| 162 | - Fix blocking_write on nosck spi. ([#3035](https://github.com/embassy-rs/embassy/pull/3035)) | 178 | - Fix blocking_write on nosck spi. ([#3035](https://github.com/embassy-rs/embassy/pull/3035)) |
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index cdb4e07d1..6cd8ed262 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -173,8 +173,8 @@ cortex-m = "0.7.6" | |||
| 173 | futures-util = { version = "0.3.30", default-features = false } | 173 | futures-util = { version = "0.3.30", default-features = false } |
| 174 | sdio-host = "0.9.0" | 174 | sdio-host = "0.9.0" |
| 175 | critical-section = "1.1" | 175 | critical-section = "1.1" |
| 176 | stm32-metapac = { version = "18" } | 176 | #stm32-metapac = { version = "18" } |
| 177 | #stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ecb93d42a6cbcd9e09cab74873908a2ca22327f7" } | 177 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7" } |
| 178 | 178 | ||
| 179 | vcell = "0.1.3" | 179 | vcell = "0.1.3" |
| 180 | nb = "1.0.0" | 180 | nb = "1.0.0" |
| @@ -192,6 +192,7 @@ bitflags = "2.4.2" | |||
| 192 | 192 | ||
| 193 | block-device-driver = { version = "0.2" } | 193 | block-device-driver = { version = "0.2" } |
| 194 | aligned = "0.4.1" | 194 | aligned = "0.4.1" |
| 195 | heapless = "0.9.1" | ||
| 195 | 196 | ||
| 196 | [dev-dependencies] | 197 | [dev-dependencies] |
| 197 | critical-section = { version = "1.1", features = ["std"] } | 198 | critical-section = { version = "1.1", features = ["std"] } |
| @@ -202,8 +203,8 @@ proptest-state-machine = "0.3.0" | |||
| 202 | proc-macro2 = "1.0.36" | 203 | proc-macro2 = "1.0.36" |
| 203 | quote = "1.0.15" | 204 | quote = "1.0.15" |
| 204 | 205 | ||
| 205 | stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} | 206 | #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} |
| 206 | #stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ecb93d42a6cbcd9e09cab74873908a2ca22327f7", default-features = false, features = ["metadata"] } | 207 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7", default-features = false, features = ["metadata"] } |
| 207 | 208 | ||
| 208 | [features] | 209 | [features] |
| 209 | default = ["rt"] | 210 | default = ["rt"] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index deefb13c1..b5f1261fe 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -16,6 +16,27 @@ use stm32_metapac::metadata::{ | |||
| 16 | #[path = "./build_common.rs"] | 16 | #[path = "./build_common.rs"] |
| 17 | mod common; | 17 | mod common; |
| 18 | 18 | ||
| 19 | /// Helper function to handle peripheral versions with underscores. | ||
| 20 | /// For a version like "v1_foo_bar", this generates all prefix combinations: | ||
| 21 | /// - "kind_v1" | ||
| 22 | /// - "kind_v1_foo" | ||
| 23 | /// - "kind_v1_foo_bar" | ||
| 24 | fn foreach_version_cfg( | ||
| 25 | cfgs: &mut common::CfgSet, | ||
| 26 | kind: &str, | ||
| 27 | version: &str, | ||
| 28 | mut cfg_fn: impl FnMut(&mut common::CfgSet, &str), | ||
| 29 | ) { | ||
| 30 | let parts: Vec<&str> = version.split('_').collect(); | ||
| 31 | |||
| 32 | // Generate all possible prefix combinations | ||
| 33 | for i in 1..=parts.len() { | ||
| 34 | let partial_version = parts[0..i].join("_"); | ||
| 35 | let cfg_name = format!("{}_{}", kind, partial_version); | ||
| 36 | cfg_fn(cfgs, &cfg_name); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 19 | fn main() { | 40 | fn main() { |
| 20 | let mut cfgs = common::CfgSet::new(); | 41 | let mut cfgs = common::CfgSet::new(); |
| 21 | common::set_target_cfgs(&mut cfgs); | 42 | common::set_target_cfgs(&mut cfgs); |
| @@ -38,14 +59,18 @@ fn main() { | |||
| 38 | for p in METADATA.peripherals { | 59 | for p in METADATA.peripherals { |
| 39 | if let Some(r) = &p.registers { | 60 | if let Some(r) = &p.registers { |
| 40 | cfgs.enable(r.kind); | 61 | cfgs.enable(r.kind); |
| 41 | cfgs.enable(format!("{}_{}", r.kind, r.version)); | 62 | foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| { |
| 63 | cfgs.enable(cfg_name); | ||
| 64 | }); | ||
| 42 | } | 65 | } |
| 43 | } | 66 | } |
| 44 | 67 | ||
| 45 | for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() { | 68 | for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() { |
| 46 | cfgs.declare(kind); | 69 | cfgs.declare(kind); |
| 47 | for &version in versions.iter() { | 70 | for &version in versions.iter() { |
| 48 | cfgs.declare(format!("{}_{}", kind, version)); | 71 | foreach_version_cfg(&mut cfgs, kind, version, |cfgs, cfg_name| { |
| 72 | cfgs.declare(cfg_name); | ||
| 73 | }); | ||
| 49 | } | 74 | } |
| 50 | } | 75 | } |
| 51 | 76 | ||
| @@ -1366,9 +1391,53 @@ fn main() { | |||
| 1366 | }) | 1391 | }) |
| 1367 | } | 1392 | } |
| 1368 | 1393 | ||
| 1369 | g.extend(quote! { | 1394 | let pin_trait_impl = if let Some(afio) = &p.afio { |
| 1370 | pin_trait_impl!(#tr, #peri, #pin_name, #af); | 1395 | let values = afio |
| 1371 | }) | 1396 | .values |
| 1397 | .iter() | ||
| 1398 | .filter(|v| v.pins.contains(&pin.pin)) | ||
| 1399 | .map(|v| v.value) | ||
| 1400 | .collect::<Vec<_>>(); | ||
| 1401 | |||
| 1402 | if values.is_empty() { | ||
| 1403 | None | ||
| 1404 | } else { | ||
| 1405 | let reg = format_ident!("{}", afio.register.to_lowercase()); | ||
| 1406 | let setter = format_ident!("set_{}", afio.field.to_lowercase()); | ||
| 1407 | let type_and_values = if is_bool_field("AFIO", afio.register, afio.field) { | ||
| 1408 | let values = values.iter().map(|&v| v > 0); | ||
| 1409 | quote!(AfioRemapBool, [#(#values),*]) | ||
| 1410 | } else { | ||
| 1411 | quote!(AfioRemap, [#(#values),*]) | ||
| 1412 | }; | ||
| 1413 | |||
| 1414 | Some(quote! { | ||
| 1415 | pin_trait_afio_impl!(#tr, #peri, #pin_name, {#reg, #setter, #type_and_values}); | ||
| 1416 | }) | ||
| 1417 | } | ||
| 1418 | } else { | ||
| 1419 | let peripherals_with_afio = [ | ||
| 1420 | "CAN", | ||
| 1421 | "CEC", | ||
| 1422 | "ETH", | ||
| 1423 | "I2C", | ||
| 1424 | "SPI", | ||
| 1425 | "SUBGHZSPI", | ||
| 1426 | "USART", | ||
| 1427 | "UART", | ||
| 1428 | "LPUART", | ||
| 1429 | "TIM", | ||
| 1430 | ]; | ||
| 1431 | let not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { | ||
| 1432 | quote!(, crate::gpio::AfioRemapNotApplicable) | ||
| 1433 | } else { | ||
| 1434 | quote!() | ||
| 1435 | }; | ||
| 1436 | |||
| 1437 | Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af #not_applicable);)) | ||
| 1438 | }; | ||
| 1439 | |||
| 1440 | g.extend(pin_trait_impl); | ||
| 1372 | } | 1441 | } |
| 1373 | 1442 | ||
| 1374 | // ADC is special | 1443 | // ADC is special |
| @@ -1563,17 +1632,7 @@ fn main() { | |||
| 1563 | let register = format_ident!("{}", remap_info.register.to_lowercase()); | 1632 | let register = format_ident!("{}", remap_info.register.to_lowercase()); |
| 1564 | let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); | 1633 | let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); |
| 1565 | 1634 | ||
| 1566 | let field_metadata = METADATA | 1635 | let value = if is_bool_field("SYSCFG", &remap_info.register, &remap_info.field) { |
| 1567 | .peripherals | ||
| 1568 | .iter() | ||
| 1569 | .filter(|p| p.name == "SYSCFG") | ||
| 1570 | .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter()) | ||
| 1571 | .filter(|f| f.name.eq_ignore_ascii_case(remap_info.register)) | ||
| 1572 | .flat_map(|f| f.fields.iter()) | ||
| 1573 | .find(|f| f.name.eq_ignore_ascii_case(remap_info.field)) | ||
| 1574 | .unwrap(); | ||
| 1575 | |||
| 1576 | let value = if field_metadata.bit_size == 1 { | ||
| 1577 | let bool_value = format_ident!("{}", remap_info.value > 0); | 1636 | let bool_value = format_ident!("{}", remap_info.value > 0); |
| 1578 | quote!(#bool_value) | 1637 | quote!(#bool_value) |
| 1579 | } else { | 1638 | } else { |
| @@ -2275,3 +2334,17 @@ fn gcd(a: u32, b: u32) -> u32 { | |||
| 2275 | } | 2334 | } |
| 2276 | gcd(b, a % b) | 2335 | gcd(b, a % b) |
| 2277 | } | 2336 | } |
| 2337 | |||
| 2338 | fn is_bool_field(peripheral: &str, register: &str, field: &str) -> bool { | ||
| 2339 | let field_metadata = METADATA | ||
| 2340 | .peripherals | ||
| 2341 | .iter() | ||
| 2342 | .filter(|p| p.name == peripheral) | ||
| 2343 | .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter()) | ||
| 2344 | .filter(|f| f.name.eq_ignore_ascii_case(register)) | ||
| 2345 | .flat_map(|f| f.fields.iter()) | ||
| 2346 | .find(|f| f.name.eq_ignore_ascii_case(field)) | ||
| 2347 | .unwrap(); | ||
| 2348 | |||
| 2349 | field_metadata.bit_size == 1 | ||
| 2350 | } | ||
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 31cbdc0d7..255dc7956 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs | |||
| @@ -83,7 +83,8 @@ pub enum DacChannel { | |||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | /// Number of samples used for averaging. | 85 | /// Number of samples used for averaging. |
| 86 | #[derive(Copy, Clone)] | 86 | #[derive(Copy, Clone, Debug)] |
| 87 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 87 | pub enum Averaging { | 88 | pub enum Averaging { |
| 88 | Disabled, | 89 | Disabled, |
| 89 | Samples2, | 90 | Samples2, |
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f5870801e..f2837a8f1 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -138,7 +138,8 @@ impl<'a> defmt::Format for Prescaler { | |||
| 138 | /// Number of samples used for averaging. | 138 | /// Number of samples used for averaging. |
| 139 | /// TODO: Implement hardware averaging setting. | 139 | /// TODO: Implement hardware averaging setting. |
| 140 | #[allow(unused)] | 140 | #[allow(unused)] |
| 141 | #[derive(Copy, Clone)] | 141 | #[derive(Copy, Clone, Debug)] |
| 142 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 142 | pub enum Averaging { | 143 | pub enum Averaging { |
| 143 | Disabled, | 144 | Disabled, |
| 144 | Samples2, | 145 | Samples2, |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 778edc6f6..ea986f4cf 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -2,12 +2,12 @@ | |||
| 2 | 2 | ||
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | #![allow(missing_docs)] // TODO | 4 | #![allow(missing_docs)] // TODO |
| 5 | #![cfg_attr(adc_f3_v2, allow(unused))] | 5 | #![cfg_attr(adc_f3v3, allow(unused))] |
| 6 | 6 | ||
| 7 | #[cfg(not(any(adc_f3_v2, adc_wba)))] | 7 | #[cfg(not(any(adc_f3v3, adc_wba)))] |
| 8 | #[cfg_attr(adc_f1, path = "f1.rs")] | 8 | #[cfg_attr(adc_f1, path = "f1.rs")] |
| 9 | #[cfg_attr(adc_f3, path = "f3.rs")] | 9 | #[cfg_attr(adc_f3v1, path = "f3.rs")] |
| 10 | #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] | 10 | #[cfg_attr(adc_f3v2, path = "f3_v1_1.rs")] |
| 11 | #[cfg_attr(adc_v1, path = "v1.rs")] | 11 | #[cfg_attr(adc_v1, path = "v1.rs")] |
| 12 | #[cfg_attr(adc_l0, path = "v1.rs")] | 12 | #[cfg_attr(adc_l0, path = "v1.rs")] |
| 13 | #[cfg_attr(adc_v2, path = "v2.rs")] | 13 | #[cfg_attr(adc_v2, path = "v2.rs")] |
| @@ -20,10 +20,10 @@ mod _version; | |||
| 20 | use core::marker::PhantomData; | 20 | use core::marker::PhantomData; |
| 21 | 21 | ||
| 22 | #[allow(unused)] | 22 | #[allow(unused)] |
| 23 | #[cfg(not(any(adc_f3_v2, adc_wba)))] | 23 | #[cfg(not(any(adc_f3v3, adc_wba)))] |
| 24 | pub use _version::*; | 24 | pub use _version::*; |
| 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; |
| 26 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] | 26 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 27 | use embassy_sync::waitqueue::AtomicWaker; | 27 | use embassy_sync::waitqueue::AtomicWaker; |
| 28 | 28 | ||
| 29 | #[cfg(any(adc_u5, adc_wba))] | 29 | #[cfg(any(adc_u5, adc_wba))] |
| @@ -31,7 +31,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 31 | pub mod adc4; | 31 | pub mod adc4; |
| 32 | 32 | ||
| 33 | pub use crate::pac::adc::vals; | 33 | pub use crate::pac::adc::vals; |
| 34 | #[cfg(not(any(adc_f1, adc_f3_v2)))] | 34 | #[cfg(not(any(adc_f1, adc_f3v3)))] |
| 35 | pub use crate::pac::adc::vals::Res as Resolution; | 35 | pub use crate::pac::adc::vals::Res as Resolution; |
| 36 | pub use crate::pac::adc::vals::SampleTime; | 36 | pub use crate::pac::adc::vals::SampleTime; |
| 37 | use crate::peripherals; | 37 | use crate::peripherals; |
| @@ -47,16 +47,16 @@ dma_trait!(RxDma4, adc4::Instance); | |||
| 47 | pub struct Adc<'d, T: Instance> { | 47 | pub struct Adc<'d, T: Instance> { |
| 48 | #[allow(unused)] | 48 | #[allow(unused)] |
| 49 | adc: crate::Peri<'d, T>, | 49 | adc: crate::Peri<'d, T>, |
| 50 | #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_wba)))] | 50 | #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))] |
| 51 | sample_time: SampleTime, | 51 | sample_time: SampleTime, |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] | 54 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 55 | pub struct State { | 55 | pub struct State { |
| 56 | pub waker: AtomicWaker, | 56 | pub waker: AtomicWaker, |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] | 59 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 60 | impl State { | 60 | impl State { |
| 61 | pub const fn new() -> Self { | 61 | pub const fn new() -> Self { |
| 62 | Self { | 62 | Self { |
| @@ -69,10 +69,10 @@ trait SealedInstance { | |||
| 69 | #[cfg(not(adc_wba))] | 69 | #[cfg(not(adc_wba))] |
| 70 | #[allow(unused)] | 70 | #[allow(unused)] |
| 71 | fn regs() -> crate::pac::adc::Adc; | 71 | fn regs() -> crate::pac::adc::Adc; |
| 72 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] | 72 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))] |
| 73 | #[allow(unused)] | 73 | #[allow(unused)] |
| 74 | fn common_regs() -> crate::pac::adccommon::AdcCommon; | 74 | fn common_regs() -> crate::pac::adccommon::AdcCommon; |
| 75 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] | 75 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 76 | fn state() -> &'static State; | 76 | fn state() -> &'static State; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| @@ -100,22 +100,8 @@ pub(crate) fn blocking_delay_us(us: u32) { | |||
| 100 | 100 | ||
| 101 | /// ADC instance. | 101 | /// ADC instance. |
| 102 | #[cfg(not(any( | 102 | #[cfg(not(any( |
| 103 | adc_f1, | 103 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, |
| 104 | adc_v1, | 104 | adc_u5, adc_c0, adc_wba, |
| 105 | adc_l0, | ||
| 106 | adc_v2, | ||
| 107 | adc_v3, | ||
| 108 | adc_v4, | ||
| 109 | adc_g4, | ||
| 110 | adc_f3, | ||
| 111 | adc_f3_v1_1, | ||
| 112 | adc_g0, | ||
| 113 | adc_u0, | ||
| 114 | adc_h5, | ||
| 115 | adc_h7rs, | ||
| 116 | adc_u5, | ||
| 117 | adc_c0, | ||
| 118 | adc_wba, | ||
| 119 | )))] | 105 | )))] |
| 120 | #[allow(private_bounds)] | 106 | #[allow(private_bounds)] |
| 121 | pub trait Instance: SealedInstance + crate::PeripheralType { | 107 | pub trait Instance: SealedInstance + crate::PeripheralType { |
| @@ -123,22 +109,8 @@ pub trait Instance: SealedInstance + crate::PeripheralType { | |||
| 123 | } | 109 | } |
| 124 | /// ADC instance. | 110 | /// ADC instance. |
| 125 | #[cfg(any( | 111 | #[cfg(any( |
| 126 | adc_f1, | 112 | adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, |
| 127 | adc_v1, | 113 | adc_u5, adc_c0, adc_wba, |
| 128 | adc_l0, | ||
| 129 | adc_v2, | ||
| 130 | adc_v3, | ||
| 131 | adc_v4, | ||
| 132 | adc_g4, | ||
| 133 | adc_f3, | ||
| 134 | adc_f3_v1_1, | ||
| 135 | adc_g0, | ||
| 136 | adc_u0, | ||
| 137 | adc_h5, | ||
| 138 | adc_h7rs, | ||
| 139 | adc_u5, | ||
| 140 | adc_c0, | ||
| 141 | adc_wba, | ||
| 142 | ))] | 114 | ))] |
| 143 | #[allow(private_bounds)] | 115 | #[allow(private_bounds)] |
| 144 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { | 116 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { |
| @@ -258,12 +230,12 @@ foreach_adc!( | |||
| 258 | crate::pac::$inst | 230 | crate::pac::$inst |
| 259 | } | 231 | } |
| 260 | 232 | ||
| 261 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5, adc_wba)))] | 233 | #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0, adc_u5, adc_wba)))] |
| 262 | fn common_regs() -> crate::pac::adccommon::AdcCommon { | 234 | fn common_regs() -> crate::pac::adccommon::AdcCommon { |
| 263 | return crate::pac::$common_inst | 235 | return crate::pac::$common_inst |
| 264 | } | 236 | } |
| 265 | 237 | ||
| 266 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] | 238 | #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] |
| 267 | fn state() -> &'static State { | 239 | fn state() -> &'static State { |
| 268 | static STATE: State = State::new(); | 240 | static STATE: State = State::new(); |
| 269 | &STATE | 241 | &STATE |
| @@ -295,7 +267,7 @@ macro_rules! impl_adc_pin { | |||
| 295 | /// Get the maximum reading value for this resolution. | 267 | /// Get the maximum reading value for this resolution. |
| 296 | /// | 268 | /// |
| 297 | /// This is `2**n - 1`. | 269 | /// This is `2**n - 1`. |
| 298 | #[cfg(not(any(adc_f1, adc_f3_v2)))] | 270 | #[cfg(not(any(adc_f1, adc_f3v3)))] |
| 299 | pub const fn resolution_to_max_count(res: Resolution) -> u32 { | 271 | pub const fn resolution_to_max_count(res: Resolution) -> u32 { |
| 300 | match res { | 272 | match res { |
| 301 | #[cfg(adc_v4)] | 273 | #[cfg(adc_v4)] |
| @@ -309,7 +281,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 309 | Resolution::BITS12 => (1 << 12) - 1, | 281 | Resolution::BITS12 => (1 << 12) - 1, |
| 310 | Resolution::BITS10 => (1 << 10) - 1, | 282 | Resolution::BITS10 => (1 << 10) - 1, |
| 311 | Resolution::BITS8 => (1 << 8) - 1, | 283 | Resolution::BITS8 => (1 << 8) - 1, |
| 312 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] | 284 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3v1, adc_f3v2, adc_h5))] |
| 313 | Resolution::BITS6 => (1 << 6) - 1, | 285 | Resolution::BITS6 => (1 << 6) - 1, |
| 314 | #[allow(unreachable_patterns)] | 286 | #[allow(unreachable_patterns)] |
| 315 | _ => core::unreachable!(), | 287 | _ => core::unreachable!(), |
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 7fe502da0..d09374876 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -66,7 +66,11 @@ pub struct Temperature; | |||
| 66 | impl AdcChannel<ADC1> for Temperature {} | 66 | impl AdcChannel<ADC1> for Temperature {} |
| 67 | impl super::SealedAdcChannel<ADC1> for Temperature { | 67 | impl super::SealedAdcChannel<ADC1> for Temperature { |
| 68 | fn channel(&self) -> u8 { | 68 | fn channel(&self) -> u8 { |
| 69 | 16 | 69 | if cfg!(adc_l0) { |
| 70 | 18 | ||
| 71 | } else { | ||
| 72 | 16 | ||
| 73 | } | ||
| 70 | } | 74 | } |
| 71 | } | 75 | } |
| 72 | 76 | ||
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index dc1faa4d1..16063ce4d 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,7 +1,13 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | #[cfg(adc_g0)] | ||
| 3 | use heapless::Vec; | ||
| 2 | use pac::adc::vals::Dmacfg; | 4 | use pac::adc::vals::Dmacfg; |
| 5 | #[cfg(adc_g0)] | ||
| 6 | use pac::adc::vals::{Ckmode, Smpsel}; | ||
| 3 | #[cfg(adc_v3)] | 7 | #[cfg(adc_v3)] |
| 4 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; | 8 | use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; |
| 9 | #[cfg(adc_g0)] | ||
| 10 | pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | ||
| 5 | 11 | ||
| 6 | use super::{ | 12 | use super::{ |
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 13 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, |
| @@ -14,6 +20,11 @@ pub const VREF_DEFAULT_MV: u32 = 3300; | |||
| 14 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 20 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 15 | pub const VREF_CALIB_MV: u32 = 3000; | 21 | pub const VREF_CALIB_MV: u32 = 3000; |
| 16 | 22 | ||
| 23 | #[cfg(adc_g0)] | ||
| 24 | /// The number of variants in Smpsel | ||
| 25 | // TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable | ||
| 26 | const SAMPLE_TIMES_CAPACITY: usize = 2; | ||
| 27 | |||
| 17 | pub struct VrefInt; | 28 | pub struct VrefInt; |
| 18 | impl<T: Instance> AdcChannel<T> for VrefInt {} | 29 | impl<T: Instance> AdcChannel<T> for VrefInt {} |
| 19 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | 30 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { |
| @@ -96,6 +107,8 @@ cfg_if! { | |||
| 96 | } | 107 | } |
| 97 | 108 | ||
| 98 | /// Number of samples used for averaging. | 109 | /// Number of samples used for averaging. |
| 110 | #[derive(Copy, Clone, Debug)] | ||
| 111 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 99 | pub enum Averaging { | 112 | pub enum Averaging { |
| 100 | Disabled, | 113 | Disabled, |
| 101 | Samples2, | 114 | Samples2, |
| @@ -107,8 +120,30 @@ pub enum Averaging { | |||
| 107 | Samples128, | 120 | Samples128, |
| 108 | Samples256, | 121 | Samples256, |
| 109 | } | 122 | } |
| 123 | |||
| 124 | cfg_if! { if #[cfg(adc_g0)] { | ||
| 125 | |||
| 126 | /// Synchronous PCLK prescaler | ||
| 127 | pub enum CkModePclk { | ||
| 128 | DIV1, | ||
| 129 | DIV2, | ||
| 130 | DIV4, | ||
| 131 | } | ||
| 132 | |||
| 133 | /// The analog clock is either the synchronous prescaled PCLK or | ||
| 134 | /// the asynchronous prescaled ADCCLK configured by the RCC mux. | ||
| 135 | /// The data sheet states the maximum analog clock frequency - | ||
| 136 | /// for STM32WL55CC it is 36 MHz. | ||
| 137 | pub enum Clock { | ||
| 138 | Sync { div: CkModePclk }, | ||
| 139 | Async { div: Presc }, | ||
| 140 | } | ||
| 141 | |||
| 142 | }} | ||
| 143 | |||
| 110 | impl<'d, T: Instance> Adc<'d, T> { | 144 | impl<'d, T: Instance> Adc<'d, T> { |
| 111 | pub fn new(adc: Peri<'d, T>) -> Self { | 145 | /// Enable the voltage regulator |
| 146 | fn init_regulator() { | ||
| 112 | rcc::enable_and_reset::<T>(); | 147 | rcc::enable_and_reset::<T>(); |
| 113 | T::regs().cr().modify(|reg| { | 148 | T::regs().cr().modify(|reg| { |
| 114 | #[cfg(not(any(adc_g0, adc_u0)))] | 149 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -117,13 +152,17 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 117 | }); | 152 | }); |
| 118 | 153 | ||
| 119 | // If this is false then each ADC_CHSELR bit enables an input channel. | 154 | // If this is false then each ADC_CHSELR bit enables an input channel. |
| 155 | // This is the reset value, so has no effect. | ||
| 120 | #[cfg(any(adc_g0, adc_u0))] | 156 | #[cfg(any(adc_g0, adc_u0))] |
| 121 | T::regs().cfgr1().modify(|reg| { | 157 | T::regs().cfgr1().modify(|reg| { |
| 122 | reg.set_chselrmod(false); | 158 | reg.set_chselrmod(false); |
| 123 | }); | 159 | }); |
| 124 | 160 | ||
| 125 | blocking_delay_us(20); | 161 | blocking_delay_us(20); |
| 162 | } | ||
| 126 | 163 | ||
| 164 | /// Calibrate to remove conversion offset | ||
| 165 | fn init_calibrate() { | ||
| 127 | T::regs().cr().modify(|reg| { | 166 | T::regs().cr().modify(|reg| { |
| 128 | reg.set_adcal(true); | 167 | reg.set_adcal(true); |
| 129 | }); | 168 | }); |
| @@ -133,6 +172,51 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 133 | } | 172 | } |
| 134 | 173 | ||
| 135 | blocking_delay_us(1); | 174 | blocking_delay_us(1); |
| 175 | } | ||
| 176 | |||
| 177 | /// Initialize the ADC leaving any analog clock at reset value. | ||
| 178 | /// For G0 and WL, this is the async clock without prescaler. | ||
| 179 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 180 | Self::init_regulator(); | ||
| 181 | Self::init_calibrate(); | ||
| 182 | Self { | ||
| 183 | adc, | ||
| 184 | sample_time: SampleTime::from_bits(0), | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | #[cfg(adc_g0)] | ||
| 189 | /// Initialize ADC with explicit clock for the analog ADC | ||
| 190 | pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { | ||
| 191 | Self::init_regulator(); | ||
| 192 | |||
| 193 | #[cfg(any(stm32wl5x))] | ||
| 194 | { | ||
| 195 | // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual | ||
| 196 | let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; | ||
| 197 | match clock { | ||
| 198 | Clock::Async { div: _ } => { | ||
| 199 | assert!(async_clock_available); | ||
| 200 | } | ||
| 201 | Clock::Sync { div: _ } => { | ||
| 202 | if async_clock_available { | ||
| 203 | warn!("Not using configured ADC clock"); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | match clock { | ||
| 209 | Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), | ||
| 210 | Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { | ||
| 211 | reg.set_ckmode(match div { | ||
| 212 | CkModePclk::DIV1 => Ckmode::PCLK, | ||
| 213 | CkModePclk::DIV2 => Ckmode::PCLK_DIV2, | ||
| 214 | CkModePclk::DIV4 => Ckmode::PCLK_DIV4, | ||
| 215 | }) | ||
| 216 | }), | ||
| 217 | } | ||
| 218 | |||
| 219 | Self::init_calibrate(); | ||
| 136 | 220 | ||
| 137 | Self { | 221 | Self { |
| 138 | adc, | 222 | adc, |
| @@ -349,53 +433,78 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 349 | w.set_l(sequence.len() as u8 - 1); | 433 | w.set_l(sequence.len() as u8 - 1); |
| 350 | }); | 434 | }); |
| 351 | 435 | ||
| 352 | #[cfg(any(adc_g0, adc_u0))] | 436 | #[cfg(adc_g0)] |
| 353 | let mut channel_mask = 0; | 437 | { |
| 354 | 438 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | |
| 355 | // Configure channels and ranks | 439 | |
| 356 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 440 | T::regs().chselr().write(|chselr| { |
| 357 | Self::configure_channel(channel, sample_time); | 441 | T::regs().smpr().write(|smpr| { |
| 358 | 442 | for (channel, sample_time) in sequence { | |
| 359 | // Each channel is sampled according to sequence | 443 | chselr.set_chsel(channel.channel.into(), true); |
| 360 | #[cfg(not(any(adc_g0, adc_u0)))] | 444 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { |
| 361 | match _i { | 445 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); |
| 362 | 0..=3 => { | 446 | } else { |
| 363 | T::regs().sqr1().modify(|w| { | 447 | smpr.set_sample_time(sample_times.len(), sample_time); |
| 364 | w.set_sq(_i, channel.channel()); | 448 | if let Err(_) = sample_times.push(sample_time) { |
| 365 | }); | 449 | panic!( |
| 366 | } | 450 | "Implementation is limited to {} unique sample times among all channels.", |
| 367 | 4..=8 => { | 451 | SAMPLE_TIMES_CAPACITY |
| 368 | T::regs().sqr2().modify(|w| { | 452 | ); |
| 369 | w.set_sq(_i - 4, channel.channel()); | 453 | } |
| 370 | }); | 454 | } |
| 371 | } | 455 | } |
| 372 | 9..=13 => { | 456 | }) |
| 373 | T::regs().sqr3().modify(|w| { | 457 | }); |
| 374 | w.set_sq(_i - 9, channel.channel()); | 458 | } |
| 375 | }); | 459 | #[cfg(not(adc_g0))] |
| 460 | { | ||
| 461 | #[cfg(adc_u0)] | ||
| 462 | let mut channel_mask = 0; | ||
| 463 | |||
| 464 | // Configure channels and ranks | ||
| 465 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 466 | Self::configure_channel(channel, sample_time); | ||
| 467 | |||
| 468 | // Each channel is sampled according to sequence | ||
| 469 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 470 | match _i { | ||
| 471 | 0..=3 => { | ||
| 472 | T::regs().sqr1().modify(|w| { | ||
| 473 | w.set_sq(_i, channel.channel()); | ||
| 474 | }); | ||
| 475 | } | ||
| 476 | 4..=8 => { | ||
| 477 | T::regs().sqr2().modify(|w| { | ||
| 478 | w.set_sq(_i - 4, channel.channel()); | ||
| 479 | }); | ||
| 480 | } | ||
| 481 | 9..=13 => { | ||
| 482 | T::regs().sqr3().modify(|w| { | ||
| 483 | w.set_sq(_i - 9, channel.channel()); | ||
| 484 | }); | ||
| 485 | } | ||
| 486 | 14..=15 => { | ||
| 487 | T::regs().sqr4().modify(|w| { | ||
| 488 | w.set_sq(_i - 14, channel.channel()); | ||
| 489 | }); | ||
| 490 | } | ||
| 491 | _ => unreachable!(), | ||
| 376 | } | 492 | } |
| 377 | 14..=15 => { | 493 | |
| 378 | T::regs().sqr4().modify(|w| { | 494 | #[cfg(adc_u0)] |
| 379 | w.set_sq(_i - 14, channel.channel()); | 495 | { |
| 380 | }); | 496 | channel_mask |= 1 << channel.channel(); |
| 381 | } | 497 | } |
| 382 | _ => unreachable!(), | ||
| 383 | } | 498 | } |
| 384 | 499 | ||
| 385 | #[cfg(any(adc_g0, adc_u0))] | 500 | // On G0 and U0 enabled channels are sampled from 0 to last channel. |
| 386 | { | 501 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. |
| 387 | channel_mask |= 1 << channel.channel(); | 502 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. |
| 388 | } | 503 | #[cfg(adc_u0)] |
| 504 | T::regs().chselr().modify(|reg| { | ||
| 505 | reg.set_chsel(channel_mask); | ||
| 506 | }); | ||
| 389 | } | 507 | } |
| 390 | |||
| 391 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | ||
| 392 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | ||
| 393 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | ||
| 394 | #[cfg(any(adc_g0, adc_u0))] | ||
| 395 | T::regs().chselr().modify(|reg| { | ||
| 396 | reg.set_chsel(channel_mask); | ||
| 397 | }); | ||
| 398 | |||
| 399 | // Set continuous mode with oneshot dma. | 508 | // Set continuous mode with oneshot dma. |
| 400 | // Clear overrun flag before starting transfer. | 509 | // Clear overrun flag before starting transfer. |
| 401 | T::regs().isr().modify(|reg| { | 510 | T::regs().isr().modify(|reg| { |
| @@ -450,6 +559,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 450 | }); | 559 | }); |
| 451 | } | 560 | } |
| 452 | 561 | ||
| 562 | #[cfg(not(adc_g0))] | ||
| 453 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 563 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 454 | // RM0492, RM0481, etc. | 564 | // RM0492, RM0481, etc. |
| 455 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 565 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." |
| @@ -464,13 +574,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 464 | 574 | ||
| 465 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 575 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { |
| 466 | self.enable(); | 576 | self.enable(); |
| 577 | #[cfg(not(adc_g0))] | ||
| 467 | Self::configure_channel(channel, self.sample_time); | 578 | Self::configure_channel(channel, self.sample_time); |
| 468 | 579 | #[cfg(adc_g0)] | |
| 580 | T::regs().smpr().write(|reg| { | ||
| 581 | reg.set_sample_time(0, self.sample_time); | ||
| 582 | reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); | ||
| 583 | }); | ||
| 469 | // Select channel | 584 | // Select channel |
| 470 | #[cfg(not(any(adc_g0, adc_u0)))] | 585 | #[cfg(not(any(adc_g0, adc_u0)))] |
| 471 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); | 586 | T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); |
| 472 | #[cfg(any(adc_g0, adc_u0))] | 587 | #[cfg(any(adc_g0, adc_u0))] |
| 473 | T::regs().chselr().write(|reg| reg.set_chsel(1 << channel.channel())); | 588 | T::regs().chselr().write(|reg| { |
| 589 | #[cfg(adc_g0)] | ||
| 590 | reg.set_chsel(channel.channel().into(), true); | ||
| 591 | #[cfg(adc_u0)] | ||
| 592 | reg.set_chsel(1 << channel.channel()); | ||
| 593 | }); | ||
| 474 | 594 | ||
| 475 | // Some models are affected by an erratum: | 595 | // Some models are affected by an erratum: |
| 476 | // If we perform conversions slower than 1 kHz, the first read ADC value can be | 596 | // If we perform conversions slower than 1 kHz, the first read ADC value can be |
| @@ -494,12 +614,20 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 494 | val | 614 | val |
| 495 | } | 615 | } |
| 496 | 616 | ||
| 497 | #[cfg(any(adc_g0, adc_u0))] | 617 | #[cfg(adc_g0)] |
| 618 | pub fn set_oversampling_shift(&mut self, shift: Ovss) { | ||
| 619 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 620 | } | ||
| 621 | #[cfg(adc_u0)] | ||
| 498 | pub fn set_oversampling_shift(&mut self, shift: u8) { | 622 | pub fn set_oversampling_shift(&mut self, shift: u8) { |
| 499 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 623 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 500 | } | 624 | } |
| 501 | 625 | ||
| 502 | #[cfg(any(adc_g0, adc_u0))] | 626 | #[cfg(adc_g0)] |
| 627 | pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { | ||
| 628 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 629 | } | ||
| 630 | #[cfg(adc_u0)] | ||
| 503 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | 631 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { |
| 504 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | 632 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); |
| 505 | } | 633 | } |
| @@ -526,9 +654,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 526 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | 654 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); |
| 527 | } | 655 | } |
| 528 | 656 | ||
| 657 | #[cfg(not(adc_g0))] | ||
| 529 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { | 658 | fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { |
| 530 | cfg_if! { | 659 | cfg_if! { |
| 531 | if #[cfg(any(adc_g0, adc_u0))] { | 660 | if #[cfg(adc_u0)] { |
| 532 | // On G0 and U6 all channels use the same sampling time. | 661 | // On G0 and U6 all channels use the same sampling time. |
| 533 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 662 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); |
| 534 | } else if #[cfg(any(adc_h5, adc_h7rs))] { | 663 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b0871019a..b66437e6e 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -142,7 +142,8 @@ impl Prescaler { | |||
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | /// Number of samples used for averaging. | 144 | /// Number of samples used for averaging. |
| 145 | #[derive(Copy, Clone)] | 145 | #[derive(Copy, Clone, Debug)] |
| 146 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 146 | pub enum Averaging { | 147 | pub enum Averaging { |
| 147 | Disabled, | 148 | Disabled, |
| 148 | Samples2, | 149 | Samples2, |
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 4c0795a2a..8eb188560 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs | |||
| @@ -181,10 +181,10 @@ pub enum TryWriteError { | |||
| 181 | impl<'d> Can<'d> { | 181 | impl<'d> Can<'d> { |
| 182 | /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. | 182 | /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. |
| 183 | /// You must call [Can::enable_non_blocking] to use the peripheral. | 183 | /// You must call [Can::enable_non_blocking] to use the peripheral. |
| 184 | pub fn new<T: Instance>( | 184 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 185 | _peri: Peri<'d, T>, | 185 | _peri: Peri<'d, T>, |
| 186 | rx: Peri<'d, impl RxPin<T>>, | 186 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 187 | tx: Peri<'d, impl TxPin<T>>, | 187 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 188 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> | 188 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> |
| 189 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> | 189 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> |
| 190 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> | 190 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> |
| @@ -194,8 +194,8 @@ impl<'d> Can<'d> { | |||
| 194 | let info = T::info(); | 194 | let info = T::info(); |
| 195 | let regs = &T::info().regs; | 195 | let regs = &T::info().regs; |
| 196 | 196 | ||
| 197 | rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); | 197 | set_as_af!(rx, AfType::input(Pull::None)); |
| 198 | tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 198 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 199 | 199 | ||
| 200 | rcc::enable_and_reset::<T>(); | 200 | rcc::enable_and_reset::<T>(); |
| 201 | 201 | ||
| @@ -229,8 +229,8 @@ impl<'d> Can<'d> { | |||
| 229 | info.sce_interrupt.enable(); | 229 | info.sce_interrupt.enable(); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); | 232 | set_as_af!(rx, AfType::input(Pull::None)); |
| 233 | tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 233 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 234 | 234 | ||
| 235 | Registers(T::regs()).leave_init_mode(); | 235 | Registers(T::regs()).leave_init_mode(); |
| 236 | 236 | ||
| @@ -1218,8 +1218,8 @@ foreach_peripheral!( | |||
| 1218 | }; | 1218 | }; |
| 1219 | ); | 1219 | ); |
| 1220 | 1220 | ||
| 1221 | pin_trait!(RxPin, Instance); | 1221 | pin_trait!(RxPin, Instance, @A); |
| 1222 | pin_trait!(TxPin, Instance); | 1222 | pin_trait!(TxPin, Instance, @A); |
| 1223 | 1223 | ||
| 1224 | trait Index { | 1224 | trait Index { |
| 1225 | fn index(&self) -> usize; | 1225 | fn index(&self) -> usize; |
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 99e40ba62..d8f71e03e 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -185,8 +185,8 @@ impl<'d> CanConfigurator<'d> { | |||
| 185 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> | 185 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> |
| 186 | + 'd, | 186 | + 'd, |
| 187 | ) -> CanConfigurator<'d> { | 187 | ) -> CanConfigurator<'d> { |
| 188 | rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); | 188 | set_as_af!(rx, AfType::input(Pull::None)); |
| 189 | tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 189 | set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 190 | 190 | ||
| 191 | rcc::enable_and_reset::<T>(); | 191 | rcc::enable_and_reset::<T>(); |
| 192 | 192 | ||
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 35d9f8cce..0173b2b5d 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs | |||
| @@ -1814,7 +1814,6 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1814 | // Configure DMA to transfer input to crypto core. | 1814 | // Configure DMA to transfer input to crypto core. |
| 1815 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); | 1815 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 1816 | let options = TransferOptions { | 1816 | let options = TransferOptions { |
| 1817 | #[cfg(not(gpdma))] | ||
| 1818 | priority: crate::dma::Priority::High, | 1817 | priority: crate::dma::Priority::High, |
| 1819 | ..Default::default() | 1818 | ..Default::default() |
| 1820 | }; | 1819 | }; |
| @@ -1834,7 +1833,6 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1834 | // Configure DMA to transfer input to crypto core. | 1833 | // Configure DMA to transfer input to crypto core. |
| 1835 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); | 1834 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 1836 | let options = TransferOptions { | 1835 | let options = TransferOptions { |
| 1837 | #[cfg(not(gpdma))] | ||
| 1838 | priority: crate::dma::Priority::High, | 1836 | priority: crate::dma::Priority::High, |
| 1839 | ..Default::default() | 1837 | ..Default::default() |
| 1840 | }; | 1838 | }; |
| @@ -1853,7 +1851,6 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { | |||
| 1853 | // Configure DMA to get output from crypto core. | 1851 | // Configure DMA to get output from crypto core. |
| 1854 | let src_ptr = T::regs().dout().as_ptr(); | 1852 | let src_ptr = T::regs().dout().as_ptr(); |
| 1855 | let options = TransferOptions { | 1853 | let options = TransferOptions { |
| 1856 | #[cfg(not(gpdma))] | ||
| 1857 | priority: crate::dma::Priority::VeryHigh, | 1854 | priority: crate::dma::Priority::VeryHigh, |
| 1858 | ..Default::default() | 1855 | ..Default::default() |
| 1859 | }; | 1856 | }; |
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index d8f1f96f2..08e001337 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -12,6 +12,7 @@ use crate::{peripherals, Peri}; | |||
| 12 | 12 | ||
| 13 | mod tsel; | 13 | mod tsel; |
| 14 | use embassy_hal_internal::PeripheralType; | 14 | use embassy_hal_internal::PeripheralType; |
| 15 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 15 | pub use tsel::TriggerSel; | 16 | pub use tsel::TriggerSel; |
| 16 | 17 | ||
| 17 | /// Operating mode for DAC channel | 18 | /// Operating mode for DAC channel |
| @@ -96,6 +97,41 @@ pub enum ValueArray<'a> { | |||
| 96 | Bit12Right(&'a [u16]), | 97 | Bit12Right(&'a [u16]), |
| 97 | } | 98 | } |
| 98 | 99 | ||
| 100 | #[derive(Debug)] | ||
| 101 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 102 | enum ChannelEvent { | ||
| 103 | Enable, | ||
| 104 | Disable, | ||
| 105 | } | ||
| 106 | |||
| 107 | struct InnerState { | ||
| 108 | channel_count: usize, | ||
| 109 | } | ||
| 110 | |||
| 111 | type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<InnerState>>; | ||
| 112 | struct State { | ||
| 113 | state: SharedState, | ||
| 114 | } | ||
| 115 | |||
| 116 | impl State { | ||
| 117 | /// Adjusts the channel count in response to a `ChannelEvent`, returning the updated value. | ||
| 118 | pub fn adjust_channel_count(&self, event: ChannelEvent) -> usize { | ||
| 119 | self.state.lock(|state| { | ||
| 120 | { | ||
| 121 | let mut mut_state = state.borrow_mut(); | ||
| 122 | match event { | ||
| 123 | ChannelEvent::Enable => { | ||
| 124 | mut_state.channel_count += 1; | ||
| 125 | } | ||
| 126 | ChannelEvent::Disable => { | ||
| 127 | mut_state.channel_count -= 1; | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | state.borrow().channel_count | ||
| 132 | }) | ||
| 133 | } | ||
| 134 | } | ||
| 99 | /// Driver for a single DAC channel. | 135 | /// Driver for a single DAC channel. |
| 100 | /// | 136 | /// |
| 101 | /// If you want to use both channels, either together or independently, | 137 | /// If you want to use both channels, either together or independently, |
| @@ -249,6 +285,16 @@ impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> { | |||
| 249 | reg.set_en(C::IDX, on); | 285 | reg.set_en(C::IDX, on); |
| 250 | }); | 286 | }); |
| 251 | }); | 287 | }); |
| 288 | let event = if on { | ||
| 289 | ChannelEvent::Enable | ||
| 290 | } else { | ||
| 291 | ChannelEvent::Disable | ||
| 292 | }; | ||
| 293 | let channel_count = T::state().adjust_channel_count(event); | ||
| 294 | // Disable the DAC only if no more channels are using it. | ||
| 295 | if channel_count == 0 { | ||
| 296 | rcc::disable::<T>(); | ||
| 297 | } | ||
| 252 | } | 298 | } |
| 253 | 299 | ||
| 254 | /// Enable this channel. | 300 | /// Enable this channel. |
| @@ -354,7 +400,7 @@ impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> { | |||
| 354 | 400 | ||
| 355 | impl<'d, T: Instance, C: Channel, M: PeriMode> Drop for DacChannel<'d, T, C, M> { | 401 | impl<'d, T: Instance, C: Channel, M: PeriMode> Drop for DacChannel<'d, T, C, M> { |
| 356 | fn drop(&mut self) { | 402 | fn drop(&mut self) { |
| 357 | rcc::disable::<T>(); | 403 | self.disable(); |
| 358 | } | 404 | } |
| 359 | } | 405 | } |
| 360 | 406 | ||
| @@ -529,8 +575,6 @@ impl<'d, T: Instance, M: PeriMode> Dac<'d, T, M> { | |||
| 529 | dma_ch2: Option<ChannelAndRequest<'d>>, | 575 | dma_ch2: Option<ChannelAndRequest<'d>>, |
| 530 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode, | 576 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode, |
| 531 | ) -> Self { | 577 | ) -> Self { |
| 532 | // Enable twice to increment the DAC refcount for each channel. | ||
| 533 | rcc::enable_and_reset::<T>(); | ||
| 534 | rcc::enable_and_reset::<T>(); | 578 | rcc::enable_and_reset::<T>(); |
| 535 | 579 | ||
| 536 | let mut ch1 = DacCh1 { | 580 | let mut ch1 = DacCh1 { |
| @@ -597,6 +641,13 @@ impl<'d, T: Instance, M: PeriMode> Dac<'d, T, M> { | |||
| 597 | 641 | ||
| 598 | trait SealedInstance { | 642 | trait SealedInstance { |
| 599 | fn regs() -> crate::pac::dac::Dac; | 643 | fn regs() -> crate::pac::dac::Dac; |
| 644 | |||
| 645 | fn state() -> &'static State { | ||
| 646 | static STATE: State = State { | ||
| 647 | state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(InnerState { channel_count: 0 })), | ||
| 648 | }; | ||
| 649 | &STATE | ||
| 650 | } | ||
| 600 | } | 651 | } |
| 601 | 652 | ||
| 602 | /// DAC instance. | 653 | /// DAC instance. |
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index d05faee21..bd03f1e00 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs | |||
| @@ -108,7 +108,7 @@ macro_rules! config_pins { | |||
| 108 | ($($pin:ident),*) => { | 108 | ($($pin:ident),*) => { |
| 109 | critical_section::with(|_| { | 109 | critical_section::with(|_| { |
| 110 | $( | 110 | $( |
| 111 | $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); | 111 | set_as_af!($pin, AfType::input(Pull::None)); |
| 112 | )* | 112 | )* |
| 113 | }) | 113 | }) |
| 114 | }; | 114 | }; |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 464823bfc..73ecab070 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -498,46 +498,52 @@ impl AnyChannel { | |||
| 498 | } | 498 | } |
| 499 | } | 499 | } |
| 500 | 500 | ||
| 501 | fn request_stop(&self) { | 501 | fn request_pause(&self) { |
| 502 | let info = self.info(); | 502 | let info = self.info(); |
| 503 | match self.info().dma { | 503 | match self.info().dma { |
| 504 | #[cfg(dma)] | 504 | #[cfg(dma)] |
| 505 | DmaInfo::Dma(r) => { | 505 | DmaInfo::Dma(r) => { |
| 506 | // Disable the channel. Keep the IEs enabled so the irqs still fire. | 506 | // Disable the channel without overwriting the existing configuration |
| 507 | r.st(info.num).cr().write(|w| { | 507 | r.st(info.num).cr().modify(|w| { |
| 508 | w.set_teie(true); | 508 | w.set_en(false); |
| 509 | w.set_tcie(true); | ||
| 510 | }); | 509 | }); |
| 511 | } | 510 | } |
| 512 | #[cfg(bdma)] | 511 | #[cfg(bdma)] |
| 513 | DmaInfo::Bdma(r) => { | 512 | DmaInfo::Bdma(r) => { |
| 514 | // Disable the channel. Keep the IEs enabled so the irqs still fire. | 513 | // Disable the channel without overwriting the existing configuration |
| 515 | r.ch(info.num).cr().write(|w| { | 514 | r.ch(info.num).cr().modify(|w| { |
| 516 | w.set_teie(true); | 515 | w.set_en(false); |
| 517 | w.set_tcie(true); | ||
| 518 | }); | 516 | }); |
| 519 | } | 517 | } |
| 520 | } | 518 | } |
| 521 | } | 519 | } |
| 522 | 520 | ||
| 523 | fn request_pause(&self) { | 521 | fn request_resume(&self) { |
| 522 | self.start() | ||
| 523 | } | ||
| 524 | |||
| 525 | fn request_reset(&self) { | ||
| 524 | let info = self.info(); | 526 | let info = self.info(); |
| 525 | match self.info().dma { | 527 | match self.info().dma { |
| 526 | #[cfg(dma)] | 528 | #[cfg(dma)] |
| 527 | DmaInfo::Dma(r) => { | 529 | DmaInfo::Dma(r) => { |
| 528 | // Disable the channel without overwriting the existing configuration | 530 | // Disable the channel. Keep the IEs enabled so the irqs still fire. |
| 529 | r.st(info.num).cr().modify(|w| { | 531 | r.st(info.num).cr().write(|w| { |
| 530 | w.set_en(false); | 532 | w.set_teie(true); |
| 533 | w.set_tcie(true); | ||
| 531 | }); | 534 | }); |
| 532 | } | 535 | } |
| 533 | #[cfg(bdma)] | 536 | #[cfg(bdma)] |
| 534 | DmaInfo::Bdma(r) => { | 537 | DmaInfo::Bdma(r) => { |
| 535 | // Disable the channel without overwriting the existing configuration | 538 | // Disable the channel. Keep the IEs enabled so the irqs still fire. |
| 536 | r.ch(info.num).cr().modify(|w| { | 539 | r.ch(info.num).cr().write(|w| { |
| 537 | w.set_en(false); | 540 | w.set_teie(true); |
| 541 | w.set_tcie(true); | ||
| 538 | }); | 542 | }); |
| 539 | } | 543 | } |
| 540 | } | 544 | } |
| 545 | |||
| 546 | while self.is_running() {} | ||
| 541 | } | 547 | } |
| 542 | 548 | ||
| 543 | fn is_running(&self) -> bool { | 549 | fn is_running(&self) -> bool { |
| @@ -710,27 +716,31 @@ impl<'a> Transfer<'a> { | |||
| 710 | Self { channel } | 716 | Self { channel } |
| 711 | } | 717 | } |
| 712 | 718 | ||
| 713 | /// Request the transfer to stop. | ||
| 714 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 715 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 716 | /// | ||
| 717 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 718 | pub fn request_stop(&mut self) { | ||
| 719 | self.channel.request_stop() | ||
| 720 | } | ||
| 721 | |||
| 722 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 719 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| 723 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 724 | /// | 720 | /// |
| 721 | /// To resume the transfer, call [`request_resume`](Self::request_resume) again. | ||
| 725 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 722 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 726 | pub fn request_pause(&mut self) { | 723 | pub fn request_pause(&mut self) { |
| 727 | self.channel.request_pause() | 724 | self.channel.request_pause() |
| 728 | } | 725 | } |
| 729 | 726 | ||
| 727 | /// Request the transfer to resume after having been paused. | ||
| 728 | pub fn request_resume(&mut self) { | ||
| 729 | self.channel.request_resume() | ||
| 730 | } | ||
| 731 | |||
| 732 | /// Request the DMA to reset. | ||
| 733 | /// | ||
| 734 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 735 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 736 | pub fn request_reset(&mut self) { | ||
| 737 | self.channel.request_reset() | ||
| 738 | } | ||
| 739 | |||
| 730 | /// Return whether this transfer is still running. | 740 | /// Return whether this transfer is still running. |
| 731 | /// | 741 | /// |
| 732 | /// If this returns `false`, it can be because either the transfer finished, or | 742 | /// If this returns `false`, it can be because either the transfer finished, or |
| 733 | /// it was requested to stop early with [`request_stop`](Self::request_stop). | 743 | /// it was requested to stop early with [`request_pause`](Self::request_pause). |
| 734 | pub fn is_running(&mut self) -> bool { | 744 | pub fn is_running(&mut self) -> bool { |
| 735 | self.channel.is_running() | 745 | self.channel.is_running() |
| 736 | } | 746 | } |
| @@ -754,7 +764,7 @@ impl<'a> Transfer<'a> { | |||
| 754 | 764 | ||
| 755 | impl<'a> Drop for Transfer<'a> { | 765 | impl<'a> Drop for Transfer<'a> { |
| 756 | fn drop(&mut self) { | 766 | fn drop(&mut self) { |
| 757 | self.request_stop(); | 767 | self.request_reset(); |
| 758 | while self.is_running() {} | 768 | while self.is_running() {} |
| 759 | 769 | ||
| 760 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | 770 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." |
| @@ -901,15 +911,6 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 901 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | 911 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); |
| 902 | } | 912 | } |
| 903 | 913 | ||
| 904 | /// Request the DMA to stop. | ||
| 905 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 906 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 907 | /// | ||
| 908 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 909 | pub fn request_stop(&mut self) { | ||
| 910 | self.channel.request_stop() | ||
| 911 | } | ||
| 912 | |||
| 913 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 914 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| 914 | /// To restart the transfer, call [`start`](Self::start) again. | 915 | /// To restart the transfer, call [`start`](Self::start) again. |
| 915 | /// | 916 | /// |
| @@ -918,10 +919,23 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 918 | self.channel.request_pause() | 919 | self.channel.request_pause() |
| 919 | } | 920 | } |
| 920 | 921 | ||
| 922 | /// Request the transfer to resume after having been paused. | ||
| 923 | pub fn request_resume(&mut self) { | ||
| 924 | self.channel.request_resume() | ||
| 925 | } | ||
| 926 | |||
| 927 | /// Request the DMA to reset. | ||
| 928 | /// | ||
| 929 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 930 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 931 | pub fn request_reset(&mut self) { | ||
| 932 | self.channel.request_reset() | ||
| 933 | } | ||
| 934 | |||
| 921 | /// Return whether DMA is still running. | 935 | /// Return whether DMA is still running. |
| 922 | /// | 936 | /// |
| 923 | /// If this returns `false`, it can be because either the transfer finished, or | 937 | /// If this returns `false`, it can be because either the transfer finished, or |
| 924 | /// it was requested to stop early with [`request_stop`](Self::request_stop). | 938 | /// it was requested to stop early with [`request_reset`](Self::request_reset). |
| 925 | pub fn is_running(&mut self) -> bool { | 939 | pub fn is_running(&mut self) -> bool { |
| 926 | self.channel.is_running() | 940 | self.channel.is_running() |
| 927 | } | 941 | } |
| @@ -934,7 +948,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 934 | /// This is designed to be used with streaming input data such as the | 948 | /// This is designed to be used with streaming input data such as the |
| 935 | /// I2S/SAI or ADC. | 949 | /// I2S/SAI or ADC. |
| 936 | /// | 950 | /// |
| 937 | /// When using the UART, you probably want `request_stop()`. | 951 | /// When using the UART, you probably want `request_reset()`. |
| 938 | pub async fn stop(&mut self) { | 952 | pub async fn stop(&mut self) { |
| 939 | self.channel.disable_circular_mode(); | 953 | self.channel.disable_circular_mode(); |
| 940 | //wait until cr.susp reads as true | 954 | //wait until cr.susp reads as true |
| @@ -948,7 +962,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 948 | 962 | ||
| 949 | impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | 963 | impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { |
| 950 | fn drop(&mut self) { | 964 | fn drop(&mut self) { |
| 951 | self.request_stop(); | 965 | self.request_reset(); |
| 952 | while self.is_running() {} | 966 | while self.is_running() {} |
| 953 | 967 | ||
| 954 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | 968 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." |
| @@ -1058,8 +1072,8 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 1058 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | 1072 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. |
| 1059 | /// | 1073 | /// |
| 1060 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 1074 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 1061 | pub fn request_stop(&mut self) { | 1075 | pub fn request_reset(&mut self) { |
| 1062 | self.channel.request_stop() | 1076 | self.channel.request_reset() |
| 1063 | } | 1077 | } |
| 1064 | 1078 | ||
| 1065 | /// Request the transfer to pause, keeping the existing configuration for this channel. | 1079 | /// Request the transfer to pause, keeping the existing configuration for this channel. |
| @@ -1073,7 +1087,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 1073 | /// Return whether DMA is still running. | 1087 | /// Return whether DMA is still running. |
| 1074 | /// | 1088 | /// |
| 1075 | /// If this returns `false`, it can be because either the transfer finished, or | 1089 | /// If this returns `false`, it can be because either the transfer finished, or |
| 1076 | /// it was requested to stop early with [`request_stop`](Self::request_stop). | 1090 | /// it was requested to stop early with [`request_reset`](Self::request_reset). |
| 1077 | pub fn is_running(&mut self) -> bool { | 1091 | pub fn is_running(&mut self) -> bool { |
| 1078 | self.channel.is_running() | 1092 | self.channel.is_running() |
| 1079 | } | 1093 | } |
| @@ -1098,7 +1112,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 1098 | 1112 | ||
| 1099 | impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> { | 1113 | impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> { |
| 1100 | fn drop(&mut self) { | 1114 | fn drop(&mut self) { |
| 1101 | self.request_stop(); | 1115 | self.request_reset(); |
| 1102 | while self.is_running() {} | 1116 | while self.is_running() {} |
| 1103 | 1117 | ||
| 1104 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | 1118 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." |
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs deleted file mode 100644 index 151e4ab9f..000000000 --- a/embassy-stm32/src/dma/gpdma.rs +++ /dev/null | |||
| @@ -1,339 +0,0 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::pin::Pin; | ||
| 5 | use core::sync::atomic::{fence, Ordering}; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 7 | |||
| 8 | use embassy_hal_internal::Peri; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | |||
| 11 | use super::word::{Word, WordSize}; | ||
| 12 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | ||
| 13 | use crate::interrupt::typelevel::Interrupt; | ||
| 14 | use crate::interrupt::Priority; | ||
| 15 | use crate::pac; | ||
| 16 | use crate::pac::gpdma::vals; | ||
| 17 | |||
| 18 | pub(crate) struct ChannelInfo { | ||
| 19 | pub(crate) dma: pac::gpdma::Gpdma, | ||
| 20 | pub(crate) num: usize, | ||
| 21 | #[cfg(feature = "_dual-core")] | ||
| 22 | pub(crate) irq: pac::Interrupt, | ||
| 23 | } | ||
| 24 | |||
| 25 | /// GPDMA transfer options. | ||
| 26 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | #[non_exhaustive] | ||
| 29 | pub struct TransferOptions {} | ||
| 30 | |||
| 31 | impl Default for TransferOptions { | ||
| 32 | fn default() -> Self { | ||
| 33 | Self {} | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | impl From<WordSize> for vals::Dw { | ||
| 38 | fn from(raw: WordSize) -> Self { | ||
| 39 | match raw { | ||
| 40 | WordSize::OneByte => Self::BYTE, | ||
| 41 | WordSize::TwoBytes => Self::HALF_WORD, | ||
| 42 | WordSize::FourBytes => Self::WORD, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | pub(crate) struct ChannelState { | ||
| 48 | waker: AtomicWaker, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl ChannelState { | ||
| 52 | pub(crate) const NEW: Self = Self { | ||
| 53 | waker: AtomicWaker::new(), | ||
| 54 | }; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// safety: must be called only once | ||
| 58 | pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: Priority) { | ||
| 59 | foreach_interrupt! { | ||
| 60 | ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { | ||
| 61 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority); | ||
| 62 | #[cfg(not(feature = "_dual-core"))] | ||
| 63 | crate::interrupt::typelevel::$irq::enable(); | ||
| 64 | }; | ||
| 65 | } | ||
| 66 | crate::_generated::init_gpdma(); | ||
| 67 | } | ||
| 68 | |||
| 69 | impl AnyChannel { | ||
| 70 | /// Safety: Must be called with a matching set of parameters for a valid dma channel | ||
| 71 | pub(crate) unsafe fn on_irq(&self) { | ||
| 72 | let info = self.info(); | ||
| 73 | #[cfg(feature = "_dual-core")] | ||
| 74 | { | ||
| 75 | use embassy_hal_internal::interrupt::InterruptExt as _; | ||
| 76 | info.irq.enable(); | ||
| 77 | } | ||
| 78 | |||
| 79 | let state = &STATE[self.id as usize]; | ||
| 80 | |||
| 81 | let ch = info.dma.ch(info.num); | ||
| 82 | let sr = ch.sr().read(); | ||
| 83 | |||
| 84 | if sr.dtef() { | ||
| 85 | panic!( | ||
| 86 | "DMA: data transfer error on DMA@{:08x} channel {}", | ||
| 87 | info.dma.as_ptr() as u32, | ||
| 88 | info.num | ||
| 89 | ); | ||
| 90 | } | ||
| 91 | if sr.usef() { | ||
| 92 | panic!( | ||
| 93 | "DMA: user settings error on DMA@{:08x} channel {}", | ||
| 94 | info.dma.as_ptr() as u32, | ||
| 95 | info.num | ||
| 96 | ); | ||
| 97 | } | ||
| 98 | |||
| 99 | if sr.suspf() || sr.tcf() { | ||
| 100 | // disable all xxIEs to prevent the irq from firing again. | ||
| 101 | ch.cr().write(|_| {}); | ||
| 102 | |||
| 103 | // Wake the future. It'll look at tcf and see it's set. | ||
| 104 | state.waker.wake(); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | /// DMA transfer. | ||
| 110 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 111 | pub struct Transfer<'a> { | ||
| 112 | channel: Peri<'a, AnyChannel>, | ||
| 113 | } | ||
| 114 | |||
| 115 | impl<'a> Transfer<'a> { | ||
| 116 | /// Create a new read DMA transfer (peripheral to memory). | ||
| 117 | pub unsafe fn new_read<W: Word>( | ||
| 118 | channel: Peri<'a, impl Channel>, | ||
| 119 | request: Request, | ||
| 120 | peri_addr: *mut W, | ||
| 121 | buf: &'a mut [W], | ||
| 122 | options: TransferOptions, | ||
| 123 | ) -> Self { | ||
| 124 | Self::new_read_raw(channel, request, peri_addr, buf, options) | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. | ||
| 128 | pub unsafe fn new_read_raw<MW: Word, PW: Word>( | ||
| 129 | channel: Peri<'a, impl Channel>, | ||
| 130 | request: Request, | ||
| 131 | peri_addr: *mut PW, | ||
| 132 | buf: *mut [MW], | ||
| 133 | options: TransferOptions, | ||
| 134 | ) -> Self { | ||
| 135 | Self::new_inner( | ||
| 136 | channel.into(), | ||
| 137 | request, | ||
| 138 | Dir::PeripheralToMemory, | ||
| 139 | peri_addr as *const u32, | ||
| 140 | buf as *mut MW as *mut u32, | ||
| 141 | buf.len(), | ||
| 142 | true, | ||
| 143 | PW::size(), | ||
| 144 | MW::size(), | ||
| 145 | options, | ||
| 146 | ) | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Create a new write DMA transfer (memory to peripheral). | ||
| 150 | pub unsafe fn new_write<MW: Word, PW: Word>( | ||
| 151 | channel: Peri<'a, impl Channel>, | ||
| 152 | request: Request, | ||
| 153 | buf: &'a [MW], | ||
| 154 | peri_addr: *mut PW, | ||
| 155 | options: TransferOptions, | ||
| 156 | ) -> Self { | ||
| 157 | Self::new_write_raw(channel, request, buf, peri_addr, options) | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. | ||
| 161 | pub unsafe fn new_write_raw<MW: Word, PW: Word>( | ||
| 162 | channel: Peri<'a, impl Channel>, | ||
| 163 | request: Request, | ||
| 164 | buf: *const [MW], | ||
| 165 | peri_addr: *mut PW, | ||
| 166 | options: TransferOptions, | ||
| 167 | ) -> Self { | ||
| 168 | Self::new_inner( | ||
| 169 | channel.into(), | ||
| 170 | request, | ||
| 171 | Dir::MemoryToPeripheral, | ||
| 172 | peri_addr as *const u32, | ||
| 173 | buf as *const MW as *mut u32, | ||
| 174 | buf.len(), | ||
| 175 | true, | ||
| 176 | MW::size(), | ||
| 177 | PW::size(), | ||
| 178 | options, | ||
| 179 | ) | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. | ||
| 183 | pub unsafe fn new_write_repeated<MW: Word, PW: Word>( | ||
| 184 | channel: Peri<'a, impl Channel>, | ||
| 185 | request: Request, | ||
| 186 | repeated: &'a MW, | ||
| 187 | count: usize, | ||
| 188 | peri_addr: *mut PW, | ||
| 189 | options: TransferOptions, | ||
| 190 | ) -> Self { | ||
| 191 | Self::new_inner( | ||
| 192 | channel.into(), | ||
| 193 | request, | ||
| 194 | Dir::MemoryToPeripheral, | ||
| 195 | peri_addr as *const u32, | ||
| 196 | repeated as *const MW as *mut u32, | ||
| 197 | count, | ||
| 198 | false, | ||
| 199 | MW::size(), | ||
| 200 | PW::size(), | ||
| 201 | options, | ||
| 202 | ) | ||
| 203 | } | ||
| 204 | |||
| 205 | unsafe fn new_inner( | ||
| 206 | channel: Peri<'a, AnyChannel>, | ||
| 207 | request: Request, | ||
| 208 | dir: Dir, | ||
| 209 | peri_addr: *const u32, | ||
| 210 | mem_addr: *mut u32, | ||
| 211 | mem_len: usize, | ||
| 212 | incr_mem: bool, | ||
| 213 | data_size: WordSize, | ||
| 214 | dst_size: WordSize, | ||
| 215 | _options: TransferOptions, | ||
| 216 | ) -> Self { | ||
| 217 | // BNDT is specified as bytes, not as number of transfers. | ||
| 218 | let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { | ||
| 219 | panic!("DMA transfers may not be larger than 65535 bytes."); | ||
| 220 | }; | ||
| 221 | |||
| 222 | let info = channel.info(); | ||
| 223 | let ch = info.dma.ch(info.num); | ||
| 224 | |||
| 225 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 226 | fence(Ordering::SeqCst); | ||
| 227 | |||
| 228 | let this = Self { channel }; | ||
| 229 | |||
| 230 | ch.cr().write(|w| w.set_reset(true)); | ||
| 231 | ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs | ||
| 232 | ch.llr().write(|_| {}); // no linked list | ||
| 233 | ch.tr1().write(|w| { | ||
| 234 | w.set_sdw(data_size.into()); | ||
| 235 | w.set_ddw(dst_size.into()); | ||
| 236 | w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); | ||
| 237 | w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); | ||
| 238 | }); | ||
| 239 | ch.tr2().write(|w| { | ||
| 240 | w.set_dreq(match dir { | ||
| 241 | Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL, | ||
| 242 | Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL, | ||
| 243 | }); | ||
| 244 | w.set_reqsel(request); | ||
| 245 | }); | ||
| 246 | ch.tr3().write(|_| {}); // no address offsets. | ||
| 247 | ch.br1().write(|w| w.set_bndt(bndt)); | ||
| 248 | |||
| 249 | match dir { | ||
| 250 | Dir::MemoryToPeripheral => { | ||
| 251 | ch.sar().write_value(mem_addr as _); | ||
| 252 | ch.dar().write_value(peri_addr as _); | ||
| 253 | } | ||
| 254 | Dir::PeripheralToMemory => { | ||
| 255 | ch.sar().write_value(peri_addr as _); | ||
| 256 | ch.dar().write_value(mem_addr as _); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | ch.cr().write(|w| { | ||
| 261 | // Enable interrupts | ||
| 262 | w.set_tcie(true); | ||
| 263 | w.set_useie(true); | ||
| 264 | w.set_dteie(true); | ||
| 265 | w.set_suspie(true); | ||
| 266 | |||
| 267 | // Start it | ||
| 268 | w.set_en(true); | ||
| 269 | }); | ||
| 270 | |||
| 271 | this | ||
| 272 | } | ||
| 273 | |||
| 274 | /// Request the transfer to stop. | ||
| 275 | /// | ||
| 276 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 277 | pub fn request_stop(&mut self) { | ||
| 278 | let info = self.channel.info(); | ||
| 279 | let ch = info.dma.ch(info.num); | ||
| 280 | |||
| 281 | ch.cr().modify(|w| w.set_susp(true)) | ||
| 282 | } | ||
| 283 | |||
| 284 | /// Return whether this transfer is still running. | ||
| 285 | /// | ||
| 286 | /// If this returns `false`, it can be because either the transfer finished, or | ||
| 287 | /// it was requested to stop early with [`request_stop`](Self::request_stop). | ||
| 288 | pub fn is_running(&mut self) -> bool { | ||
| 289 | let info = self.channel.info(); | ||
| 290 | let ch = info.dma.ch(info.num); | ||
| 291 | |||
| 292 | let sr = ch.sr().read(); | ||
| 293 | !sr.tcf() && !sr.suspf() | ||
| 294 | } | ||
| 295 | |||
| 296 | /// Gets the total remaining transfers for the channel | ||
| 297 | /// Note: this will be zero for transfers that completed without cancellation. | ||
| 298 | pub fn get_remaining_transfers(&self) -> u16 { | ||
| 299 | let info = self.channel.info(); | ||
| 300 | let ch = info.dma.ch(info.num); | ||
| 301 | |||
| 302 | ch.br1().read().bndt() | ||
| 303 | } | ||
| 304 | |||
| 305 | /// Blocking wait until the transfer finishes. | ||
| 306 | pub fn blocking_wait(mut self) { | ||
| 307 | while self.is_running() {} | ||
| 308 | |||
| 309 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 310 | fence(Ordering::SeqCst); | ||
| 311 | |||
| 312 | core::mem::forget(self); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | impl<'a> Drop for Transfer<'a> { | ||
| 317 | fn drop(&mut self) { | ||
| 318 | self.request_stop(); | ||
| 319 | while self.is_running() {} | ||
| 320 | |||
| 321 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 322 | fence(Ordering::SeqCst); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | impl<'a> Unpin for Transfer<'a> {} | ||
| 327 | impl<'a> Future for Transfer<'a> { | ||
| 328 | type Output = (); | ||
| 329 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 330 | let state = &STATE[self.channel.id as usize]; | ||
| 331 | state.waker.register(cx.waker()); | ||
| 332 | |||
| 333 | if self.is_running() { | ||
| 334 | Poll::Pending | ||
| 335 | } else { | ||
| 336 | Poll::Ready(()) | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
diff --git a/embassy-stm32/src/dma/gpdma/linked_list.rs b/embassy-stm32/src/dma/gpdma/linked_list.rs new file mode 100644 index 000000000..f7c1fbbed --- /dev/null +++ b/embassy-stm32/src/dma/gpdma/linked_list.rs | |||
| @@ -0,0 +1,267 @@ | |||
| 1 | //! Implementation of the GPDMA linked list and linked list items. | ||
| 2 | #![macro_use] | ||
| 3 | |||
| 4 | use stm32_metapac::gpdma::regs; | ||
| 5 | use stm32_metapac::gpdma::vals::Dreq; | ||
| 6 | |||
| 7 | use crate::dma::word::{Word, WordSize}; | ||
| 8 | use crate::dma::{Dir, Request}; | ||
| 9 | |||
| 10 | /// The mode in which to run the linked list. | ||
| 11 | #[derive(Debug)] | ||
| 12 | pub enum RunMode { | ||
| 13 | /// List items are not linked together. | ||
| 14 | Unlinked, | ||
| 15 | /// The list is linked sequentially and only run once. | ||
| 16 | Once, | ||
| 17 | /// The list is linked sequentially, and the end of the list is linked to the beginning. | ||
| 18 | Circular, | ||
| 19 | } | ||
| 20 | |||
| 21 | /// A linked-list item for linear GPDMA transfers. | ||
| 22 | /// | ||
| 23 | /// Also works for 2D-capable GPDMA channels, but does not use 2D capabilities. | ||
| 24 | #[derive(Debug, Copy, Clone, Default)] | ||
| 25 | #[repr(C)] | ||
| 26 | pub struct LinearItem { | ||
| 27 | /// Transfer register 1. | ||
| 28 | pub tr1: regs::ChTr1, | ||
| 29 | /// Transfer register 2. | ||
| 30 | pub tr2: regs::ChTr2, | ||
| 31 | /// Block register 2. | ||
| 32 | pub br1: regs::ChBr1, | ||
| 33 | /// Source address register. | ||
| 34 | pub sar: u32, | ||
| 35 | /// Destination address register. | ||
| 36 | pub dar: u32, | ||
| 37 | /// Linked-list address register. | ||
| 38 | pub llr: regs::ChLlr, | ||
| 39 | } | ||
| 40 | |||
| 41 | impl LinearItem { | ||
| 42 | /// Create a new read DMA transfer (peripheral to memory). | ||
| 43 | pub unsafe fn new_read<'d, W: Word>(request: Request, peri_addr: *mut W, buf: &'d mut [W]) -> Self { | ||
| 44 | Self::new_inner( | ||
| 45 | request, | ||
| 46 | Dir::PeripheralToMemory, | ||
| 47 | peri_addr as *const u32, | ||
| 48 | buf as *mut [W] as *mut W as *mut u32, | ||
| 49 | buf.len(), | ||
| 50 | true, | ||
| 51 | W::size(), | ||
| 52 | W::size(), | ||
| 53 | ) | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Create a new write DMA transfer (memory to peripheral). | ||
| 57 | pub unsafe fn new_write<'d, MW: Word, PW: Word>(request: Request, buf: &'d [MW], peri_addr: *mut PW) -> Self { | ||
| 58 | Self::new_inner( | ||
| 59 | request, | ||
| 60 | Dir::MemoryToPeripheral, | ||
| 61 | peri_addr as *const u32, | ||
| 62 | buf as *const [MW] as *const MW as *mut u32, | ||
| 63 | buf.len(), | ||
| 64 | true, | ||
| 65 | MW::size(), | ||
| 66 | PW::size(), | ||
| 67 | ) | ||
| 68 | } | ||
| 69 | |||
| 70 | unsafe fn new_inner( | ||
| 71 | request: Request, | ||
| 72 | dir: Dir, | ||
| 73 | peri_addr: *const u32, | ||
| 74 | mem_addr: *mut u32, | ||
| 75 | mem_len: usize, | ||
| 76 | incr_mem: bool, | ||
| 77 | data_size: WordSize, | ||
| 78 | dst_size: WordSize, | ||
| 79 | ) -> Self { | ||
| 80 | // BNDT is specified as bytes, not as number of transfers. | ||
| 81 | let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { | ||
| 82 | panic!("DMA transfers may not be larger than 65535 bytes."); | ||
| 83 | }; | ||
| 84 | |||
| 85 | let mut br1 = regs::ChBr1(0); | ||
| 86 | br1.set_bndt(bndt); | ||
| 87 | |||
| 88 | let mut tr1 = regs::ChTr1(0); | ||
| 89 | tr1.set_sdw(data_size.into()); | ||
| 90 | tr1.set_ddw(dst_size.into()); | ||
| 91 | tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); | ||
| 92 | tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); | ||
| 93 | |||
| 94 | let mut tr2 = regs::ChTr2(0); | ||
| 95 | tr2.set_dreq(match dir { | ||
| 96 | Dir::MemoryToPeripheral => Dreq::DESTINATION_PERIPHERAL, | ||
| 97 | Dir::PeripheralToMemory => Dreq::SOURCE_PERIPHERAL, | ||
| 98 | }); | ||
| 99 | tr2.set_reqsel(request); | ||
| 100 | |||
| 101 | let (sar, dar) = match dir { | ||
| 102 | Dir::MemoryToPeripheral => (mem_addr as _, peri_addr as _), | ||
| 103 | Dir::PeripheralToMemory => (peri_addr as _, mem_addr as _), | ||
| 104 | }; | ||
| 105 | |||
| 106 | let llr = regs::ChLlr(0); | ||
| 107 | |||
| 108 | Self { | ||
| 109 | tr1, | ||
| 110 | tr2, | ||
| 111 | br1, | ||
| 112 | sar, | ||
| 113 | dar, | ||
| 114 | llr, | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Link to the next linear item at the given address. | ||
| 119 | /// | ||
| 120 | /// Enables channel update bits. | ||
| 121 | fn link_to(&mut self, next: u16) { | ||
| 122 | let mut llr = regs::ChLlr(0); | ||
| 123 | |||
| 124 | llr.set_ut1(true); | ||
| 125 | llr.set_ut2(true); | ||
| 126 | llr.set_ub1(true); | ||
| 127 | llr.set_usa(true); | ||
| 128 | llr.set_uda(true); | ||
| 129 | llr.set_ull(true); | ||
| 130 | |||
| 131 | // Lower two bits are ignored: 32 bit aligned. | ||
| 132 | llr.set_la(next >> 2); | ||
| 133 | |||
| 134 | self.llr = llr; | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Unlink the next linear item. | ||
| 138 | /// | ||
| 139 | /// Disables channel update bits. | ||
| 140 | fn unlink(&mut self) { | ||
| 141 | self.llr = regs::ChLlr(0); | ||
| 142 | } | ||
| 143 | |||
| 144 | /// The item's transfer count in number of words. | ||
| 145 | fn transfer_count(&self) -> usize { | ||
| 146 | let word_size: WordSize = self.tr1.ddw().into(); | ||
| 147 | self.br1.bndt() as usize / word_size.bytes() | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /// A table of linked list items. | ||
| 152 | #[repr(C)] | ||
| 153 | pub struct Table<const ITEM_COUNT: usize> { | ||
| 154 | /// The items. | ||
| 155 | pub items: [LinearItem; ITEM_COUNT], | ||
| 156 | } | ||
| 157 | |||
| 158 | impl<const ITEM_COUNT: usize> Table<ITEM_COUNT> { | ||
| 159 | /// Create a new table. | ||
| 160 | pub fn new(items: [LinearItem; ITEM_COUNT]) -> Self { | ||
| 161 | assert!(!items.is_empty()); | ||
| 162 | |||
| 163 | Self { items } | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Create a ping-pong linked-list table. | ||
| 167 | /// | ||
| 168 | /// This uses two linked-list items, one for each half of the buffer. | ||
| 169 | pub unsafe fn new_ping_pong<W: Word>( | ||
| 170 | request: Request, | ||
| 171 | peri_addr: *mut W, | ||
| 172 | buffer: &mut [W], | ||
| 173 | direction: Dir, | ||
| 174 | ) -> Table<2> { | ||
| 175 | // Buffer halves should be the same length. | ||
| 176 | let half_len = buffer.len() / 2; | ||
| 177 | assert_eq!(half_len * 2, buffer.len()); | ||
| 178 | |||
| 179 | let items = match direction { | ||
| 180 | Dir::MemoryToPeripheral => [ | ||
| 181 | LinearItem::new_write(request, &mut buffer[..half_len], peri_addr), | ||
| 182 | LinearItem::new_write(request, &mut buffer[half_len..], peri_addr), | ||
| 183 | ], | ||
| 184 | Dir::PeripheralToMemory => [ | ||
| 185 | LinearItem::new_read(request, peri_addr, &mut buffer[..half_len]), | ||
| 186 | LinearItem::new_read(request, peri_addr, &mut buffer[half_len..]), | ||
| 187 | ], | ||
| 188 | }; | ||
| 189 | |||
| 190 | Table::new(items) | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Link the table as given by the run mode. | ||
| 194 | pub fn link(&mut self, run_mode: RunMode) { | ||
| 195 | if matches!(run_mode, RunMode::Once | RunMode::Circular) { | ||
| 196 | self.link_sequential(); | ||
| 197 | } | ||
| 198 | |||
| 199 | if matches!(run_mode, RunMode::Circular) { | ||
| 200 | self.link_repeat(); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | /// The number of linked list items. | ||
| 205 | pub fn len(&self) -> usize { | ||
| 206 | self.items.len() | ||
| 207 | } | ||
| 208 | |||
| 209 | /// The total transfer count of the table in number of words. | ||
| 210 | pub fn transfer_count(&self) -> usize { | ||
| 211 | let mut count = 0; | ||
| 212 | for item in self.items { | ||
| 213 | count += item.transfer_count() as usize | ||
| 214 | } | ||
| 215 | |||
| 216 | count | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Link items of given indices together: first -> second. | ||
| 220 | pub fn link_indices(&mut self, first: usize, second: usize) { | ||
| 221 | assert!(first < self.len()); | ||
| 222 | assert!(second < self.len()); | ||
| 223 | |||
| 224 | let second_item = self.offset_address(second); | ||
| 225 | self.items[first].link_to(second_item); | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Link items sequentially. | ||
| 229 | pub fn link_sequential(&mut self) { | ||
| 230 | if self.len() > 1 { | ||
| 231 | for index in 0..(self.items.len() - 1) { | ||
| 232 | let next = self.offset_address(index + 1); | ||
| 233 | self.items[index].link_to(next); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Link last to first item. | ||
| 239 | pub fn link_repeat(&mut self) { | ||
| 240 | let first_address = self.offset_address(0); | ||
| 241 | self.items.last_mut().unwrap().link_to(first_address); | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Unlink all items. | ||
| 245 | pub fn unlink(&mut self) { | ||
| 246 | for item in self.items.iter_mut() { | ||
| 247 | item.unlink(); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Linked list base address (upper 16 address bits). | ||
| 252 | pub fn base_address(&self) -> u16 { | ||
| 253 | ((&raw const self.items as u32) >> 16) as _ | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Linked list offset address (lower 16 address bits) at the selected index. | ||
| 257 | pub fn offset_address(&self, index: usize) -> u16 { | ||
| 258 | assert!(self.items.len() > index); | ||
| 259 | |||
| 260 | let address = &raw const self.items[index] as _; | ||
| 261 | |||
| 262 | // Ensure 32 bit address alignment. | ||
| 263 | assert_eq!(address & 0b11, 0); | ||
| 264 | |||
| 265 | address | ||
| 266 | } | ||
| 267 | } | ||
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs new file mode 100644 index 000000000..4a14c2a8e --- /dev/null +++ b/embassy-stm32/src/dma/gpdma/mod.rs | |||
| @@ -0,0 +1,699 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::pin::Pin; | ||
| 5 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 7 | |||
| 8 | use embassy_hal_internal::Peri; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | use linked_list::Table; | ||
| 11 | |||
| 12 | use super::word::{Word, WordSize}; | ||
| 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | ||
| 14 | use crate::interrupt::typelevel::Interrupt; | ||
| 15 | use crate::pac; | ||
| 16 | use crate::pac::gpdma::vals; | ||
| 17 | |||
| 18 | pub mod linked_list; | ||
| 19 | pub mod ringbuffered; | ||
| 20 | |||
| 21 | pub(crate) struct ChannelInfo { | ||
| 22 | pub(crate) dma: pac::gpdma::Gpdma, | ||
| 23 | pub(crate) num: usize, | ||
| 24 | #[cfg(feature = "_dual-core")] | ||
| 25 | pub(crate) irq: pac::Interrupt, | ||
| 26 | } | ||
| 27 | |||
| 28 | /// DMA request priority | ||
| 29 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 30 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 31 | pub enum Priority { | ||
| 32 | /// Low Priority | ||
| 33 | Low, | ||
| 34 | /// Medium Priority | ||
| 35 | Medium, | ||
| 36 | /// High Priority | ||
| 37 | High, | ||
| 38 | /// Very High Priority | ||
| 39 | VeryHigh, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl From<Priority> for pac::gpdma::vals::Prio { | ||
| 43 | fn from(value: Priority) -> Self { | ||
| 44 | match value { | ||
| 45 | Priority::Low => pac::gpdma::vals::Prio::LOW_WITH_LOWH_WEIGHT, | ||
| 46 | Priority::Medium => pac::gpdma::vals::Prio::LOW_WITH_MID_WEIGHT, | ||
| 47 | Priority::High => pac::gpdma::vals::Prio::LOW_WITH_HIGH_WEIGHT, | ||
| 48 | Priority::VeryHigh => pac::gpdma::vals::Prio::HIGH, | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | /// GPDMA transfer options. | ||
| 54 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 55 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 56 | #[non_exhaustive] | ||
| 57 | pub struct TransferOptions { | ||
| 58 | /// Request priority level. | ||
| 59 | pub priority: Priority, | ||
| 60 | /// Enable half transfer interrupt. | ||
| 61 | pub half_transfer_ir: bool, | ||
| 62 | /// Enable transfer complete interrupt. | ||
| 63 | pub complete_transfer_ir: bool, | ||
| 64 | } | ||
| 65 | |||
| 66 | impl Default for TransferOptions { | ||
| 67 | fn default() -> Self { | ||
| 68 | Self { | ||
| 69 | priority: Priority::VeryHigh, | ||
| 70 | half_transfer_ir: false, | ||
| 71 | complete_transfer_ir: true, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | impl From<WordSize> for vals::Dw { | ||
| 77 | fn from(raw: WordSize) -> Self { | ||
| 78 | match raw { | ||
| 79 | WordSize::OneByte => Self::BYTE, | ||
| 80 | WordSize::TwoBytes => Self::HALF_WORD, | ||
| 81 | WordSize::FourBytes => Self::WORD, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | impl From<vals::Dw> for WordSize { | ||
| 87 | fn from(raw: vals::Dw) -> Self { | ||
| 88 | match raw { | ||
| 89 | vals::Dw::BYTE => Self::OneByte, | ||
| 90 | vals::Dw::HALF_WORD => Self::TwoBytes, | ||
| 91 | vals::Dw::WORD => Self::FourBytes, | ||
| 92 | _ => panic!("Invalid word size"), | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | pub(crate) struct LLiState { | ||
| 98 | /// The number of linked-list items. | ||
| 99 | count: AtomicUsize, | ||
| 100 | /// The index of the current linked-list item. | ||
| 101 | index: AtomicUsize, | ||
| 102 | /// The total transfer count of all linked-list items in number of words. | ||
| 103 | transfer_count: AtomicUsize, | ||
| 104 | } | ||
| 105 | |||
| 106 | pub(crate) struct ChannelState { | ||
| 107 | waker: AtomicWaker, | ||
| 108 | complete_count: AtomicUsize, | ||
| 109 | lli_state: LLiState, | ||
| 110 | } | ||
| 111 | |||
| 112 | impl ChannelState { | ||
| 113 | pub(crate) const NEW: Self = Self { | ||
| 114 | waker: AtomicWaker::new(), | ||
| 115 | complete_count: AtomicUsize::new(0), | ||
| 116 | |||
| 117 | lli_state: LLiState { | ||
| 118 | count: AtomicUsize::new(0), | ||
| 119 | index: AtomicUsize::new(0), | ||
| 120 | transfer_count: AtomicUsize::new(0), | ||
| 121 | }, | ||
| 122 | }; | ||
| 123 | } | ||
| 124 | |||
| 125 | /// safety: must be called only once | ||
| 126 | pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: crate::interrupt::Priority) { | ||
| 127 | foreach_interrupt! { | ||
| 128 | ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { | ||
| 129 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority); | ||
| 130 | #[cfg(not(feature = "_dual-core"))] | ||
| 131 | crate::interrupt::typelevel::$irq::enable(); | ||
| 132 | }; | ||
| 133 | } | ||
| 134 | crate::_generated::init_gpdma(); | ||
| 135 | } | ||
| 136 | |||
| 137 | impl AnyChannel { | ||
| 138 | /// Safety: Must be called with a matching set of parameters for a valid dma channel | ||
| 139 | pub(crate) unsafe fn on_irq(&self) { | ||
| 140 | let info = self.info(); | ||
| 141 | #[cfg(feature = "_dual-core")] | ||
| 142 | { | ||
| 143 | use embassy_hal_internal::interrupt::InterruptExt as _; | ||
| 144 | info.irq.enable(); | ||
| 145 | } | ||
| 146 | |||
| 147 | let state = &STATE[self.id as usize]; | ||
| 148 | |||
| 149 | let ch = info.dma.ch(info.num); | ||
| 150 | let sr = ch.sr().read(); | ||
| 151 | |||
| 152 | if sr.dtef() { | ||
| 153 | panic!( | ||
| 154 | "DMA: data transfer error on DMA@{:08x} channel {}", | ||
| 155 | info.dma.as_ptr() as u32, | ||
| 156 | info.num | ||
| 157 | ); | ||
| 158 | } | ||
| 159 | if sr.usef() { | ||
| 160 | panic!( | ||
| 161 | "DMA: user settings error on DMA@{:08x} channel {}", | ||
| 162 | info.dma.as_ptr() as u32, | ||
| 163 | info.num | ||
| 164 | ); | ||
| 165 | } | ||
| 166 | if sr.ulef() { | ||
| 167 | panic!( | ||
| 168 | "DMA: link transfer error on DMA@{:08x} channel {}", | ||
| 169 | info.dma.as_ptr() as u32, | ||
| 170 | info.num | ||
| 171 | ); | ||
| 172 | } | ||
| 173 | |||
| 174 | if sr.htf() { | ||
| 175 | ch.fcr().write(|w| w.set_htf(true)); | ||
| 176 | } | ||
| 177 | |||
| 178 | if sr.tcf() { | ||
| 179 | ch.fcr().write(|w| w.set_tcf(true)); | ||
| 180 | |||
| 181 | let lli_count = state.lli_state.count.load(Ordering::Acquire); | ||
| 182 | let complete = if lli_count > 0 { | ||
| 183 | let next_lli_index = state.lli_state.index.load(Ordering::Acquire) + 1; | ||
| 184 | let complete = next_lli_index >= lli_count; | ||
| 185 | |||
| 186 | state | ||
| 187 | .lli_state | ||
| 188 | .index | ||
| 189 | .store(if complete { 0 } else { next_lli_index }, Ordering::Release); | ||
| 190 | |||
| 191 | complete | ||
| 192 | } else { | ||
| 193 | true | ||
| 194 | }; | ||
| 195 | |||
| 196 | if complete { | ||
| 197 | state.complete_count.fetch_add(1, Ordering::Release); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | if sr.suspf() { | ||
| 202 | // Disable all xxIEs to prevent the irq from firing again. | ||
| 203 | ch.cr().write(|_| {}); | ||
| 204 | } | ||
| 205 | state.waker.wake(); | ||
| 206 | } | ||
| 207 | |||
| 208 | fn get_remaining_transfers(&self) -> u16 { | ||
| 209 | let info = self.info(); | ||
| 210 | let ch = info.dma.ch(info.num); | ||
| 211 | let word_size: WordSize = ch.tr1().read().ddw().into(); | ||
| 212 | |||
| 213 | ch.br1().read().bndt() / word_size.bytes() as u16 | ||
| 214 | } | ||
| 215 | |||
| 216 | unsafe fn configure( | ||
| 217 | &self, | ||
| 218 | request: Request, | ||
| 219 | dir: Dir, | ||
| 220 | peri_addr: *const u32, | ||
| 221 | mem_addr: *mut u32, | ||
| 222 | mem_len: usize, | ||
| 223 | incr_mem: bool, | ||
| 224 | data_size: WordSize, | ||
| 225 | dst_size: WordSize, | ||
| 226 | options: TransferOptions, | ||
| 227 | ) { | ||
| 228 | // BNDT is specified as bytes, not as number of transfers. | ||
| 229 | let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { | ||
| 230 | panic!("DMA transfers may not be larger than 65535 bytes."); | ||
| 231 | }; | ||
| 232 | |||
| 233 | let info = self.info(); | ||
| 234 | let ch = info.dma.ch(info.num); | ||
| 235 | |||
| 236 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 237 | fence(Ordering::SeqCst); | ||
| 238 | |||
| 239 | ch.cr().write(|w| w.set_reset(true)); | ||
| 240 | ch.fcr().write(|w| { | ||
| 241 | // Clear all irqs | ||
| 242 | w.set_dtef(true); | ||
| 243 | w.set_htf(true); | ||
| 244 | w.set_suspf(true); | ||
| 245 | w.set_tcf(true); | ||
| 246 | w.set_tof(true); | ||
| 247 | w.set_ulef(true); | ||
| 248 | w.set_usef(true); | ||
| 249 | }); | ||
| 250 | ch.llr().write(|_| {}); // no linked list | ||
| 251 | ch.tr1().write(|w| { | ||
| 252 | w.set_sdw(data_size.into()); | ||
| 253 | w.set_ddw(dst_size.into()); | ||
| 254 | w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); | ||
| 255 | w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); | ||
| 256 | }); | ||
| 257 | ch.tr2().write(|w| { | ||
| 258 | w.set_dreq(match dir { | ||
| 259 | Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL, | ||
| 260 | Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL, | ||
| 261 | }); | ||
| 262 | w.set_reqsel(request); | ||
| 263 | }); | ||
| 264 | ch.tr3().write(|_| {}); // no address offsets. | ||
| 265 | ch.br1().write(|w| w.set_bndt(bndt)); | ||
| 266 | |||
| 267 | match dir { | ||
| 268 | Dir::MemoryToPeripheral => { | ||
| 269 | ch.sar().write_value(mem_addr as _); | ||
| 270 | ch.dar().write_value(peri_addr as _); | ||
| 271 | } | ||
| 272 | Dir::PeripheralToMemory => { | ||
| 273 | ch.sar().write_value(peri_addr as _); | ||
| 274 | ch.dar().write_value(mem_addr as _); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | ch.cr().write(|w| { | ||
| 279 | w.set_prio(options.priority.into()); | ||
| 280 | w.set_htie(options.half_transfer_ir); | ||
| 281 | w.set_tcie(options.complete_transfer_ir); | ||
| 282 | w.set_useie(true); | ||
| 283 | w.set_dteie(true); | ||
| 284 | w.set_suspie(true); | ||
| 285 | }); | ||
| 286 | |||
| 287 | let state = &STATE[self.id as usize]; | ||
| 288 | state.lli_state.count.store(0, Ordering::Relaxed); | ||
| 289 | state.lli_state.index.store(0, Ordering::Relaxed); | ||
| 290 | state.lli_state.transfer_count.store(0, Ordering::Relaxed) | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Configure a linked-list transfer. | ||
| 294 | unsafe fn configure_linked_list<const ITEM_COUNT: usize>( | ||
| 295 | &self, | ||
| 296 | table: &Table<ITEM_COUNT>, | ||
| 297 | options: TransferOptions, | ||
| 298 | ) { | ||
| 299 | let info = self.info(); | ||
| 300 | let ch = info.dma.ch(info.num); | ||
| 301 | |||
| 302 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 303 | fence(Ordering::SeqCst); | ||
| 304 | |||
| 305 | ch.cr().write(|w| w.set_reset(true)); | ||
| 306 | ch.fcr().write(|w| { | ||
| 307 | // Clear all irqs | ||
| 308 | w.set_dtef(true); | ||
| 309 | w.set_htf(true); | ||
| 310 | w.set_suspf(true); | ||
| 311 | w.set_tcf(true); | ||
| 312 | w.set_tof(true); | ||
| 313 | w.set_ulef(true); | ||
| 314 | w.set_usef(true); | ||
| 315 | }); | ||
| 316 | ch.lbar().write(|reg| reg.set_lba(table.base_address())); | ||
| 317 | |||
| 318 | // Empty LLI0. | ||
| 319 | ch.br1().write(|w| w.set_bndt(0)); | ||
| 320 | |||
| 321 | // Enable all linked-list field updates. | ||
| 322 | ch.llr().write(|w| { | ||
| 323 | w.set_ut1(true); | ||
| 324 | w.set_ut2(true); | ||
| 325 | w.set_ub1(true); | ||
| 326 | w.set_usa(true); | ||
| 327 | w.set_uda(true); | ||
| 328 | w.set_ull(true); | ||
| 329 | |||
| 330 | // Lower two bits are ignored: 32 bit aligned. | ||
| 331 | w.set_la(table.offset_address(0) >> 2); | ||
| 332 | }); | ||
| 333 | |||
| 334 | ch.tr3().write(|_| {}); // no address offsets. | ||
| 335 | |||
| 336 | ch.cr().write(|w| { | ||
| 337 | w.set_prio(options.priority.into()); | ||
| 338 | w.set_htie(options.half_transfer_ir); | ||
| 339 | w.set_tcie(options.complete_transfer_ir); | ||
| 340 | w.set_useie(true); | ||
| 341 | w.set_uleie(true); | ||
| 342 | w.set_dteie(true); | ||
| 343 | w.set_suspie(true); | ||
| 344 | }); | ||
| 345 | |||
| 346 | let state = &STATE[self.id as usize]; | ||
| 347 | state.lli_state.count.store(ITEM_COUNT, Ordering::Relaxed); | ||
| 348 | state.lli_state.index.store(0, Ordering::Relaxed); | ||
| 349 | state | ||
| 350 | .lli_state | ||
| 351 | .transfer_count | ||
| 352 | .store(table.transfer_count(), Ordering::Relaxed) | ||
| 353 | } | ||
| 354 | |||
| 355 | fn start(&self) { | ||
| 356 | let info = self.info(); | ||
| 357 | let ch = info.dma.ch(info.num); | ||
| 358 | |||
| 359 | ch.cr().modify(|w| w.set_en(true)); | ||
| 360 | } | ||
| 361 | |||
| 362 | fn request_pause(&self) { | ||
| 363 | let info = self.info(); | ||
| 364 | let ch = info.dma.ch(info.num); | ||
| 365 | |||
| 366 | ch.cr().modify(|w| w.set_susp(true)) | ||
| 367 | } | ||
| 368 | |||
| 369 | fn request_resume(&self) { | ||
| 370 | let info = self.info(); | ||
| 371 | let ch = info.dma.ch(info.num); | ||
| 372 | |||
| 373 | ch.cr().modify(|w| w.set_susp(false)); | ||
| 374 | } | ||
| 375 | |||
| 376 | fn request_reset(&self) { | ||
| 377 | let info = self.info(); | ||
| 378 | let ch = info.dma.ch(info.num); | ||
| 379 | |||
| 380 | self.request_pause(); | ||
| 381 | while self.is_running() {} | ||
| 382 | |||
| 383 | ch.cr().modify(|w| w.set_reset(true)); | ||
| 384 | } | ||
| 385 | |||
| 386 | fn is_running(&self) -> bool { | ||
| 387 | let info = self.info(); | ||
| 388 | let ch = info.dma.ch(info.num); | ||
| 389 | |||
| 390 | let sr = ch.sr().read(); | ||
| 391 | |||
| 392 | !sr.suspf() && !sr.idlef() | ||
| 393 | } | ||
| 394 | |||
| 395 | fn poll_stop(&self) -> Poll<()> { | ||
| 396 | use core::sync::atomic::compiler_fence; | ||
| 397 | compiler_fence(Ordering::SeqCst); | ||
| 398 | |||
| 399 | if !self.is_running() { | ||
| 400 | Poll::Ready(()) | ||
| 401 | } else { | ||
| 402 | Poll::Pending | ||
| 403 | } | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | /// Linked-list DMA transfer. | ||
| 408 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 409 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { | ||
| 410 | channel: Peri<'a, AnyChannel>, | ||
| 411 | } | ||
| 412 | |||
| 413 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | ||
| 414 | /// Create a new linked-list transfer. | ||
| 415 | pub unsafe fn new_linked_list<const N: usize>( | ||
| 416 | channel: Peri<'a, impl Channel>, | ||
| 417 | table: Table<ITEM_COUNT>, | ||
| 418 | options: TransferOptions, | ||
| 419 | ) -> Self { | ||
| 420 | Self::new_inner_linked_list(channel.into(), table, options) | ||
| 421 | } | ||
| 422 | |||
| 423 | unsafe fn new_inner_linked_list( | ||
| 424 | channel: Peri<'a, AnyChannel>, | ||
| 425 | table: Table<ITEM_COUNT>, | ||
| 426 | options: TransferOptions, | ||
| 427 | ) -> Self { | ||
| 428 | channel.configure_linked_list(&table, options); | ||
| 429 | channel.start(); | ||
| 430 | |||
| 431 | Self { channel } | ||
| 432 | } | ||
| 433 | |||
| 434 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 435 | /// | ||
| 436 | /// To resume the transfer, call [`request_resume`](Self::request_resume) again. | ||
| 437 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 438 | pub fn request_pause(&mut self) { | ||
| 439 | self.channel.request_pause() | ||
| 440 | } | ||
| 441 | |||
| 442 | /// Request the transfer to resume after having been paused. | ||
| 443 | pub fn request_resume(&mut self) { | ||
| 444 | self.channel.request_resume() | ||
| 445 | } | ||
| 446 | |||
| 447 | /// Request the DMA to reset. | ||
| 448 | /// | ||
| 449 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 450 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 451 | pub fn request_reset(&mut self) { | ||
| 452 | self.channel.request_reset() | ||
| 453 | } | ||
| 454 | |||
| 455 | /// Return whether this transfer is still running. | ||
| 456 | /// | ||
| 457 | /// If this returns `false`, it can be because either the transfer finished, or | ||
| 458 | /// it was requested to stop early with [`request_pause`](Self::request_pause). | ||
| 459 | pub fn is_running(&mut self) -> bool { | ||
| 460 | self.channel.is_running() | ||
| 461 | } | ||
| 462 | |||
| 463 | /// Gets the total remaining transfers for the channel | ||
| 464 | /// Note: this will be zero for transfers that completed without cancellation. | ||
| 465 | pub fn get_remaining_transfers(&self) -> u16 { | ||
| 466 | self.channel.get_remaining_transfers() | ||
| 467 | } | ||
| 468 | |||
| 469 | /// Blocking wait until the transfer finishes. | ||
| 470 | pub fn blocking_wait(mut self) { | ||
| 471 | while self.is_running() {} | ||
| 472 | |||
| 473 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 474 | fence(Ordering::SeqCst); | ||
| 475 | |||
| 476 | core::mem::forget(self); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | impl<'a, const ITEM_COUNT: usize> Drop for LinkedListTransfer<'a, ITEM_COUNT> { | ||
| 481 | fn drop(&mut self) { | ||
| 482 | self.request_reset(); | ||
| 483 | |||
| 484 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 485 | fence(Ordering::SeqCst); | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | impl<'a, const ITEM_COUNT: usize> Unpin for LinkedListTransfer<'a, ITEM_COUNT> {} | ||
| 490 | impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> { | ||
| 491 | type Output = (); | ||
| 492 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 493 | let state = &STATE[self.channel.id as usize]; | ||
| 494 | state.waker.register(cx.waker()); | ||
| 495 | |||
| 496 | if self.is_running() { | ||
| 497 | Poll::Pending | ||
| 498 | } else { | ||
| 499 | Poll::Ready(()) | ||
| 500 | } | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | /// DMA transfer. | ||
| 505 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 506 | pub struct Transfer<'a> { | ||
| 507 | channel: Peri<'a, AnyChannel>, | ||
| 508 | } | ||
| 509 | |||
| 510 | impl<'a> Transfer<'a> { | ||
| 511 | /// Create a new read DMA transfer (peripheral to memory). | ||
| 512 | pub unsafe fn new_read<W: Word>( | ||
| 513 | channel: Peri<'a, impl Channel>, | ||
| 514 | request: Request, | ||
| 515 | peri_addr: *mut W, | ||
| 516 | buf: &'a mut [W], | ||
| 517 | options: TransferOptions, | ||
| 518 | ) -> Self { | ||
| 519 | Self::new_read_raw(channel, request, peri_addr, buf, options) | ||
| 520 | } | ||
| 521 | |||
| 522 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. | ||
| 523 | pub unsafe fn new_read_raw<MW: Word, PW: Word>( | ||
| 524 | channel: Peri<'a, impl Channel>, | ||
| 525 | request: Request, | ||
| 526 | peri_addr: *mut PW, | ||
| 527 | buf: *mut [MW], | ||
| 528 | options: TransferOptions, | ||
| 529 | ) -> Self { | ||
| 530 | Self::new_inner( | ||
| 531 | channel.into(), | ||
| 532 | request, | ||
| 533 | Dir::PeripheralToMemory, | ||
| 534 | peri_addr as *const u32, | ||
| 535 | buf as *mut MW as *mut u32, | ||
| 536 | buf.len(), | ||
| 537 | true, | ||
| 538 | PW::size(), | ||
| 539 | MW::size(), | ||
| 540 | options, | ||
| 541 | ) | ||
| 542 | } | ||
| 543 | |||
| 544 | /// Create a new write DMA transfer (memory to peripheral). | ||
| 545 | pub unsafe fn new_write<MW: Word, PW: Word>( | ||
| 546 | channel: Peri<'a, impl Channel>, | ||
| 547 | request: Request, | ||
| 548 | buf: &'a [MW], | ||
| 549 | peri_addr: *mut PW, | ||
| 550 | options: TransferOptions, | ||
| 551 | ) -> Self { | ||
| 552 | Self::new_write_raw(channel, request, buf, peri_addr, options) | ||
| 553 | } | ||
| 554 | |||
| 555 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. | ||
| 556 | pub unsafe fn new_write_raw<MW: Word, PW: Word>( | ||
| 557 | channel: Peri<'a, impl Channel>, | ||
| 558 | request: Request, | ||
| 559 | buf: *const [MW], | ||
| 560 | peri_addr: *mut PW, | ||
| 561 | options: TransferOptions, | ||
| 562 | ) -> Self { | ||
| 563 | Self::new_inner( | ||
| 564 | channel.into(), | ||
| 565 | request, | ||
| 566 | Dir::MemoryToPeripheral, | ||
| 567 | peri_addr as *const u32, | ||
| 568 | buf as *const MW as *mut u32, | ||
| 569 | buf.len(), | ||
| 570 | true, | ||
| 571 | MW::size(), | ||
| 572 | PW::size(), | ||
| 573 | options, | ||
| 574 | ) | ||
| 575 | } | ||
| 576 | |||
| 577 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. | ||
| 578 | pub unsafe fn new_write_repeated<MW: Word, PW: Word>( | ||
| 579 | channel: Peri<'a, impl Channel>, | ||
| 580 | request: Request, | ||
| 581 | repeated: &'a MW, | ||
| 582 | count: usize, | ||
| 583 | peri_addr: *mut PW, | ||
| 584 | options: TransferOptions, | ||
| 585 | ) -> Self { | ||
| 586 | Self::new_inner( | ||
| 587 | channel.into(), | ||
| 588 | request, | ||
| 589 | Dir::MemoryToPeripheral, | ||
| 590 | peri_addr as *const u32, | ||
| 591 | repeated as *const MW as *mut u32, | ||
| 592 | count, | ||
| 593 | false, | ||
| 594 | MW::size(), | ||
| 595 | PW::size(), | ||
| 596 | options, | ||
| 597 | ) | ||
| 598 | } | ||
| 599 | |||
| 600 | unsafe fn new_inner( | ||
| 601 | channel: Peri<'a, AnyChannel>, | ||
| 602 | request: Request, | ||
| 603 | dir: Dir, | ||
| 604 | peri_addr: *const u32, | ||
| 605 | mem_addr: *mut u32, | ||
| 606 | mem_len: usize, | ||
| 607 | incr_mem: bool, | ||
| 608 | data_size: WordSize, | ||
| 609 | peripheral_size: WordSize, | ||
| 610 | options: TransferOptions, | ||
| 611 | ) -> Self { | ||
| 612 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | ||
| 613 | |||
| 614 | channel.configure( | ||
| 615 | request, | ||
| 616 | dir, | ||
| 617 | peri_addr, | ||
| 618 | mem_addr, | ||
| 619 | mem_len, | ||
| 620 | incr_mem, | ||
| 621 | data_size, | ||
| 622 | peripheral_size, | ||
| 623 | options, | ||
| 624 | ); | ||
| 625 | channel.start(); | ||
| 626 | |||
| 627 | Self { channel } | ||
| 628 | } | ||
| 629 | |||
| 630 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 631 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 632 | /// | ||
| 633 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 634 | pub fn request_pause(&mut self) { | ||
| 635 | self.channel.request_pause() | ||
| 636 | } | ||
| 637 | |||
| 638 | /// Request the transfer to resume after being suspended. | ||
| 639 | pub fn request_resume(&mut self) { | ||
| 640 | self.channel.request_resume() | ||
| 641 | } | ||
| 642 | |||
| 643 | /// Request the DMA to reset. | ||
| 644 | /// | ||
| 645 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 646 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 647 | pub fn request_reset(&mut self) { | ||
| 648 | self.channel.request_reset() | ||
| 649 | } | ||
| 650 | |||
| 651 | /// Return whether this transfer is still running. | ||
| 652 | /// | ||
| 653 | /// If this returns `false`, it can be because either the transfer finished, or | ||
| 654 | /// it was requested to stop early with [`request_pause`](Self::request_pause). | ||
| 655 | pub fn is_running(&mut self) -> bool { | ||
| 656 | self.channel.is_running() | ||
| 657 | } | ||
| 658 | |||
| 659 | /// Gets the total remaining transfers for the channel | ||
| 660 | /// Note: this will be zero for transfers that completed without cancellation. | ||
| 661 | pub fn get_remaining_transfers(&self) -> u16 { | ||
| 662 | self.channel.get_remaining_transfers() | ||
| 663 | } | ||
| 664 | |||
| 665 | /// Blocking wait until the transfer finishes. | ||
| 666 | pub fn blocking_wait(mut self) { | ||
| 667 | while self.is_running() {} | ||
| 668 | |||
| 669 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 670 | fence(Ordering::SeqCst); | ||
| 671 | |||
| 672 | core::mem::forget(self); | ||
| 673 | } | ||
| 674 | } | ||
| 675 | |||
| 676 | impl<'a> Drop for Transfer<'a> { | ||
| 677 | fn drop(&mut self) { | ||
| 678 | self.request_pause(); | ||
| 679 | while self.is_running() {} | ||
| 680 | |||
| 681 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 682 | fence(Ordering::SeqCst); | ||
| 683 | } | ||
| 684 | } | ||
| 685 | |||
| 686 | impl<'a> Unpin for Transfer<'a> {} | ||
| 687 | impl<'a> Future for Transfer<'a> { | ||
| 688 | type Output = (); | ||
| 689 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 690 | let state = &STATE[self.channel.id as usize]; | ||
| 691 | state.waker.register(cx.waker()); | ||
| 692 | |||
| 693 | if self.is_running() { | ||
| 694 | Poll::Pending | ||
| 695 | } else { | ||
| 696 | Poll::Ready(()) | ||
| 697 | } | ||
| 698 | } | ||
| 699 | } | ||
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs new file mode 100644 index 000000000..9ee52193b --- /dev/null +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs | |||
| @@ -0,0 +1,332 @@ | |||
| 1 | //! GPDMA ring buffer implementation. | ||
| 2 | //! | ||
| 3 | //! FIXME: Add request_pause functionality? | ||
| 4 | //! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically). | ||
| 5 | use core::future::poll_fn; | ||
| 6 | use core::sync::atomic::{fence, Ordering}; | ||
| 7 | use core::task::Waker; | ||
| 8 | |||
| 9 | use embassy_hal_internal::Peri; | ||
| 10 | |||
| 11 | use super::{AnyChannel, TransferOptions, STATE}; | ||
| 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; | ||
| 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | ||
| 14 | use crate::dma::word::Word; | ||
| 15 | use crate::dma::{Channel, Dir, Request}; | ||
| 16 | |||
| 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); | ||
| 18 | |||
| 19 | impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | ||
| 20 | fn get_remaining_transfers(&self) -> usize { | ||
| 21 | let state = &STATE[self.0.id as usize]; | ||
| 22 | let current_remaining = self.0.get_remaining_transfers() as usize; | ||
| 23 | |||
| 24 | let lli_count = state.lli_state.count.load(Ordering::Acquire); | ||
| 25 | |||
| 26 | if lli_count > 0 { | ||
| 27 | // In linked-list mode, the remaining transfers are the sum of the full lengths of LLIs that follow, | ||
| 28 | // and the remaining transfers for the current LLI. | ||
| 29 | let lli_index = state.lli_state.index.load(Ordering::Acquire); | ||
| 30 | let single_transfer_count = state.lli_state.transfer_count.load(Ordering::Acquire) / lli_count; | ||
| 31 | |||
| 32 | (lli_count - lli_index - 1) * single_transfer_count + current_remaining | ||
| 33 | } else { | ||
| 34 | // No linked-list mode. | ||
| 35 | current_remaining | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | fn reset_complete_count(&mut self) -> usize { | ||
| 40 | let state = &STATE[self.0.id as usize]; | ||
| 41 | |||
| 42 | state.complete_count.swap(0, Ordering::AcqRel) | ||
| 43 | } | ||
| 44 | |||
| 45 | fn set_waker(&mut self, waker: &Waker) { | ||
| 46 | STATE[self.0.id as usize].waker.register(waker); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Ringbuffer for receiving data using GPDMA linked-list mode. | ||
| 51 | pub struct ReadableRingBuffer<'a, W: Word> { | ||
| 52 | channel: Peri<'a, AnyChannel>, | ||
| 53 | ringbuf: ReadableDmaRingBuffer<'a, W>, | ||
| 54 | table: Table<2>, | ||
| 55 | options: TransferOptions, | ||
| 56 | } | ||
| 57 | |||
| 58 | impl<'a, W: Word> ReadableRingBuffer<'a, W> { | ||
| 59 | /// Create a new ring buffer. | ||
| 60 | /// | ||
| 61 | /// Transfer options are applied to the individual linked list items. | ||
| 62 | pub unsafe fn new( | ||
| 63 | channel: Peri<'a, impl Channel>, | ||
| 64 | request: Request, | ||
| 65 | peri_addr: *mut W, | ||
| 66 | buffer: &'a mut [W], | ||
| 67 | options: TransferOptions, | ||
| 68 | ) -> Self { | ||
| 69 | let channel: Peri<'a, AnyChannel> = channel.into(); | ||
| 70 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); | ||
| 71 | |||
| 72 | Self { | ||
| 73 | channel, | ||
| 74 | ringbuf: ReadableDmaRingBuffer::new(buffer), | ||
| 75 | table, | ||
| 76 | options, | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Start the ring buffer operation. | ||
| 81 | pub fn start(&mut self) { | ||
| 82 | // Apply the default configuration to the channel. | ||
| 83 | unsafe { self.channel.configure_linked_list(&self.table, self.options) }; | ||
| 84 | self.table.link(RunMode::Circular); | ||
| 85 | self.channel.start(); | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Clear all data in the ring buffer. | ||
| 89 | pub fn clear(&mut self) { | ||
| 90 | self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow())); | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Read elements from the ring buffer | ||
| 94 | /// Return a tuple of the length read and the length remaining in the buffer | ||
| 95 | /// If not all of the elements were read, then there will be some elements in the buffer remaining | ||
| 96 | /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read | ||
| 97 | /// Error is returned if the portion to be read was overwritten by the DMA controller. | ||
| 98 | pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), Error> { | ||
| 99 | self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Read an exact number of elements from the ringbuffer. | ||
| 103 | /// | ||
| 104 | /// Returns the remaining number of elements available for immediate reading. | ||
| 105 | /// Error is returned if the portion to be read was overwritten by the DMA controller. | ||
| 106 | /// | ||
| 107 | /// Async/Wake Behavior: | ||
| 108 | /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, | ||
| 109 | /// and when it wraps around. This means that when called with a buffer of length 'M', when this | ||
| 110 | /// ring buffer was created with a buffer of size 'N': | ||
| 111 | /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. | ||
| 112 | /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. | ||
| 113 | pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, Error> { | ||
| 114 | self.ringbuf | ||
| 115 | .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) | ||
| 116 | .await | ||
| 117 | } | ||
| 118 | |||
| 119 | /// The current length of the ringbuffer | ||
| 120 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 121 | Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) | ||
| 122 | } | ||
| 123 | |||
| 124 | /// The capacity of the ringbuffer | ||
| 125 | pub const fn capacity(&self) -> usize { | ||
| 126 | self.ringbuf.cap() | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Set a waker to be woken when at least one byte is received. | ||
| 130 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 131 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 135 | /// | ||
| 136 | /// To resume the transfer, call [`request_resume`](Self::request_resume) again. | ||
| 137 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 138 | pub fn request_pause(&mut self) { | ||
| 139 | self.channel.request_pause() | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Request the transfer to resume after having been paused. | ||
| 143 | pub fn request_resume(&mut self) { | ||
| 144 | self.channel.request_resume() | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Request the DMA to reset. | ||
| 148 | /// | ||
| 149 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 150 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 151 | pub fn request_reset(&mut self) { | ||
| 152 | self.channel.request_reset() | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Return whether this transfer is still running. | ||
| 156 | /// | ||
| 157 | /// If this returns `false`, it can be because either the transfer finished, or | ||
| 158 | /// it was requested to stop early with [`request_pause`](Self::request_pause). | ||
| 159 | pub fn is_running(&mut self) -> bool { | ||
| 160 | self.channel.is_running() | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Stop the DMA transfer and await until the buffer is full. | ||
| 164 | /// | ||
| 165 | /// This disables the DMA transfer's circular mode so that the transfer | ||
| 166 | /// stops when the buffer is full. | ||
| 167 | /// | ||
| 168 | /// This is designed to be used with streaming input data such as the | ||
| 169 | /// I2S/SAI or ADC. | ||
| 170 | pub async fn stop(&mut self) { | ||
| 171 | // wait until cr.susp reads as true | ||
| 172 | poll_fn(|cx| { | ||
| 173 | self.set_waker(cx.waker()); | ||
| 174 | self.channel.poll_stop() | ||
| 175 | }) | ||
| 176 | .await | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | ||
| 181 | fn drop(&mut self) { | ||
| 182 | self.request_pause(); | ||
| 183 | while self.is_running() {} | ||
| 184 | |||
| 185 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 186 | fence(Ordering::SeqCst); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Ringbuffer for writing data using GPDMA linked-list mode. | ||
| 191 | pub struct WritableRingBuffer<'a, W: Word> { | ||
| 192 | channel: Peri<'a, AnyChannel>, | ||
| 193 | ringbuf: WritableDmaRingBuffer<'a, W>, | ||
| 194 | table: Table<2>, | ||
| 195 | options: TransferOptions, | ||
| 196 | } | ||
| 197 | |||
| 198 | impl<'a, W: Word> WritableRingBuffer<'a, W> { | ||
| 199 | /// Create a new ring buffer. | ||
| 200 | /// | ||
| 201 | /// Transfer options are applied to the individual linked list items. | ||
| 202 | pub unsafe fn new( | ||
| 203 | channel: Peri<'a, impl Channel>, | ||
| 204 | request: Request, | ||
| 205 | peri_addr: *mut W, | ||
| 206 | buffer: &'a mut [W], | ||
| 207 | options: TransferOptions, | ||
| 208 | ) -> Self { | ||
| 209 | let channel: Peri<'a, AnyChannel> = channel.into(); | ||
| 210 | let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); | ||
| 211 | |||
| 212 | Self { | ||
| 213 | channel, | ||
| 214 | ringbuf: WritableDmaRingBuffer::new(buffer), | ||
| 215 | table, | ||
| 216 | options, | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Start the ring buffer operation. | ||
| 221 | pub fn start(&mut self) { | ||
| 222 | // Apply the default configuration to the channel. | ||
| 223 | unsafe { self.channel.configure_linked_list(&self.table, self.options) }; | ||
| 224 | self.table.link(RunMode::Circular); | ||
| 225 | |||
| 226 | self.channel.start(); | ||
| 227 | } | ||
| 228 | |||
| 229 | /// Clear all data in the ring buffer. | ||
| 230 | pub fn clear(&mut self) { | ||
| 231 | self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow())); | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Write elements directly to the raw buffer. | ||
| 235 | /// This can be used to fill the buffer before starting the DMA transfer. | ||
| 236 | pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 237 | self.ringbuf.write_immediate(buf) | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Write elements from the ring buffer | ||
| 241 | /// Return a tuple of the length written and the length remaining in the buffer | ||
| 242 | pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 243 | self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) | ||
| 244 | } | ||
| 245 | |||
| 246 | /// Write an exact number of elements to the ringbuffer. | ||
| 247 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> { | ||
| 248 | self.ringbuf | ||
| 249 | .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) | ||
| 250 | .await | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Wait for any ring buffer write error. | ||
| 254 | pub async fn wait_write_error(&mut self) -> Result<usize, Error> { | ||
| 255 | self.ringbuf | ||
| 256 | .wait_write_error(&mut DmaCtrlImpl(self.channel.reborrow())) | ||
| 257 | .await | ||
| 258 | } | ||
| 259 | |||
| 260 | /// The current length of the ringbuffer | ||
| 261 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 262 | Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) | ||
| 263 | } | ||
| 264 | |||
| 265 | /// The capacity of the ringbuffer | ||
| 266 | pub const fn capacity(&self) -> usize { | ||
| 267 | self.ringbuf.cap() | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Set a waker to be woken when at least one byte is received. | ||
| 271 | pub fn set_waker(&mut self, waker: &Waker) { | ||
| 272 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | ||
| 273 | } | ||
| 274 | |||
| 275 | /// Request the DMA to suspend. | ||
| 276 | /// | ||
| 277 | /// To resume the transfer, call [`request_resume`](Self::request_resume) again. | ||
| 278 | /// | ||
| 279 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 280 | pub fn request_pause(&mut self) { | ||
| 281 | self.channel.request_pause() | ||
| 282 | } | ||
| 283 | |||
| 284 | /// Request the DMA to resume transfers after being suspended. | ||
| 285 | pub fn request_resume(&mut self) { | ||
| 286 | self.channel.request_resume() | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Request the DMA to reset. | ||
| 290 | /// | ||
| 291 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 292 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 293 | pub fn request_reset(&mut self) { | ||
| 294 | self.channel.request_reset() | ||
| 295 | } | ||
| 296 | |||
| 297 | /// Return whether DMA is still running. | ||
| 298 | /// | ||
| 299 | /// If this returns `false`, it can be because either the transfer finished, or | ||
| 300 | /// it was requested to stop early with [`request_stop`](Self::request_stop). | ||
| 301 | pub fn is_running(&mut self) -> bool { | ||
| 302 | self.channel.is_running() | ||
| 303 | } | ||
| 304 | |||
| 305 | /// Stop the DMA transfer and await until the buffer is full. | ||
| 306 | /// | ||
| 307 | /// This disables the DMA transfer's circular mode so that the transfer | ||
| 308 | /// stops when the buffer is full. | ||
| 309 | /// | ||
| 310 | /// This is designed to be used with streaming input data such as the | ||
| 311 | /// I2S/SAI or ADC. | ||
| 312 | /// | ||
| 313 | /// When using the UART, you probably want `request_stop()`. | ||
| 314 | pub async fn stop(&mut self) { | ||
| 315 | // wait until cr.susp reads as true | ||
| 316 | poll_fn(|cx| { | ||
| 317 | self.set_waker(cx.waker()); | ||
| 318 | self.channel.poll_stop() | ||
| 319 | }) | ||
| 320 | .await | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> { | ||
| 325 | fn drop(&mut self) { | ||
| 326 | self.request_pause(); | ||
| 327 | while self.is_running() {} | ||
| 328 | |||
| 329 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." | ||
| 330 | fence(Ordering::SeqCst); | ||
| 331 | } | ||
| 332 | } | ||
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index d3b070a6d..5989bfd7c 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -9,6 +9,8 @@ pub use dma_bdma::*; | |||
| 9 | #[cfg(gpdma)] | 9 | #[cfg(gpdma)] |
| 10 | pub(crate) mod gpdma; | 10 | pub(crate) mod gpdma; |
| 11 | #[cfg(gpdma)] | 11 | #[cfg(gpdma)] |
| 12 | pub use gpdma::ringbuffered::*; | ||
| 13 | #[cfg(gpdma)] | ||
| 12 | pub use gpdma::*; | 14 | pub use gpdma::*; |
| 13 | 15 | ||
| 14 | #[cfg(dmamux)] | 16 | #[cfg(dmamux)] |
| @@ -26,10 +28,13 @@ use embassy_hal_internal::{impl_peripheral, PeripheralType}; | |||
| 26 | 28 | ||
| 27 | use crate::interrupt; | 29 | use crate::interrupt; |
| 28 | 30 | ||
| 31 | /// The direction of a DMA transfer. | ||
| 29 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 32 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| 30 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 33 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 31 | enum Dir { | 34 | pub enum Dir { |
| 35 | /// Transfer from memory to a peripheral. | ||
| 32 | MemoryToPeripheral, | 36 | MemoryToPeripheral, |
| 37 | /// Transfer from a peripheral to memory. | ||
| 33 | PeripheralToMemory, | 38 | PeripheralToMemory, |
| 34 | } | 39 | } |
| 35 | 40 | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 44ea497fe..659ffa9e5 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | #![cfg_attr(gpdma, allow(unused))] | ||
| 2 | |||
| 3 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 4 | use core::task::{Poll, Waker}; | 2 | use core::task::{Poll, Waker}; |
| 5 | 3 | ||
| @@ -285,17 +283,20 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { | |||
| 285 | } | 283 | } |
| 286 | 284 | ||
| 287 | /// Write an exact number of elements to the ringbuffer. | 285 | /// Write an exact number of elements to the ringbuffer. |
| 286 | /// | ||
| 287 | /// Returns the remaining write capacity in the buffer. | ||
| 288 | #[allow(dead_code)] | ||
| 288 | pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> { | 289 | pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> { |
| 289 | let mut written_data = 0; | 290 | let mut written_len = 0; |
| 290 | let buffer_len = buffer.len(); | 291 | let buffer_len = buffer.len(); |
| 291 | 292 | ||
| 292 | poll_fn(|cx| { | 293 | poll_fn(|cx| { |
| 293 | dma.set_waker(cx.waker()); | 294 | dma.set_waker(cx.waker()); |
| 294 | 295 | ||
| 295 | match self.write(dma, &buffer[written_data..buffer_len]) { | 296 | match self.write(dma, &buffer[written_len..buffer_len]) { |
| 296 | Ok((len, remaining)) => { | 297 | Ok((len, remaining)) => { |
| 297 | written_data += len; | 298 | written_len += len; |
| 298 | if written_data == buffer_len { | 299 | if written_len == buffer_len { |
| 299 | Poll::Ready(Ok(remaining)) | 300 | Poll::Ready(Ok(remaining)) |
| 300 | } else { | 301 | } else { |
| 301 | Poll::Pending | 302 | Poll::Pending |
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index e97ccd9d0..deda956af 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -78,7 +78,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 78 | rcc::enable_and_reset::<T>(); | 78 | rcc::enable_and_reset::<T>(); |
| 79 | 79 | ||
| 80 | // Set Tearing Enable pin according to CubeMx example | 80 | // Set Tearing Enable pin according to CubeMx example |
| 81 | te.set_as_af(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low)); | 81 | set_as_af!(te, AfType::output(OutputType::PushPull, Speed::Low)); |
| 82 | /* | 82 | /* |
| 83 | T::regs().wcr().modify(|w| { | 83 | T::regs().wcr().modify(|w| { |
| 84 | w.set_dsien(true); | 84 | w.set_dsien(true); |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 97d7b4347..10b3a0517 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -209,19 +209,19 @@ impl SealedInstance for crate::peripherals::ETH { | |||
| 209 | } | 209 | } |
| 210 | impl Instance for crate::peripherals::ETH {} | 210 | impl Instance for crate::peripherals::ETH {} |
| 211 | 211 | ||
| 212 | pin_trait!(RXClkPin, Instance); | 212 | pin_trait!(RXClkPin, Instance, @A); |
| 213 | pin_trait!(TXClkPin, Instance); | 213 | pin_trait!(TXClkPin, Instance, @A); |
| 214 | pin_trait!(RefClkPin, Instance); | 214 | pin_trait!(RefClkPin, Instance, @A); |
| 215 | pin_trait!(MDIOPin, Instance); | 215 | pin_trait!(MDIOPin, Instance, @A); |
| 216 | pin_trait!(MDCPin, Instance); | 216 | pin_trait!(MDCPin, Instance, @A); |
| 217 | pin_trait!(RXDVPin, Instance); | 217 | pin_trait!(RXDVPin, Instance, @A); |
| 218 | pin_trait!(CRSPin, Instance); | 218 | pin_trait!(CRSPin, Instance, @A); |
| 219 | pin_trait!(RXD0Pin, Instance); | 219 | pin_trait!(RXD0Pin, Instance, @A); |
| 220 | pin_trait!(RXD1Pin, Instance); | 220 | pin_trait!(RXD1Pin, Instance, @A); |
| 221 | pin_trait!(RXD2Pin, Instance); | 221 | pin_trait!(RXD2Pin, Instance, @A); |
| 222 | pin_trait!(RXD3Pin, Instance); | 222 | pin_trait!(RXD3Pin, Instance, @A); |
| 223 | pin_trait!(TXD0Pin, Instance); | 223 | pin_trait!(TXD0Pin, Instance, @A); |
| 224 | pin_trait!(TXD1Pin, Instance); | 224 | pin_trait!(TXD1Pin, Instance, @A); |
| 225 | pin_trait!(TXD2Pin, Instance); | 225 | pin_trait!(TXD2Pin, Instance, @A); |
| 226 | pin_trait!(TXD3Pin, Instance); | 226 | pin_trait!(TXD3Pin, Instance, @A); |
| 227 | pin_trait!(TXEnPin, Instance); | 227 | pin_trait!(TXEnPin, Instance, @A); |
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index b9746231f..5be1c9739 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -69,7 +69,7 @@ macro_rules! config_in_pins { | |||
| 69 | critical_section::with(|_| { | 69 | critical_section::with(|_| { |
| 70 | $( | 70 | $( |
| 71 | // TODO properly create a set_as_input function | 71 | // TODO properly create a set_as_input function |
| 72 | $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); | 72 | set_as_af!($pin, AfType::input(Pull::None)); |
| 73 | )* | 73 | )* |
| 74 | }) | 74 | }) |
| 75 | } | 75 | } |
| @@ -80,7 +80,7 @@ macro_rules! config_af_pins { | |||
| 80 | ($($pin:ident),*) => { | 80 | ($($pin:ident),*) => { |
| 81 | critical_section::with(|_| { | 81 | critical_section::with(|_| { |
| 82 | $( | 82 | $( |
| 83 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 83 | set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 84 | )* | 84 | )* |
| 85 | }) | 85 | }) |
| 86 | }; | 86 | }; |
| @@ -91,7 +91,7 @@ macro_rules! config_pins { | |||
| 91 | ($($pin:ident),*) => { | 91 | ($($pin:ident),*) => { |
| 92 | critical_section::with(|_| { | 92 | critical_section::with(|_| { |
| 93 | $( | 93 | $( |
| 94 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 94 | set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 95 | )* | 95 | )* |
| 96 | }) | 96 | }) |
| 97 | }; | 97 | }; |
| @@ -99,19 +99,19 @@ macro_rules! config_pins { | |||
| 99 | 99 | ||
| 100 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 100 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 101 | /// safety: the returned instance is not leak-safe | 101 | /// safety: the returned instance is not leak-safe |
| 102 | pub fn new<const TX: usize, const RX: usize>( | 102 | pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 103 | queue: &'d mut PacketQueue<TX, RX>, | 103 | queue: &'d mut PacketQueue<TX, RX>, |
| 104 | peri: Peri<'d, T>, | 104 | peri: Peri<'d, T>, |
| 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 106 | ref_clk: Peri<'d, impl RefClkPin<T>>, | 106 | ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, |
| 107 | mdio: Peri<'d, impl MDIOPin<T>>, | 107 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, |
| 108 | mdc: Peri<'d, impl MDCPin<T>>, | 108 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, |
| 109 | crs: Peri<'d, impl CRSPin<T>>, | 109 | crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, |
| 110 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 110 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 111 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 111 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| 112 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | 112 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, |
| 113 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | 113 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, |
| 114 | tx_en: Peri<'d, impl TXEnPin<T>>, | 114 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 115 | phy: P, | 115 | phy: P, |
| 116 | mac_addr: [u8; 6], | 116 | mac_addr: [u8; 6], |
| 117 | ) -> Self { | 117 | ) -> Self { |
| @@ -289,24 +289,24 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | /// Create a new MII ethernet driver using 14 pins. | 291 | /// Create a new MII ethernet driver using 14 pins. |
| 292 | pub fn new_mii<const TX: usize, const RX: usize>( | 292 | pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( |
| 293 | queue: &'d mut PacketQueue<TX, RX>, | 293 | queue: &'d mut PacketQueue<TX, RX>, |
| 294 | peri: Peri<'d, T>, | 294 | peri: Peri<'d, T>, |
| 295 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 295 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 296 | rx_clk: Peri<'d, impl RXClkPin<T>>, | 296 | rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, |
| 297 | tx_clk: Peri<'d, impl TXClkPin<T>>, | 297 | tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, |
| 298 | mdio: Peri<'d, impl MDIOPin<T>>, | 298 | mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>, |
| 299 | mdc: Peri<'d, impl MDCPin<T>>, | 299 | mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>, |
| 300 | rxdv: Peri<'d, impl RXDVPin<T>>, | 300 | rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, |
| 301 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | 301 | rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, |
| 302 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | 302 | rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, |
| 303 | rx_d2: Peri<'d, impl RXD2Pin<T>>, | 303 | rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>, |
| 304 | rx_d3: Peri<'d, impl RXD3Pin<T>>, | 304 | rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>, |
| 305 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | 305 | tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, |
| 306 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | 306 | tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, |
| 307 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | 307 | tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, |
| 308 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | 308 | tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, |
| 309 | tx_en: Peri<'d, impl TXEnPin<T>>, | 309 | tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, |
| 310 | phy: P, | 310 | phy: P, |
| 311 | mac_addr: [u8; 6], | 311 | mac_addr: [u8; 6], |
| 312 | ) -> Self { | 312 | ) -> Self { |
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 034c5dd88..cf7a9901b 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -57,7 +57,7 @@ macro_rules! config_pins { | |||
| 57 | critical_section::with(|_| { | 57 | critical_section::with(|_| { |
| 58 | $( | 58 | $( |
| 59 | // TODO: shouldn't some pins be configured as inputs? | 59 | // TODO: shouldn't some pins be configured as inputs? |
| 60 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 60 | set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 61 | )* | 61 | )* |
| 62 | }) | 62 | }) |
| 63 | }; | 63 | }; |
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 71ca775cb..ff18a8bee 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs | |||
| @@ -75,7 +75,7 @@ where | |||
| 75 | macro_rules! config_pins { | 75 | macro_rules! config_pins { |
| 76 | ($($pin:ident),*) => { | 76 | ($($pin:ident),*) => { |
| 77 | $( | 77 | $( |
| 78 | $pin.set_as_af($pin.af_num(), AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); | 78 | set_as_af!($pin, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); |
| 79 | )* | 79 | )* |
| 80 | }; | 80 | }; |
| 81 | } | 81 | } |
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index bb37c4194..5a8d23183 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -150,9 +150,13 @@ impl<'d> Flex<'d> { | |||
| 150 | /// This puts the pin into the AF mode, with the requested number and AF type. This is | 150 | /// This puts the pin into the AF mode, with the requested number and AF type. This is |
| 151 | /// completely unchecked, it can attach the pin to literally any peripheral, so use with care. | 151 | /// completely unchecked, it can attach the pin to literally any peripheral, so use with care. |
| 152 | #[inline] | 152 | #[inline] |
| 153 | pub fn set_as_af_unchecked(&mut self, af_num: u8, af_type: AfType) { | 153 | pub fn set_as_af_unchecked(&mut self, #[cfg(not(afio))] af_num: u8, af_type: AfType) { |
| 154 | critical_section::with(|_| { | 154 | critical_section::with(|_| { |
| 155 | self.pin.set_as_af(af_num, af_type); | 155 | self.pin.set_as_af( |
| 156 | #[cfg(not(afio))] | ||
| 157 | af_num, | ||
| 158 | af_type, | ||
| 159 | ); | ||
| 156 | }); | 160 | }); |
| 157 | } | 161 | } |
| 158 | 162 | ||
| @@ -588,7 +592,7 @@ impl AfType { | |||
| 588 | 592 | ||
| 589 | #[inline(never)] | 593 | #[inline(never)] |
| 590 | #[cfg(gpio_v1)] | 594 | #[cfg(gpio_v1)] |
| 591 | fn set_as_af(pin_port: u8, _af_num: u8, af_type: AfType) { | 595 | fn set_as_af(pin_port: u8, af_type: AfType) { |
| 592 | let pin = unsafe { AnyPin::steal(pin_port) }; | 596 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 593 | let r = pin.block(); | 597 | let r = pin.block(); |
| 594 | let n = pin._pin() as usize; | 598 | let n = pin._pin() as usize; |
| @@ -710,6 +714,18 @@ fn get_pull(pin_port: u8) -> Pull { | |||
| 710 | }; | 714 | }; |
| 711 | } | 715 | } |
| 712 | 716 | ||
| 717 | #[cfg(afio)] | ||
| 718 | /// Holds the AFIO remap value for a peripheral's pin | ||
| 719 | pub struct AfioRemap<const V: u8>; | ||
| 720 | |||
| 721 | #[cfg(afio)] | ||
| 722 | /// Holds the AFIO remap value for a peripheral's pin | ||
| 723 | pub struct AfioRemapBool<const V: bool>; | ||
| 724 | |||
| 725 | #[cfg(afio)] | ||
| 726 | /// Placeholder for a peripheral's pin which cannot be remapped via AFIO. | ||
| 727 | pub struct AfioRemapNotApplicable; | ||
| 728 | |||
| 713 | pub(crate) trait SealedPin { | 729 | pub(crate) trait SealedPin { |
| 714 | fn pin_port(&self) -> u8; | 730 | fn pin_port(&self) -> u8; |
| 715 | 731 | ||
| @@ -743,8 +759,13 @@ pub(crate) trait SealedPin { | |||
| 743 | } | 759 | } |
| 744 | 760 | ||
| 745 | #[inline] | 761 | #[inline] |
| 746 | fn set_as_af(&self, af_num: u8, af_type: AfType) { | 762 | fn set_as_af(&self, #[cfg(not(afio))] af_num: u8, af_type: AfType) { |
| 747 | set_as_af(self.pin_port(), af_num, af_type) | 763 | set_as_af( |
| 764 | self.pin_port(), | ||
| 765 | #[cfg(not(afio))] | ||
| 766 | af_num, | ||
| 767 | af_type, | ||
| 768 | ) | ||
| 748 | } | 769 | } |
| 749 | 770 | ||
| 750 | #[inline] | 771 | #[inline] |
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 1d0594125..6fece5eb2 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs | |||
| @@ -79,10 +79,7 @@ macro_rules! advanced_channel_impl { | |||
| 79 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | 79 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| 80 | critical_section::with(|_| { | 80 | critical_section::with(|_| { |
| 81 | pin.set_low(); | 81 | pin.set_low(); |
| 82 | pin.set_as_af( | 82 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 83 | pin.af_num(), | ||
| 84 | AfType::output(OutputType::PushPull, Speed::VeryHigh), | ||
| 85 | ); | ||
| 86 | }); | 83 | }); |
| 87 | PwmPin { | 84 | PwmPin { |
| 88 | _pin: pin.into(), | 85 | _pin: pin.into(), |
| @@ -96,10 +93,7 @@ macro_rules! advanced_channel_impl { | |||
| 96 | pub fn $new_chx(pin: Peri<'d, impl $complementary_pin_trait<T>>) -> Self { | 93 | pub fn $new_chx(pin: Peri<'d, impl $complementary_pin_trait<T>>) -> Self { |
| 97 | critical_section::with(|_| { | 94 | critical_section::with(|_| { |
| 98 | pin.set_low(); | 95 | pin.set_low(); |
| 99 | pin.set_as_af( | 96 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 100 | pin.af_num(), | ||
| 101 | AfType::output(OutputType::PushPull, Speed::VeryHigh), | ||
| 102 | ); | ||
| 103 | }); | 97 | }); |
| 104 | ComplementaryPwmPin { | 98 | ComplementaryPwmPin { |
| 105 | _pin: pin.into(), | 99 | _pin: pin.into(), |
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 62bc0e979..95d9e5099 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs | |||
| @@ -86,6 +86,8 @@ impl Default for Config { | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | /// HSPI transfer configuration. | 88 | /// HSPI transfer configuration. |
| 89 | #[derive(Clone, Copy)] | ||
| 90 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 89 | pub struct TransferConfig { | 91 | pub struct TransferConfig { |
| 90 | /// Instruction width (IMODE) | 92 | /// Instruction width (IMODE) |
| 91 | pub iwidth: HspiWidth, | 93 | pub iwidth: HspiWidth, |
| @@ -116,7 +118,7 @@ pub struct TransferConfig { | |||
| 116 | 118 | ||
| 117 | /// Data width (DMODE) | 119 | /// Data width (DMODE) |
| 118 | pub dwidth: HspiWidth, | 120 | pub dwidth: HspiWidth, |
| 119 | /// Data buffer | 121 | /// Data Double Transfer rate enable |
| 120 | pub ddtr: bool, | 122 | pub ddtr: bool, |
| 121 | 123 | ||
| 122 | /// Number of dummy cycles (DCYC) | 124 | /// Number of dummy cycles (DCYC) |
| @@ -395,11 +397,6 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 395 | // Configure alternate bytes | 397 | // Configure alternate bytes |
| 396 | if let Some(ab) = command.alternate_bytes { | 398 | if let Some(ab) = command.alternate_bytes { |
| 397 | T::REGS.abr().write(|v| v.set_alternate(ab)); | 399 | T::REGS.abr().write(|v| v.set_alternate(ab)); |
| 398 | T::REGS.ccr().modify(|w| { | ||
| 399 | w.set_abmode(command.abwidth.into()); | ||
| 400 | w.set_abdtr(command.abdtr); | ||
| 401 | w.set_absize(command.absize.into()); | ||
| 402 | }) | ||
| 403 | } | 400 | } |
| 404 | 401 | ||
| 405 | // Configure dummy cycles | 402 | // Configure dummy cycles |
| @@ -411,14 +408,14 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 411 | if let Some(data_length) = data_len { | 408 | if let Some(data_length) = data_len { |
| 412 | T::REGS.dlr().write(|v| { | 409 | T::REGS.dlr().write(|v| { |
| 413 | v.set_dl((data_length - 1) as u32); | 410 | v.set_dl((data_length - 1) as u32); |
| 414 | }) | 411 | }); |
| 415 | } else { | 412 | } else { |
| 416 | T::REGS.dlr().write(|v| { | 413 | T::REGS.dlr().write(|v| { |
| 417 | v.set_dl((0) as u32); | 414 | v.set_dl((0) as u32); |
| 418 | }) | 415 | }); |
| 419 | } | 416 | } |
| 420 | 417 | ||
| 421 | // Configure instruction/address/data modes | 418 | // Configure instruction/address/alternate bytes/data modes |
| 422 | T::REGS.ccr().modify(|w| { | 419 | T::REGS.ccr().modify(|w| { |
| 423 | w.set_imode(command.iwidth.into()); | 420 | w.set_imode(command.iwidth.into()); |
| 424 | w.set_idtr(command.idtr); | 421 | w.set_idtr(command.idtr); |
| @@ -428,6 +425,10 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | |||
| 428 | w.set_addtr(command.addtr); | 425 | w.set_addtr(command.addtr); |
| 429 | w.set_adsize(command.adsize.into()); | 426 | w.set_adsize(command.adsize.into()); |
| 430 | 427 | ||
| 428 | w.set_abmode(command.abwidth.into()); | ||
| 429 | w.set_abdtr(command.abdtr); | ||
| 430 | w.set_absize(command.absize.into()); | ||
| 431 | |||
| 431 | w.set_dmode(command.dwidth.into()); | 432 | w.set_dmode(command.dwidth.into()); |
| 432 | w.set_ddtr(command.ddtr); | 433 | w.set_ddtr(command.ddtr); |
| 433 | }); | 434 | }); |
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 5fb49f943..249bac41c 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -149,10 +149,10 @@ pub struct I2c<'d, M: Mode, IM: MasterMode> { | |||
| 149 | 149 | ||
| 150 | impl<'d> I2c<'d, Async, Master> { | 150 | impl<'d> I2c<'d, Async, Master> { |
| 151 | /// Create a new I2C driver. | 151 | /// Create a new I2C driver. |
| 152 | pub fn new<T: Instance>( | 152 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 153 | peri: Peri<'d, T>, | 153 | peri: Peri<'d, T>, |
| 154 | scl: Peri<'d, impl SclPin<T>>, | 154 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, |
| 155 | sda: Peri<'d, impl SdaPin<T>>, | 155 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, |
| 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> |
| 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> |
| 158 | + 'd, | 158 | + 'd, |
| @@ -173,10 +173,10 @@ impl<'d> I2c<'d, Async, Master> { | |||
| 173 | 173 | ||
| 174 | impl<'d> I2c<'d, Blocking, Master> { | 174 | impl<'d> I2c<'d, Blocking, Master> { |
| 175 | /// Create a new blocking I2C driver. | 175 | /// Create a new blocking I2C driver. |
| 176 | pub fn new_blocking<T: Instance>( | 176 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 177 | peri: Peri<'d, T>, | 177 | peri: Peri<'d, T>, |
| 178 | scl: Peri<'d, impl SclPin<T>>, | 178 | scl: Peri<'d, if_afio!(impl SclPin<T, A>)>, |
| 179 | sda: Peri<'d, impl SdaPin<T>>, | 179 | sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>, |
| 180 | config: Config, | 180 | config: Config, |
| 181 | ) -> Self { | 181 | ) -> Self { |
| 182 | Self::new_inner( | 182 | Self::new_inner( |
| @@ -296,8 +296,8 @@ peri_trait!( | |||
| 296 | irqs: [EventInterrupt, ErrorInterrupt], | 296 | irqs: [EventInterrupt, ErrorInterrupt], |
| 297 | ); | 297 | ); |
| 298 | 298 | ||
| 299 | pin_trait!(SclPin, Instance); | 299 | pin_trait!(SclPin, Instance, @A); |
| 300 | pin_trait!(SdaPin, Instance); | 300 | pin_trait!(SdaPin, Instance, @A); |
| 301 | dma_trait!(RxDma, Instance); | 301 | dma_trait!(RxDma, Instance); |
| 302 | dma_trait!(TxDma, Instance); | 302 | dma_trait!(TxDma, Instance); |
| 303 | 303 | ||
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 3b09f1b34..0bfc795ac 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -454,7 +454,8 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 454 | // (START has been ACKed or last byte when | 454 | // (START has been ACKed or last byte when |
| 455 | // through) | 455 | // through) |
| 456 | if let Err(err) = self.wait_txis(timeout) { | 456 | if let Err(err) = self.wait_txis(timeout) { |
| 457 | if send_stop { | 457 | if send_stop && err != Error::Nack { |
| 458 | // STOP is sent automatically if a NACK was received | ||
| 458 | self.master_stop(); | 459 | self.master_stop(); |
| 459 | } | 460 | } |
| 460 | return Err(err); | 461 | return Err(err); |
| @@ -548,7 +549,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 548 | (idx != last_slice_index) || (slice_len > 255), | 549 | (idx != last_slice_index) || (slice_len > 255), |
| 549 | timeout, | 550 | timeout, |
| 550 | ) { | 551 | ) { |
| 551 | self.master_stop(); | 552 | if err != Error::Nack { |
| 553 | self.master_stop(); | ||
| 554 | } | ||
| 552 | return Err(err); | 555 | return Err(err); |
| 553 | } | 556 | } |
| 554 | } | 557 | } |
| @@ -561,7 +564,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 561 | (number != last_chunk_idx) || (idx != last_slice_index), | 564 | (number != last_chunk_idx) || (idx != last_slice_index), |
| 562 | timeout, | 565 | timeout, |
| 563 | ) { | 566 | ) { |
| 564 | self.master_stop(); | 567 | if err != Error::Nack { |
| 568 | self.master_stop(); | ||
| 569 | } | ||
| 565 | return Err(err); | 570 | return Err(err); |
| 566 | } | 571 | } |
| 567 | } | 572 | } |
| @@ -571,7 +576,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 571 | // (START has been ACKed or last byte when | 576 | // (START has been ACKed or last byte when |
| 572 | // through) | 577 | // through) |
| 573 | if let Err(err) = self.wait_txis(timeout) { | 578 | if let Err(err) = self.wait_txis(timeout) { |
| 574 | self.master_stop(); | 579 | if err != Error::Nack { |
| 580 | self.master_stop(); | ||
| 581 | } | ||
| 575 | return Err(err); | 582 | return Err(err); |
| 576 | } | 583 | } |
| 577 | 584 | ||
| @@ -1276,7 +1283,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1276 | } else if isr.stopf() { | 1283 | } else if isr.stopf() { |
| 1277 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); | 1284 | self.info.regs.icr().write(|reg| reg.set_stopcf(true)); |
| 1278 | if remaining_len > 0 { | 1285 | if remaining_len > 0 { |
| 1279 | dma_transfer.request_stop(); | 1286 | dma_transfer.request_pause(); |
| 1280 | Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize))) | 1287 | Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize))) |
| 1281 | } else { | 1288 | } else { |
| 1282 | Poll::Ready(Ok(SendStatus::Done)) | 1289 | Poll::Ready(Ok(SendStatus::Done)) |
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index a51d21bb0..b6d3daf54 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs | |||
| @@ -27,7 +27,8 @@ enum Function { | |||
| 27 | Transmit, | 27 | Transmit, |
| 28 | /// Receive audio data | 28 | /// Receive audio data |
| 29 | Receive, | 29 | Receive, |
| 30 | #[cfg(spi_v3)] | 30 | #[cfg(any(spi_v4, spi_v5))] |
| 31 | |||
| 31 | /// Transmit and Receive audio data | 32 | /// Transmit and Receive audio data |
| 32 | FullDuplex, | 33 | FullDuplex, |
| 33 | } | 34 | } |
| @@ -72,7 +73,6 @@ impl From<ringbuffer::Error> for Error { | |||
| 72 | } | 73 | } |
| 73 | 74 | ||
| 74 | impl Standard { | 75 | impl Standard { |
| 75 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | ||
| 76 | const fn i2sstd(&self) -> vals::I2sstd { | 76 | const fn i2sstd(&self) -> vals::I2sstd { |
| 77 | match self { | 77 | match self { |
| 78 | Standard::Philips => vals::I2sstd::PHILIPS, | 78 | Standard::Philips => vals::I2sstd::PHILIPS, |
| @@ -83,7 +83,6 @@ impl Standard { | |||
| 83 | } | 83 | } |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | ||
| 87 | const fn pcmsync(&self) -> vals::Pcmsync { | 86 | const fn pcmsync(&self) -> vals::Pcmsync { |
| 88 | match self { | 87 | match self { |
| 89 | Standard::PcmLongSync => vals::Pcmsync::LONG, | 88 | Standard::PcmLongSync => vals::Pcmsync::LONG, |
| @@ -106,7 +105,6 @@ pub enum Format { | |||
| 106 | } | 105 | } |
| 107 | 106 | ||
| 108 | impl Format { | 107 | impl Format { |
| 109 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | ||
| 110 | const fn datlen(&self) -> vals::Datlen { | 108 | const fn datlen(&self) -> vals::Datlen { |
| 111 | match self { | 109 | match self { |
| 112 | Format::Data16Channel16 => vals::Datlen::BITS16, | 110 | Format::Data16Channel16 => vals::Datlen::BITS16, |
| @@ -116,7 +114,6 @@ impl Format { | |||
| 116 | } | 114 | } |
| 117 | } | 115 | } |
| 118 | 116 | ||
| 119 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | ||
| 120 | const fn chlen(&self) -> vals::Chlen { | 117 | const fn chlen(&self) -> vals::Chlen { |
| 121 | match self { | 118 | match self { |
| 122 | Format::Data16Channel16 => vals::Chlen::BITS16, | 119 | Format::Data16Channel16 => vals::Chlen::BITS16, |
| @@ -137,7 +134,6 @@ pub enum ClockPolarity { | |||
| 137 | } | 134 | } |
| 138 | 135 | ||
| 139 | impl ClockPolarity { | 136 | impl ClockPolarity { |
| 140 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | ||
| 141 | const fn ckpol(&self) -> vals::Ckpol { | 137 | const fn ckpol(&self) -> vals::Ckpol { |
| 142 | match self { | 138 | match self { |
| 143 | ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH, | 139 | ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH, |
| @@ -241,12 +237,12 @@ pub struct I2S<'d, W: Word> { | |||
| 241 | 237 | ||
| 242 | impl<'d, W: Word> I2S<'d, W> { | 238 | impl<'d, W: Word> I2S<'d, W> { |
| 243 | /// Create a transmitter driver. | 239 | /// Create a transmitter driver. |
| 244 | pub fn new_txonly<T: Instance>( | 240 | pub fn new_txonly<T: Instance, #[cfg(afio)] A>( |
| 245 | peri: Peri<'d, T>, | 241 | peri: Peri<'d, T>, |
| 246 | sd: Peri<'d, impl MosiPin<T>>, | 242 | sd: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 247 | ws: Peri<'d, impl WsPin<T>>, | 243 | ws: Peri<'d, if_afio!(impl WsPin<T, A>)>, |
| 248 | ck: Peri<'d, impl CkPin<T>>, | 244 | ck: Peri<'d, if_afio!(impl CkPin<T, A>)>, |
| 249 | mck: Peri<'d, impl MckPin<T>>, | 245 | mck: Peri<'d, if_afio!(impl MckPin<T, A>)>, |
| 250 | txdma: Peri<'d, impl TxDma<T>>, | 246 | txdma: Peri<'d, impl TxDma<T>>, |
| 251 | txdma_buf: &'d mut [W], | 247 | txdma_buf: &'d mut [W], |
| 252 | config: Config, | 248 | config: Config, |
| @@ -266,11 +262,11 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 266 | } | 262 | } |
| 267 | 263 | ||
| 268 | /// Create a transmitter driver without a master clock pin. | 264 | /// Create a transmitter driver without a master clock pin. |
| 269 | pub fn new_txonly_nomck<T: Instance>( | 265 | pub fn new_txonly_nomck<T: Instance, #[cfg(afio)] A>( |
| 270 | peri: Peri<'d, T>, | 266 | peri: Peri<'d, T>, |
| 271 | sd: Peri<'d, impl MosiPin<T>>, | 267 | sd: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 272 | ws: Peri<'d, impl WsPin<T>>, | 268 | ws: Peri<'d, if_afio!(impl WsPin<T, A>)>, |
| 273 | ck: Peri<'d, impl CkPin<T>>, | 269 | ck: Peri<'d, if_afio!(impl CkPin<T, A>)>, |
| 274 | txdma: Peri<'d, impl TxDma<T>>, | 270 | txdma: Peri<'d, impl TxDma<T>>, |
| 275 | txdma_buf: &'d mut [W], | 271 | txdma_buf: &'d mut [W], |
| 276 | config: Config, | 272 | config: Config, |
| @@ -290,12 +286,12 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 290 | } | 286 | } |
| 291 | 287 | ||
| 292 | /// Create a receiver driver. | 288 | /// Create a receiver driver. |
| 293 | pub fn new_rxonly<T: Instance>( | 289 | pub fn new_rxonly<T: Instance, #[cfg(afio)] A>( |
| 294 | peri: Peri<'d, T>, | 290 | peri: Peri<'d, T>, |
| 295 | sd: Peri<'d, impl MisoPin<T>>, | 291 | sd: Peri<'d, if_afio!(impl MisoPin<T, A>)>, |
| 296 | ws: Peri<'d, impl WsPin<T>>, | 292 | ws: Peri<'d, if_afio!(impl WsPin<T, A>)>, |
| 297 | ck: Peri<'d, impl CkPin<T>>, | 293 | ck: Peri<'d, if_afio!(impl CkPin<T, A>)>, |
| 298 | mck: Peri<'d, impl MckPin<T>>, | 294 | mck: Peri<'d, if_afio!(impl MckPin<T, A>)>, |
| 299 | rxdma: Peri<'d, impl RxDma<T>>, | 295 | rxdma: Peri<'d, impl RxDma<T>>, |
| 300 | rxdma_buf: &'d mut [W], | 296 | rxdma_buf: &'d mut [W], |
| 301 | config: Config, | 297 | config: Config, |
| @@ -314,15 +310,16 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 314 | ) | 310 | ) |
| 315 | } | 311 | } |
| 316 | 312 | ||
| 317 | #[cfg(spi_v3)] | 313 | #[cfg(any(spi_v4, spi_v5))] |
| 314 | |||
| 318 | /// Create a full duplex driver. | 315 | /// Create a full duplex driver. |
| 319 | pub fn new_full_duplex<T: Instance>( | 316 | pub fn new_full_duplex<T: Instance, #[cfg(afio)] A>( |
| 320 | peri: Peri<'d, T>, | 317 | peri: Peri<'d, T>, |
| 321 | txsd: Peri<'d, impl MosiPin<T>>, | 318 | txsd: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 322 | rxsd: Peri<'d, impl MisoPin<T>>, | 319 | rxsd: Peri<'d, if_afio!(impl MisoPin<T, A>)>, |
| 323 | ws: Peri<'d, impl WsPin<T>>, | 320 | ws: Peri<'d, if_afio!(impl WsPin<T, A>)>, |
| 324 | ck: Peri<'d, impl CkPin<T>>, | 321 | ck: Peri<'d, if_afio!(impl CkPin<T, A>)>, |
| 325 | mck: Peri<'d, impl MckPin<T>>, | 322 | mck: Peri<'d, if_afio!(impl MckPin<T, A>)>, |
| 326 | txdma: Peri<'d, impl TxDma<T>>, | 323 | txdma: Peri<'d, impl TxDma<T>>, |
| 327 | txdma_buf: &'d mut [W], | 324 | txdma_buf: &'d mut [W], |
| 328 | rxdma: Peri<'d, impl RxDma<T>>, | 325 | rxdma: Peri<'d, impl RxDma<T>>, |
| @@ -357,7 +354,7 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 357 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | 354 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { |
| 358 | rx_ring_buffer.start(); | 355 | rx_ring_buffer.start(); |
| 359 | // SPIv3 clears rxfifo on SPE=0 | 356 | // SPIv3 clears rxfifo on SPE=0 |
| 360 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 357 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 361 | flush_rx_fifo(self.spi.info.regs); | 358 | flush_rx_fifo(self.spi.info.regs); |
| 362 | 359 | ||
| 363 | set_rxdmaen(self.spi.info.regs, true); | 360 | set_rxdmaen(self.spi.info.regs, true); |
| @@ -365,7 +362,7 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 365 | self.spi.info.regs.cr1().modify(|w| { | 362 | self.spi.info.regs.cr1().modify(|w| { |
| 366 | w.set_spe(true); | 363 | w.set_spe(true); |
| 367 | }); | 364 | }); |
| 368 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 365 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 369 | self.spi.info.regs.cr1().modify(|w| { | 366 | self.spi.info.regs.cr1().modify(|w| { |
| 370 | w.set_cstart(true); | 367 | w.set_cstart(true); |
| 371 | }); | 368 | }); |
| @@ -404,7 +401,7 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 404 | 401 | ||
| 405 | join(rx_f, tx_f).await; | 402 | join(rx_f, tx_f).await; |
| 406 | 403 | ||
| 407 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 404 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 408 | { | 405 | { |
| 409 | if let Mode::Master = self.mode { | 406 | if let Mode::Master = self.mode { |
| 410 | regs.cr1().modify(|w| { | 407 | regs.cr1().modify(|w| { |
| @@ -462,20 +459,20 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 462 | } | 459 | } |
| 463 | } | 460 | } |
| 464 | 461 | ||
| 465 | fn new_inner<T: Instance>( | 462 | fn new_inner<T: Instance, #[cfg(afio)] A>( |
| 466 | peri: Peri<'d, T>, | 463 | peri: Peri<'d, T>, |
| 467 | txsd: Option<Peri<'d, AnyPin>>, | 464 | txsd: Option<Peri<'d, AnyPin>>, |
| 468 | rxsd: Option<Peri<'d, AnyPin>>, | 465 | rxsd: Option<Peri<'d, AnyPin>>, |
| 469 | ws: Peri<'d, impl WsPin<T>>, | 466 | ws: Peri<'d, if_afio!(impl WsPin<T, A>)>, |
| 470 | ck: Peri<'d, impl CkPin<T>>, | 467 | ck: Peri<'d, if_afio!(impl CkPin<T, A>)>, |
| 471 | mck: Option<Peri<'d, AnyPin>>, | 468 | mck: Option<Peri<'d, AnyPin>>, |
| 472 | txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, | 469 | txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, |
| 473 | rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, | 470 | rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, |
| 474 | config: Config, | 471 | config: Config, |
| 475 | function: Function, | 472 | function: Function, |
| 476 | ) -> Self { | 473 | ) -> Self { |
| 477 | ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, config.gpio_speed)); | 474 | set_as_af!(ws, AfType::output(OutputType::PushPull, config.gpio_speed)); |
| 478 | ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, config.gpio_speed)); | 475 | set_as_af!(ck, AfType::output(OutputType::PushPull, config.gpio_speed)); |
| 479 | 476 | ||
| 480 | let spi = Spi::new_internal(peri, None, None, { | 477 | let spi = Spi::new_internal(peri, None, None, { |
| 481 | let mut spi_config = SpiConfig::default(); | 478 | let mut spi_config = SpiConfig::default(); |
| @@ -492,103 +489,98 @@ impl<'d, W: Word> I2S<'d, W> { | |||
| 492 | 489 | ||
| 493 | let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format); | 490 | let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format); |
| 494 | 491 | ||
| 495 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | 492 | #[cfg(any(spi_v4, spi_v5))] |
| 496 | { | 493 | { |
| 497 | #[cfg(spi_v3)] | 494 | regs.cr1().modify(|w| w.set_spe(false)); |
| 498 | { | ||
| 499 | regs.cr1().modify(|w| w.set_spe(false)); | ||
| 500 | 495 | ||
| 501 | reset_incompatible_bitfields::<T>(); | 496 | reset_incompatible_bitfields::<T>(); |
| 502 | } | 497 | } |
| 503 | 498 | ||
| 504 | use stm32_metapac::spi::vals::{I2scfg, Odd}; | 499 | use stm32_metapac::spi::vals::{I2scfg, Odd}; |
| 505 | 500 | ||
| 506 | // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud | 501 | // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud |
| 507 | // rate to reach the proper audio sample frequency. The ODD bit in the | 502 | // rate to reach the proper audio sample frequency. The ODD bit in the |
| 508 | // SPI_I2SPR/SPI_I2SCFGR register also has to be defined. | 503 | // SPI_I2SPR/SPI_I2SCFGR register also has to be defined. |
| 509 | |||
| 510 | // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the | ||
| 511 | // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to | ||
| 512 | // the external DAC/ADC audio component (the I2SDIV and ODD values should be | ||
| 513 | // computed depending on the state of the MCK output, for more details refer to | ||
| 514 | // Section 28.4.4: Clock generator). | ||
| 515 | |||
| 516 | // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the | ||
| 517 | // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the | ||
| 518 | // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit. | ||
| 519 | // Select also the I2S master mode and direction (Transmitter or Receiver) through the | ||
| 520 | // I2SCFG[1:0] bits in the SPI_I2SCFGR register. | ||
| 521 | |||
| 522 | // 4. If needed, select all the potential interruption sources and the DMA capabilities by | ||
| 523 | // writing the SPI_CR2 register. | ||
| 524 | |||
| 525 | // 5. The I2SE bit in SPI_I2SCFGR register must be set. | ||
| 526 | |||
| 527 | let clk_reg = { | ||
| 528 | #[cfg(any(spi_v1, spi_f1))] | ||
| 529 | { | ||
| 530 | regs.i2spr() | ||
| 531 | } | ||
| 532 | #[cfg(spi_v3)] | ||
| 533 | { | ||
| 534 | regs.i2scfgr() | ||
| 535 | } | ||
| 536 | }; | ||
| 537 | |||
| 538 | clk_reg.modify(|w| { | ||
| 539 | w.set_i2sdiv(div); | ||
| 540 | w.set_odd(match odd { | ||
| 541 | true => Odd::ODD, | ||
| 542 | false => Odd::EVEN, | ||
| 543 | }); | ||
| 544 | 504 | ||
| 545 | w.set_mckoe(config.master_clock); | 505 | // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the |
| 506 | // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to | ||
| 507 | // the external DAC/ADC audio component (the I2SDIV and ODD values should be | ||
| 508 | // computed depending on the state of the MCK output, for more details refer to | ||
| 509 | // Section 28.4.4: Clock generator). | ||
| 510 | |||
| 511 | // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the | ||
| 512 | // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the | ||
| 513 | // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit. | ||
| 514 | // Select also the I2S master mode and direction (Transmitter or Receiver) through the | ||
| 515 | // I2SCFG[1:0] bits in the SPI_I2SCFGR register. | ||
| 516 | |||
| 517 | // 4. If needed, select all the potential interruption sources and the DMA capabilities by | ||
| 518 | // writing the SPI_CR2 register. | ||
| 519 | |||
| 520 | // 5. The I2SE bit in SPI_I2SCFGR register must be set. | ||
| 521 | |||
| 522 | let clk_reg = { | ||
| 523 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 524 | { | ||
| 525 | regs.i2spr() | ||
| 526 | } | ||
| 527 | #[cfg(any(spi_v4, spi_v5))] | ||
| 528 | { | ||
| 529 | regs.i2scfgr() | ||
| 530 | } | ||
| 531 | }; | ||
| 532 | |||
| 533 | clk_reg.modify(|w| { | ||
| 534 | w.set_i2sdiv(div); | ||
| 535 | w.set_odd(match odd { | ||
| 536 | true => Odd::ODD, | ||
| 537 | false => Odd::EVEN, | ||
| 546 | }); | 538 | }); |
| 547 | 539 | ||
| 548 | regs.i2scfgr().modify(|w| { | 540 | w.set_mckoe(config.master_clock); |
| 549 | w.set_ckpol(config.clock_polarity.ckpol()); | 541 | }); |
| 550 | 542 | ||
| 551 | w.set_i2smod(true); | 543 | regs.i2scfgr().modify(|w| { |
| 544 | w.set_ckpol(config.clock_polarity.ckpol()); | ||
| 552 | 545 | ||
| 553 | w.set_i2sstd(config.standard.i2sstd()); | 546 | w.set_i2smod(true); |
| 554 | w.set_pcmsync(config.standard.pcmsync()); | ||
| 555 | 547 | ||
| 556 | w.set_datlen(config.format.datlen()); | 548 | w.set_i2sstd(config.standard.i2sstd()); |
| 557 | w.set_chlen(config.format.chlen()); | 549 | w.set_pcmsync(config.standard.pcmsync()); |
| 558 | 550 | ||
| 559 | w.set_i2scfg(match (config.mode, function) { | 551 | w.set_datlen(config.format.datlen()); |
| 560 | (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX, | 552 | w.set_chlen(config.format.chlen()); |
| 561 | (Mode::Master, Function::Receive) => I2scfg::MASTER_RX, | ||
| 562 | #[cfg(spi_v3)] | ||
| 563 | (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX, | ||
| 564 | (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX, | ||
| 565 | (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX, | ||
| 566 | #[cfg(spi_v3)] | ||
| 567 | (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX, | ||
| 568 | }); | ||
| 569 | 553 | ||
| 570 | #[cfg(any(spi_v1, spi_f1))] | 554 | w.set_i2scfg(match (config.mode, function) { |
| 571 | w.set_i2se(true); | 555 | (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX, |
| 556 | (Mode::Master, Function::Receive) => I2scfg::MASTER_RX, | ||
| 557 | #[cfg(any(spi_v4, spi_v5))] | ||
| 558 | (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX, | ||
| 559 | (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX, | ||
| 560 | (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX, | ||
| 561 | #[cfg(any(spi_v4, spi_v5))] | ||
| 562 | (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX, | ||
| 572 | }); | 563 | }); |
| 573 | 564 | ||
| 574 | let mut opts = TransferOptions::default(); | 565 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 575 | opts.half_transfer_ir = true; | 566 | w.set_i2se(true); |
| 576 | 567 | }); | |
| 577 | Self { | 568 | |
| 578 | mode: config.mode, | 569 | let mut opts = TransferOptions::default(); |
| 579 | spi, | 570 | opts.half_transfer_ir = true; |
| 580 | txsd: txsd.map(|w| w.into()), | 571 | |
| 581 | rxsd: rxsd.map(|w| w.into()), | 572 | Self { |
| 582 | ws: Some(ws.into()), | 573 | mode: config.mode, |
| 583 | ck: Some(ck.into()), | 574 | spi, |
| 584 | mck: mck.map(|w| w.into()), | 575 | txsd: txsd.map(|w| w.into()), |
| 585 | tx_ring_buffer: txdma.map(|(ch, buf)| unsafe { | 576 | rxsd: rxsd.map(|w| w.into()), |
| 586 | WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) | 577 | ws: Some(ws.into()), |
| 587 | }), | 578 | ck: Some(ck.into()), |
| 588 | rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe { | 579 | mck: mck.map(|w| w.into()), |
| 589 | ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) | 580 | tx_ring_buffer: txdma |
| 590 | }), | 581 | .map(|(ch, buf)| unsafe { WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) }), |
| 591 | } | 582 | rx_ring_buffer: rxdma |
| 583 | .map(|(ch, buf)| unsafe { ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) }), | ||
| 592 | } | 584 | } |
| 593 | } | 585 | } |
| 594 | } | 586 | } |
| @@ -639,7 +631,8 @@ fn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_for | |||
| 639 | } | 631 | } |
| 640 | } | 632 | } |
| 641 | 633 | ||
| 642 | #[cfg(spi_v3)] | 634 | #[cfg(any(spi_v4, spi_v5))] |
| 635 | |||
| 643 | // The STM32H7 reference manual specifies that any incompatible bitfields should be reset | 636 | // The STM32H7 reference manual specifies that any incompatible bitfields should be reset |
| 644 | // to their reset values while operating in I2S mode. | 637 | // to their reset values while operating in I2S mode. |
| 645 | fn reset_incompatible_bitfields<T: Instance>() { | 638 | fn reset_incompatible_bitfields<T: Instance>() { |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 3be98c462..7e0f7884e 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -87,7 +87,7 @@ pub mod hsem; | |||
| 87 | pub mod hspi; | 87 | pub mod hspi; |
| 88 | #[cfg(i2c)] | 88 | #[cfg(i2c)] |
| 89 | pub mod i2c; | 89 | pub mod i2c; |
| 90 | #[cfg(any(all(spi_v1, rcc_f4), spi_v3))] | 90 | #[cfg(any(spi_v1_i2s, spi_v2_i2s, spi_v3_i2s, spi_v4_i2s, spi_v5_i2s))] |
| 91 | pub mod i2s; | 91 | pub mod i2s; |
| 92 | #[cfg(stm32wb)] | 92 | #[cfg(stm32wb)] |
| 93 | pub mod ipcc; | 93 | pub mod ipcc; |
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 2f2d7ba01..96af9f4d9 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs | |||
| @@ -50,10 +50,7 @@ macro_rules! channel_impl { | |||
| 50 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | 50 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| 51 | critical_section::with(|_| { | 51 | critical_section::with(|_| { |
| 52 | pin.set_low(); | 52 | pin.set_low(); |
| 53 | pin.set_as_af( | 53 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 54 | pin.af_num(), | ||
| 55 | AfType::output(OutputType::PushPull, Speed::VeryHigh), | ||
| 56 | ); | ||
| 57 | }); | 54 | }); |
| 58 | PwmPin { | 55 | PwmPin { |
| 59 | _pin: pin.into(), | 56 | _pin: pin.into(), |
| @@ -64,12 +61,12 @@ macro_rules! channel_impl { | |||
| 64 | pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self { | 61 | pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self { |
| 65 | critical_section::with(|_| { | 62 | critical_section::with(|_| { |
| 66 | pin.set_low(); | 63 | pin.set_low(); |
| 67 | pin.set_as_af( | 64 | #[cfg(gpio_v1)] |
| 68 | pin.af_num(), | 65 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); |
| 69 | #[cfg(gpio_v1)] | 66 | #[cfg(gpio_v2)] |
| 70 | AfType::output(pin_config.output_type, pin_config.speed), | 67 | set_as_af!( |
| 71 | #[cfg(gpio_v2)] | 68 | pin, |
| 72 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | 69 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull) |
| 73 | ); | 70 | ); |
| 74 | }); | 71 | }); |
| 75 | PwmPin { | 72 | PwmPin { |
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 3a0b490ba..22cc2e049 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs | |||
| @@ -41,17 +41,30 @@ macro_rules! peri_trait_impl { | |||
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | macro_rules! pin_trait { | 43 | macro_rules! pin_trait { |
| 44 | ($signal:ident, $instance:path $(, $mode:path)?) => { | 44 | ($signal:ident, $instance:path $(, $mode:path)? $(, @$afio:ident)?) => { |
| 45 | #[doc = concat!(stringify!($signal), " pin trait")] | 45 | #[doc = concat!(stringify!($signal), " pin trait")] |
| 46 | pub trait $signal<T: $instance $(, M: $mode)?>: crate::gpio::Pin { | 46 | pub trait $signal<T: $instance $(, M: $mode)? $(, #[cfg(afio)] $afio)?>: crate::gpio::Pin { |
| 47 | #[cfg(not(afio))] | ||
| 47 | #[doc = concat!("Get the AF number needed to use this pin as ", stringify!($signal))] | 48 | #[doc = concat!("Get the AF number needed to use this pin as ", stringify!($signal))] |
| 48 | fn af_num(&self) -> u8; | 49 | fn af_num(&self) -> u8; |
| 50 | |||
| 51 | #[cfg(afio)] | ||
| 52 | #[doc = concat!("Configures AFIO_MAPR to use this pin as ", stringify!($signal))] | ||
| 53 | fn afio_remap(&self); | ||
| 49 | } | 54 | } |
| 50 | }; | 55 | }; |
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | macro_rules! pin_trait_impl { | 58 | macro_rules! pin_trait_impl { |
| 54 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr) => { | 59 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr $(, $afio:path)?) => { |
| 60 | #[cfg(afio)] | ||
| 61 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)? $(, $afio)?> for crate::peripherals::$pin { | ||
| 62 | fn afio_remap(&self) { | ||
| 63 | // nothing | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | #[cfg(not(afio))] | ||
| 55 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin { | 68 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin { |
| 56 | fn af_num(&self) -> u8 { | 69 | fn af_num(&self) -> u8 { |
| 57 | $af | 70 | $af |
| @@ -60,6 +73,39 @@ macro_rules! pin_trait_impl { | |||
| 60 | }; | 73 | }; |
| 61 | } | 74 | } |
| 62 | 75 | ||
| 76 | #[cfg(afio)] | ||
| 77 | macro_rules! pin_trait_afio_impl { | ||
| 78 | (@set mapr, $setter:ident, $val:expr) => { | ||
| 79 | crate::pac::AFIO.mapr().modify(|w| { | ||
| 80 | w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); | ||
| 81 | w.$setter($val); | ||
| 82 | }); | ||
| 83 | }; | ||
| 84 | (@set mapr2, $setter:ident, $val:expr) => { | ||
| 85 | crate::pac::AFIO.mapr2().modify(|w| { | ||
| 86 | w.$setter($val); | ||
| 87 | }); | ||
| 88 | }; | ||
| 89 | (crate::$mod:ident::$trait:ident<$mode:ident>, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => { | ||
| 90 | $( | ||
| 91 | impl crate::$mod::$trait<crate::peripherals::$instance, crate::$mod::$mode, crate::gpio::$type<$val>> for crate::peripherals::$pin { | ||
| 92 | fn afio_remap(&self) { | ||
| 93 | pin_trait_afio_impl!(@set $reg, $setter, $val); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | )+ | ||
| 97 | }; | ||
| 98 | (crate::$mod:ident::$trait:ident, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => { | ||
| 99 | $( | ||
| 100 | impl crate::$mod::$trait<crate::peripherals::$instance, crate::gpio::$type<$val>> for crate::peripherals::$pin { | ||
| 101 | fn afio_remap(&self) { | ||
| 102 | pin_trait_afio_impl!(@set $reg, $setter, $val); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | )+ | ||
| 106 | }; | ||
| 107 | } | ||
| 108 | |||
| 63 | #[allow(unused_macros)] | 109 | #[allow(unused_macros)] |
| 64 | macro_rules! sel_trait_impl { | 110 | macro_rules! sel_trait_impl { |
| 65 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { | 111 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { |
| @@ -134,7 +180,73 @@ macro_rules! new_dma { | |||
| 134 | macro_rules! new_pin { | 180 | macro_rules! new_pin { |
| 135 | ($name:ident, $af_type:expr) => {{ | 181 | ($name:ident, $af_type:expr) => {{ |
| 136 | let pin = $name; | 182 | let pin = $name; |
| 137 | pin.set_as_af(pin.af_num(), $af_type); | 183 | #[cfg(afio)] |
| 184 | pin.afio_remap(); | ||
| 185 | pin.set_as_af( | ||
| 186 | #[cfg(not(afio))] | ||
| 187 | pin.af_num(), | ||
| 188 | $af_type, | ||
| 189 | ); | ||
| 138 | Some(pin.into()) | 190 | Some(pin.into()) |
| 139 | }}; | 191 | }}; |
| 140 | } | 192 | } |
| 193 | |||
| 194 | /// Macro to configure a pin for alternate function use. | ||
| 195 | /// For AFIO chips (STM32F1), it calls afio_remap(). | ||
| 196 | /// For non-AFIO chips, it calls set_as_af() with the pin's af_num(). | ||
| 197 | macro_rules! set_as_af { | ||
| 198 | ($pin:expr, $af_type:expr) => { | ||
| 199 | #[cfg(afio)] | ||
| 200 | { | ||
| 201 | $pin.set_as_af($af_type); | ||
| 202 | $pin.afio_remap(); | ||
| 203 | } | ||
| 204 | #[cfg(not(afio))] | ||
| 205 | { | ||
| 206 | $pin.set_as_af($pin.af_num(), $af_type); | ||
| 207 | } | ||
| 208 | }; | ||
| 209 | } | ||
| 210 | |||
| 211 | #[cfg(afio)] | ||
| 212 | macro_rules! if_afio { | ||
| 213 | ($($t:tt)*) => { | ||
| 214 | $($t)* | ||
| 215 | } | ||
| 216 | } | ||
| 217 | #[cfg(not(afio))] | ||
| 218 | macro_rules! if_afio { | ||
| 219 | (($a:ty, A)) => { | ||
| 220 | ($a,) | ||
| 221 | }; | ||
| 222 | (($a:ty, $b:ty, A)) => { | ||
| 223 | ($a,$b) | ||
| 224 | }; | ||
| 225 | (($a:ty, $b:ty, $c:ty, A)) => { | ||
| 226 | ($a,$b, $c) | ||
| 227 | }; | ||
| 228 | ($type:ident<$lt:lifetime, $a:ty, $b:ty, A>) => { | ||
| 229 | $type<$lt, $a, $b> | ||
| 230 | }; | ||
| 231 | ($type:ident<$lt:lifetime, $a:ty, $b:ty, $c:ty, A>) => { | ||
| 232 | $type<$lt, $a, $b, $c> | ||
| 233 | }; | ||
| 234 | ($type:ident<$a:ty, A>) => { | ||
| 235 | $type<$a> | ||
| 236 | }; | ||
| 237 | ($type:ident<$a:ty, $b:ty, A>) => { | ||
| 238 | $type<$a, $b> | ||
| 239 | }; | ||
| 240 | ($type:ident<$a:ty, $b:ty, $c:ty, A>) => { | ||
| 241 | $type<$a, $b, $c> | ||
| 242 | }; | ||
| 243 | (impl $trait:ident<$a:ty, A>) => { | ||
| 244 | impl $trait<$a> | ||
| 245 | }; | ||
| 246 | (impl $trait:ident<$a:ty, $b:ty, A>) => { | ||
| 247 | impl $trait<$a, $b> | ||
| 248 | }; | ||
| 249 | (impl $trait:ident<$a:ty, $b:ty, $c:ty, A>) => { | ||
| 250 | impl $trait<$a, $b, $c> | ||
| 251 | }; | ||
| 252 | } | ||
diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs index 4021f7ce3..4db801752 100644 --- a/embassy-stm32/src/ospi/enums.rs +++ b/embassy-stm32/src/ospi/enums.rs | |||
| @@ -23,6 +23,7 @@ impl Into<u8> for OspiMode { | |||
| 23 | /// Ospi lane width | 23 | /// Ospi lane width |
| 24 | #[allow(dead_code)] | 24 | #[allow(dead_code)] |
| 25 | #[derive(Copy, Clone)] | 25 | #[derive(Copy, Clone)] |
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub enum OspiWidth { | 27 | pub enum OspiWidth { |
| 27 | /// None | 28 | /// None |
| 28 | NONE, | 29 | NONE, |
| @@ -71,6 +72,7 @@ impl Into<bool> for FlashSelection { | |||
| 71 | #[allow(dead_code)] | 72 | #[allow(dead_code)] |
| 72 | #[allow(missing_docs)] | 73 | #[allow(missing_docs)] |
| 73 | #[derive(Copy, Clone)] | 74 | #[derive(Copy, Clone)] |
| 75 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 74 | pub enum WrapSize { | 76 | pub enum WrapSize { |
| 75 | None, | 77 | None, |
| 76 | _16Bytes, | 78 | _16Bytes, |
| @@ -95,6 +97,7 @@ impl Into<u8> for WrapSize { | |||
| 95 | #[allow(missing_docs)] | 97 | #[allow(missing_docs)] |
| 96 | #[allow(dead_code)] | 98 | #[allow(dead_code)] |
| 97 | #[derive(Copy, Clone)] | 99 | #[derive(Copy, Clone)] |
| 100 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 98 | pub enum MemoryType { | 101 | pub enum MemoryType { |
| 99 | Micron, | 102 | Micron, |
| 100 | Macronix, | 103 | Macronix, |
| @@ -120,6 +123,7 @@ impl Into<u8> for MemoryType { | |||
| 120 | /// Ospi memory size. | 123 | /// Ospi memory size. |
| 121 | #[allow(missing_docs)] | 124 | #[allow(missing_docs)] |
| 122 | #[derive(Copy, Clone)] | 125 | #[derive(Copy, Clone)] |
| 126 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 123 | pub enum MemorySize { | 127 | pub enum MemorySize { |
| 124 | _1KiB, | 128 | _1KiB, |
| 125 | _2KiB, | 129 | _2KiB, |
| @@ -180,6 +184,7 @@ impl Into<u8> for MemorySize { | |||
| 180 | 184 | ||
| 181 | /// Ospi Address size | 185 | /// Ospi Address size |
| 182 | #[derive(Copy, Clone)] | 186 | #[derive(Copy, Clone)] |
| 187 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 183 | pub enum AddressSize { | 188 | pub enum AddressSize { |
| 184 | /// 8-bit address | 189 | /// 8-bit address |
| 185 | _8Bit, | 190 | _8Bit, |
| @@ -205,6 +210,7 @@ impl Into<u8> for AddressSize { | |||
| 205 | /// Time the Chip Select line stays high. | 210 | /// Time the Chip Select line stays high. |
| 206 | #[allow(missing_docs)] | 211 | #[allow(missing_docs)] |
| 207 | #[derive(Copy, Clone)] | 212 | #[derive(Copy, Clone)] |
| 213 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 208 | pub enum ChipSelectHighTime { | 214 | pub enum ChipSelectHighTime { |
| 209 | _1Cycle, | 215 | _1Cycle, |
| 210 | _2Cycle, | 216 | _2Cycle, |
| @@ -234,6 +240,7 @@ impl Into<u8> for ChipSelectHighTime { | |||
| 234 | /// FIFO threshold. | 240 | /// FIFO threshold. |
| 235 | #[allow(missing_docs)] | 241 | #[allow(missing_docs)] |
| 236 | #[derive(Copy, Clone)] | 242 | #[derive(Copy, Clone)] |
| 243 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 237 | pub enum FIFOThresholdLevel { | 244 | pub enum FIFOThresholdLevel { |
| 238 | _1Bytes, | 245 | _1Bytes, |
| 239 | _2Bytes, | 246 | _2Bytes, |
| @@ -311,6 +318,7 @@ impl Into<u8> for FIFOThresholdLevel { | |||
| 311 | /// Dummy cycle count | 318 | /// Dummy cycle count |
| 312 | #[allow(missing_docs)] | 319 | #[allow(missing_docs)] |
| 313 | #[derive(Copy, Clone)] | 320 | #[derive(Copy, Clone)] |
| 321 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 314 | pub enum DummyCycles { | 322 | pub enum DummyCycles { |
| 315 | _0, | 323 | _0, |
| 316 | _1, | 324 | _1, |
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 74edfd5e4..d93cecb69 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -23,6 +23,7 @@ use crate::{peripherals, Peri}; | |||
| 23 | 23 | ||
| 24 | /// OPSI driver config. | 24 | /// OPSI driver config. |
| 25 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub struct Config { | 27 | pub struct Config { |
| 27 | /// Fifo threshold used by the peripheral to generate the interrupt indicating data | 28 | /// Fifo threshold used by the peripheral to generate the interrupt indicating data |
| 28 | /// or space is available in the FIFO | 29 | /// or space is available in the FIFO |
| @@ -30,7 +31,9 @@ pub struct Config { | |||
| 30 | /// Indicates the type of external device connected | 31 | /// Indicates the type of external device connected |
| 31 | pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface | 32 | pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface |
| 32 | /// Defines the size of the external device connected to the OSPI corresponding | 33 | /// Defines the size of the external device connected to the OSPI corresponding |
| 33 | /// to the number of address bits required to access the device | 34 | /// to the number of address bits required to access the device. |
| 35 | /// When using indirect mode, [`TransferConfig::address`] + the length of the data being read | ||
| 36 | /// or written must fit within the configured `device_size`, otherwise an error is returned. | ||
| 34 | pub device_size: MemorySize, | 37 | pub device_size: MemorySize, |
| 35 | /// Sets the minimum number of clock cycles that the chip select signal must be held high | 38 | /// Sets the minimum number of clock cycles that the chip select signal must be held high |
| 36 | /// between commands | 39 | /// between commands |
| @@ -83,6 +86,8 @@ impl Default for Config { | |||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | /// OSPI transfer configuration. | 88 | /// OSPI transfer configuration. |
| 89 | #[derive(Clone, Copy)] | ||
| 90 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 86 | pub struct TransferConfig { | 91 | pub struct TransferConfig { |
| 87 | /// Instruction width (IMODE) | 92 | /// Instruction width (IMODE) |
| 88 | pub iwidth: OspiWidth, | 93 | pub iwidth: OspiWidth, |
| @@ -92,10 +97,11 @@ pub struct TransferConfig { | |||
| 92 | pub isize: AddressSize, | 97 | pub isize: AddressSize, |
| 93 | /// Instruction Double Transfer rate enable | 98 | /// Instruction Double Transfer rate enable |
| 94 | pub idtr: bool, | 99 | pub idtr: bool, |
| 95 | |||
| 96 | /// Address width (ADMODE) | 100 | /// Address width (ADMODE) |
| 97 | pub adwidth: OspiWidth, | 101 | pub adwidth: OspiWidth, |
| 98 | /// Device memory address | 102 | /// Device memory address. |
| 103 | /// In indirect mode, this value + the length of the data being read or written must be within | ||
| 104 | /// configured [`Config::device_size`], otherwise the transfer returns an error. | ||
| 99 | pub address: Option<u32>, | 105 | pub address: Option<u32>, |
| 100 | /// Number of Address Bytes | 106 | /// Number of Address Bytes |
| 101 | pub adsize: AddressSize, | 107 | pub adsize: AddressSize, |
| @@ -113,11 +119,16 @@ pub struct TransferConfig { | |||
| 113 | 119 | ||
| 114 | /// Data width (DMODE) | 120 | /// Data width (DMODE) |
| 115 | pub dwidth: OspiWidth, | 121 | pub dwidth: OspiWidth, |
| 116 | /// Data buffer | 122 | /// Data Double Transfer rate enable |
| 117 | pub ddtr: bool, | 123 | pub ddtr: bool, |
| 118 | 124 | ||
| 119 | /// Number of dummy cycles (DCYC) | 125 | /// Number of dummy cycles (DCYC) |
| 120 | pub dummy: DummyCycles, | 126 | pub dummy: DummyCycles, |
| 127 | |||
| 128 | /// Data strobe (DQS) management enable | ||
| 129 | pub dqse: bool, | ||
| 130 | /// Send instruction only once (SIOO) mode enable | ||
| 131 | pub sioo: bool, | ||
| 121 | } | 132 | } |
| 122 | 133 | ||
| 123 | impl Default for TransferConfig { | 134 | impl Default for TransferConfig { |
| @@ -142,6 +153,9 @@ impl Default for TransferConfig { | |||
| 142 | ddtr: false, | 153 | ddtr: false, |
| 143 | 154 | ||
| 144 | dummy: DummyCycles::_0, | 155 | dummy: DummyCycles::_0, |
| 156 | |||
| 157 | dqse: false, | ||
| 158 | sioo: true, | ||
| 145 | } | 159 | } |
| 146 | } | 160 | } |
| 147 | } | 161 | } |
| @@ -192,26 +206,27 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 192 | let reg = T::REGS; | 206 | let reg = T::REGS; |
| 193 | while reg.sr().read().busy() {} | 207 | while reg.sr().read().busy() {} |
| 194 | 208 | ||
| 195 | reg.ccr().modify(|r| { | 209 | if let Some(instruction) = write_config.instruction { |
| 196 | r.set_dqse(false); | 210 | reg.wir().write(|r| { |
| 197 | r.set_sioo(true); | 211 | r.set_instruction(instruction); |
| 198 | }); | 212 | }); |
| 213 | } | ||
| 199 | 214 | ||
| 200 | // Set wrting configurations, there are separate registers for write configurations in memory mapped mode | 215 | // Set writing configurations, there are separate registers for write configurations in memory mapped mode |
| 201 | reg.wccr().modify(|w| { | 216 | reg.wccr().modify(|w| { |
| 202 | w.set_imode(PhaseMode::from_bits(write_config.iwidth.into())); | 217 | w.set_imode(PhaseMode::from_bits(write_config.iwidth.into())); |
| 203 | w.set_idtr(write_config.idtr); | 218 | w.set_idtr(write_config.idtr); |
| 204 | w.set_isize(SizeInBits::from_bits(write_config.isize.into())); | 219 | w.set_isize(SizeInBits::from_bits(write_config.isize.into())); |
| 205 | 220 | ||
| 206 | w.set_admode(PhaseMode::from_bits(write_config.adwidth.into())); | 221 | w.set_admode(PhaseMode::from_bits(write_config.adwidth.into())); |
| 207 | w.set_addtr(write_config.idtr); | 222 | w.set_addtr(write_config.addtr); |
| 208 | w.set_adsize(SizeInBits::from_bits(write_config.adsize.into())); | 223 | w.set_adsize(SizeInBits::from_bits(write_config.adsize.into())); |
| 209 | 224 | ||
| 210 | w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into())); | 225 | w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into())); |
| 211 | w.set_ddtr(write_config.ddtr); | 226 | w.set_ddtr(write_config.ddtr); |
| 212 | 227 | ||
| 213 | w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into())); | 228 | w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into())); |
| 214 | w.set_dqse(true); | 229 | w.set_dqse(write_config.dqse); |
| 215 | }); | 230 | }); |
| 216 | 231 | ||
| 217 | reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); | 232 | reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); |
| @@ -442,11 +457,6 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 442 | // Configure alternate bytes | 457 | // Configure alternate bytes |
| 443 | if let Some(ab) = command.alternate_bytes { | 458 | if let Some(ab) = command.alternate_bytes { |
| 444 | T::REGS.abr().write(|v| v.set_alternate(ab)); | 459 | T::REGS.abr().write(|v| v.set_alternate(ab)); |
| 445 | T::REGS.ccr().modify(|w| { | ||
| 446 | w.set_abmode(PhaseMode::from_bits(command.abwidth.into())); | ||
| 447 | w.set_abdtr(command.abdtr); | ||
| 448 | w.set_absize(SizeInBits::from_bits(command.absize.into())); | ||
| 449 | }) | ||
| 450 | } | 460 | } |
| 451 | 461 | ||
| 452 | // Configure dummy cycles | 462 | // Configure dummy cycles |
| @@ -458,28 +468,35 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 458 | if let Some(data_length) = data_len { | 468 | if let Some(data_length) = data_len { |
| 459 | T::REGS.dlr().write(|v| { | 469 | T::REGS.dlr().write(|v| { |
| 460 | v.set_dl((data_length - 1) as u32); | 470 | v.set_dl((data_length - 1) as u32); |
| 461 | }) | 471 | }); |
| 462 | } else { | 472 | } else { |
| 463 | T::REGS.dlr().write(|v| { | 473 | T::REGS.dlr().write(|v| { |
| 464 | v.set_dl((0) as u32); | 474 | v.set_dl((0) as u32); |
| 465 | }) | 475 | }); |
| 466 | } | 476 | } |
| 467 | 477 | ||
| 468 | // Configure instruction/address/data modes | 478 | // Configure instruction/address/alternate bytes/data/communication modes |
| 469 | T::REGS.ccr().modify(|w| { | 479 | T::REGS.ccr().modify(|w| { |
| 470 | w.set_imode(PhaseMode::from_bits(command.iwidth.into())); | 480 | w.set_imode(PhaseMode::from_bits(command.iwidth.into())); |
| 471 | w.set_idtr(command.idtr); | 481 | w.set_idtr(command.idtr); |
| 472 | w.set_isize(SizeInBits::from_bits(command.isize.into())); | 482 | w.set_isize(SizeInBits::from_bits(command.isize.into())); |
| 473 | 483 | ||
| 474 | w.set_admode(PhaseMode::from_bits(command.adwidth.into())); | 484 | w.set_admode(PhaseMode::from_bits(command.adwidth.into())); |
| 475 | w.set_addtr(command.idtr); | 485 | w.set_addtr(command.addtr); |
| 476 | w.set_adsize(SizeInBits::from_bits(command.adsize.into())); | 486 | w.set_adsize(SizeInBits::from_bits(command.adsize.into())); |
| 477 | 487 | ||
| 488 | w.set_abmode(PhaseMode::from_bits(command.abwidth.into())); | ||
| 489 | w.set_abdtr(command.abdtr); | ||
| 490 | w.set_absize(SizeInBits::from_bits(command.absize.into())); | ||
| 491 | |||
| 478 | w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); | 492 | w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); |
| 479 | w.set_ddtr(command.ddtr); | 493 | w.set_ddtr(command.ddtr); |
| 494 | |||
| 495 | w.set_dqse(command.dqse); | ||
| 496 | w.set_sioo(command.sioo); | ||
| 480 | }); | 497 | }); |
| 481 | 498 | ||
| 482 | // Set informationrequired to initiate transaction | 499 | // Set information required to initiate transaction |
| 483 | if let Some(instruction) = command.instruction { | 500 | if let Some(instruction) = command.instruction { |
| 484 | if let Some(address) = command.address { | 501 | if let Some(address) = command.address { |
| 485 | T::REGS.ir().write(|v| { | 502 | T::REGS.ir().write(|v| { |
| @@ -514,6 +531,18 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 514 | } | 531 | } |
| 515 | } | 532 | } |
| 516 | 533 | ||
| 534 | // The following errors set the TEF flag in OCTOSPI_SR register: | ||
| 535 | // - in indirect or automatic status-polling mode, when a wrong address has been programmed | ||
| 536 | // in OCTOSPI_AR (according to the device size defined by DEVSIZE[4:0]) | ||
| 537 | // - in indirect mode, if the address plus the data length exceed the device size: TEF is | ||
| 538 | // set as soon as the access is triggered. | ||
| 539 | if T::REGS.sr().read().tef() { | ||
| 540 | // Clear the TEF register to make it ready for the next transfer. | ||
| 541 | T::REGS.fcr().write(|w| w.set_ctef(true)); | ||
| 542 | |||
| 543 | return Err(OspiError::InvalidCommand); | ||
| 544 | } | ||
| 545 | |||
| 517 | Ok(()) | 546 | Ok(()) |
| 518 | } | 547 | } |
| 519 | 548 | ||
| @@ -854,6 +883,45 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { | |||
| 854 | false, | 883 | false, |
| 855 | ) | 884 | ) |
| 856 | } | 885 | } |
| 886 | |||
| 887 | /// Create new blocking OSPI driver for octospi external chips with DQS support | ||
| 888 | pub fn new_blocking_octospi_with_dqs( | ||
| 889 | peri: Peri<'d, T>, | ||
| 890 | sck: Peri<'d, impl SckPin<T>>, | ||
| 891 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 892 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 893 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 894 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 895 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 896 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 897 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 898 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 899 | nss: Peri<'d, impl NSSPin<T>>, | ||
| 900 | dqs: Peri<'d, impl DQSPin<T>>, | ||
| 901 | config: Config, | ||
| 902 | ) -> Self { | ||
| 903 | Self::new_inner( | ||
| 904 | peri, | ||
| 905 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 906 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 907 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 908 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 909 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 910 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 911 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 912 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 913 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 914 | new_pin!( | ||
| 915 | nss, | ||
| 916 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 917 | ), | ||
| 918 | new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 919 | None, | ||
| 920 | config, | ||
| 921 | OspiWidth::OCTO, | ||
| 922 | false, | ||
| 923 | ) | ||
| 924 | } | ||
| 857 | } | 925 | } |
| 858 | 926 | ||
| 859 | impl<'d, T: Instance> Ospi<'d, T, Async> { | 927 | impl<'d, T: Instance> Ospi<'d, T, Async> { |
| @@ -1036,6 +1104,46 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1036 | ) | 1104 | ) |
| 1037 | } | 1105 | } |
| 1038 | 1106 | ||
| 1107 | /// Create new blocking OSPI driver for octospi external chips with DQS support | ||
| 1108 | pub fn new_octospi_with_dqs( | ||
| 1109 | peri: Peri<'d, T>, | ||
| 1110 | sck: Peri<'d, impl SckPin<T>>, | ||
| 1111 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 1112 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 1113 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 1114 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 1115 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 1116 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 1117 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 1118 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 1119 | nss: Peri<'d, impl NSSPin<T>>, | ||
| 1120 | dqs: Peri<'d, impl DQSPin<T>>, | ||
| 1121 | dma: Peri<'d, impl OctoDma<T>>, | ||
| 1122 | config: Config, | ||
| 1123 | ) -> Self { | ||
| 1124 | Self::new_inner( | ||
| 1125 | peri, | ||
| 1126 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1127 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1128 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1129 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1130 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1131 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1132 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1133 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1134 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1135 | new_pin!( | ||
| 1136 | nss, | ||
| 1137 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 1138 | ), | ||
| 1139 | new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1140 | new_dma!(dma), | ||
| 1141 | config, | ||
| 1142 | OspiWidth::OCTO, | ||
| 1143 | false, | ||
| 1144 | ) | ||
| 1145 | } | ||
| 1146 | |||
| 1039 | /// Blocking read with DMA transfer | 1147 | /// Blocking read with DMA transfer |
| 1040 | pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { | 1148 | pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { |
| 1041 | if buf.is_empty() { | 1149 | if buf.is_empty() { |
| @@ -1090,16 +1198,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1090 | .cr() | 1198 | .cr() |
| 1091 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1199 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1092 | 1200 | ||
| 1093 | let transfer = unsafe { | 1201 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1094 | self.dma | 1202 | for chunk in buf.chunks(0xFFFF) { |
| 1095 | .as_mut() | 1203 | let transfer = unsafe { |
| 1096 | .unwrap() | 1204 | self.dma |
| 1097 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1205 | .as_mut() |
| 1098 | }; | 1206 | .unwrap() |
| 1207 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1208 | }; | ||
| 1099 | 1209 | ||
| 1100 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1210 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1101 | 1211 | ||
| 1102 | transfer.blocking_wait(); | 1212 | transfer.blocking_wait(); |
| 1213 | } | ||
| 1103 | 1214 | ||
| 1104 | finish_dma(T::REGS); | 1215 | finish_dma(T::REGS); |
| 1105 | 1216 | ||
| @@ -1160,16 +1271,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1160 | .cr() | 1271 | .cr() |
| 1161 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); | 1272 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1162 | 1273 | ||
| 1163 | let transfer = unsafe { | 1274 | // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. |
| 1164 | self.dma | 1275 | for chunk in buf.chunks(0xFFFF) { |
| 1165 | .as_mut() | 1276 | let transfer = unsafe { |
| 1166 | .unwrap() | 1277 | self.dma |
| 1167 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | 1278 | .as_mut() |
| 1168 | }; | 1279 | .unwrap() |
| 1280 | .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1281 | }; | ||
| 1169 | 1282 | ||
| 1170 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | 1283 | T::REGS.cr().modify(|w| w.set_dmaen(true)); |
| 1171 | 1284 | ||
| 1172 | transfer.await; | 1285 | transfer.await; |
| 1286 | } | ||
| 1173 | 1287 | ||
| 1174 | finish_dma(T::REGS); | 1288 | finish_dma(T::REGS); |
| 1175 | 1289 | ||
diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs index fa5e36d06..8a8420127 100644 --- a/embassy-stm32/src/qspi/enums.rs +++ b/embassy-stm32/src/qspi/enums.rs | |||
| @@ -23,6 +23,7 @@ impl From<QspiMode> for u8 { | |||
| 23 | /// QSPI lane width | 23 | /// QSPI lane width |
| 24 | #[allow(dead_code)] | 24 | #[allow(dead_code)] |
| 25 | #[derive(Copy, Clone)] | 25 | #[derive(Copy, Clone)] |
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub enum QspiWidth { | 27 | pub enum QspiWidth { |
| 27 | /// None | 28 | /// None |
| 28 | NONE, | 29 | NONE, |
| @@ -67,6 +68,7 @@ impl From<FlashSelection> for bool { | |||
| 67 | /// QSPI memory size. | 68 | /// QSPI memory size. |
| 68 | #[allow(missing_docs)] | 69 | #[allow(missing_docs)] |
| 69 | #[derive(Copy, Clone)] | 70 | #[derive(Copy, Clone)] |
| 71 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | pub enum MemorySize { | 72 | pub enum MemorySize { |
| 71 | _1KiB, | 73 | _1KiB, |
| 72 | _2KiB, | 74 | _2KiB, |
| @@ -127,6 +129,7 @@ impl From<MemorySize> for u8 { | |||
| 127 | 129 | ||
| 128 | /// QSPI Address size | 130 | /// QSPI Address size |
| 129 | #[derive(Copy, Clone)] | 131 | #[derive(Copy, Clone)] |
| 132 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 130 | pub enum AddressSize { | 133 | pub enum AddressSize { |
| 131 | /// 8-bit address | 134 | /// 8-bit address |
| 132 | _8Bit, | 135 | _8Bit, |
| @@ -152,6 +155,7 @@ impl From<AddressSize> for u8 { | |||
| 152 | /// Time the Chip Select line stays high. | 155 | /// Time the Chip Select line stays high. |
| 153 | #[allow(missing_docs)] | 156 | #[allow(missing_docs)] |
| 154 | #[derive(Copy, Clone)] | 157 | #[derive(Copy, Clone)] |
| 158 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 155 | pub enum ChipSelectHighTime { | 159 | pub enum ChipSelectHighTime { |
| 156 | _1Cycle, | 160 | _1Cycle, |
| 157 | _2Cycle, | 161 | _2Cycle, |
| @@ -181,6 +185,7 @@ impl From<ChipSelectHighTime> for u8 { | |||
| 181 | /// FIFO threshold. | 185 | /// FIFO threshold. |
| 182 | #[allow(missing_docs)] | 186 | #[allow(missing_docs)] |
| 183 | #[derive(Copy, Clone)] | 187 | #[derive(Copy, Clone)] |
| 188 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 184 | pub enum FIFOThresholdLevel { | 189 | pub enum FIFOThresholdLevel { |
| 185 | _1Bytes, | 190 | _1Bytes, |
| 186 | _2Bytes, | 191 | _2Bytes, |
| @@ -258,6 +263,7 @@ impl From<FIFOThresholdLevel> for u8 { | |||
| 258 | /// Dummy cycle count | 263 | /// Dummy cycle count |
| 259 | #[allow(missing_docs)] | 264 | #[allow(missing_docs)] |
| 260 | #[derive(Copy, Clone)] | 265 | #[derive(Copy, Clone)] |
| 266 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 261 | pub enum DummyCycles { | 267 | pub enum DummyCycles { |
| 262 | _0, | 268 | _0, |
| 263 | _1, | 269 | _1, |
| @@ -334,6 +340,7 @@ impl From<DummyCycles> for u8 { | |||
| 334 | 340 | ||
| 335 | #[allow(missing_docs)] | 341 | #[allow(missing_docs)] |
| 336 | #[derive(Copy, Clone)] | 342 | #[derive(Copy, Clone)] |
| 343 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 337 | pub enum SampleShifting { | 344 | pub enum SampleShifting { |
| 338 | None, | 345 | None, |
| 339 | HalfCycle, | 346 | HalfCycle, |
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 1e20d7cd3..b03cd9009 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -17,6 +17,8 @@ use crate::rcc::{self, RccPeripheral}; | |||
| 17 | use crate::{peripherals, Peri}; | 17 | use crate::{peripherals, Peri}; |
| 18 | 18 | ||
| 19 | /// QSPI transfer configuration. | 19 | /// QSPI transfer configuration. |
| 20 | #[derive(Clone, Copy)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 20 | pub struct TransferConfig { | 22 | pub struct TransferConfig { |
| 21 | /// Instruction width (IMODE) | 23 | /// Instruction width (IMODE) |
| 22 | pub iwidth: QspiWidth, | 24 | pub iwidth: QspiWidth, |
| @@ -46,6 +48,9 @@ impl Default for TransferConfig { | |||
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | /// QSPI driver configuration. | 50 | /// QSPI driver configuration. |
| 51 | #[derive(Clone, Copy)] | ||
| 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 53 | #[non_exhaustive] | ||
| 49 | pub struct Config { | 54 | pub struct Config { |
| 50 | /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. | 55 | /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. |
| 51 | /// If you need other value the whose predefined use `Other` variant. | 56 | /// If you need other value the whose predefined use `Other` variant. |
| @@ -60,6 +65,8 @@ pub struct Config { | |||
| 60 | pub cs_high_time: ChipSelectHighTime, | 65 | pub cs_high_time: ChipSelectHighTime, |
| 61 | /// Shift sampling point of input data (none, or half-cycle) | 66 | /// Shift sampling point of input data (none, or half-cycle) |
| 62 | pub sample_shifting: SampleShifting, | 67 | pub sample_shifting: SampleShifting, |
| 68 | /// GPIO Speed | ||
| 69 | pub gpio_speed: Speed, | ||
| 63 | } | 70 | } |
| 64 | 71 | ||
| 65 | impl Default for Config { | 72 | impl Default for Config { |
| @@ -71,6 +78,7 @@ impl Default for Config { | |||
| 71 | fifo_threshold: FIFOThresholdLevel::_17Bytes, | 78 | fifo_threshold: FIFOThresholdLevel::_17Bytes, |
| 72 | cs_high_time: ChipSelectHighTime::_5Cycle, | 79 | cs_high_time: ChipSelectHighTime::_5Cycle, |
| 73 | sample_shifting: SampleShifting::None, | 80 | sample_shifting: SampleShifting::None, |
| 81 | gpio_speed: Speed::VeryHigh, | ||
| 74 | } | 82 | } |
| 75 | } | 83 | } |
| 76 | } | 84 | } |
| @@ -284,14 +292,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> { | |||
| 284 | ) -> Self { | 292 | ) -> Self { |
| 285 | Self::new_inner( | 293 | Self::new_inner( |
| 286 | peri, | 294 | peri, |
| 287 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 295 | new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 288 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 296 | new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 289 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 297 | new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 290 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 298 | new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 291 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 299 | new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 292 | new_pin!( | 300 | new_pin!( |
| 293 | nss, | 301 | nss, |
| 294 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | 302 | AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) |
| 295 | ), | 303 | ), |
| 296 | None, | 304 | None, |
| 297 | config, | 305 | config, |
| @@ -312,14 +320,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> { | |||
| 312 | ) -> Self { | 320 | ) -> Self { |
| 313 | Self::new_inner( | 321 | Self::new_inner( |
| 314 | peri, | 322 | peri, |
| 315 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 323 | new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 316 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 324 | new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 317 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 325 | new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 318 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 326 | new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 319 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 327 | new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 320 | new_pin!( | 328 | new_pin!( |
| 321 | nss, | 329 | nss, |
| 322 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | 330 | AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) |
| 323 | ), | 331 | ), |
| 324 | None, | 332 | None, |
| 325 | config, | 333 | config, |
| @@ -343,14 +351,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 343 | ) -> Self { | 351 | ) -> Self { |
| 344 | Self::new_inner( | 352 | Self::new_inner( |
| 345 | peri, | 353 | peri, |
| 346 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 354 | new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 347 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 355 | new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 348 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 356 | new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 349 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 357 | new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 350 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 358 | new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 351 | new_pin!( | 359 | new_pin!( |
| 352 | nss, | 360 | nss, |
| 353 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | 361 | AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) |
| 354 | ), | 362 | ), |
| 355 | new_dma!(dma), | 363 | new_dma!(dma), |
| 356 | config, | 364 | config, |
| @@ -372,14 +380,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 372 | ) -> Self { | 380 | ) -> Self { |
| 373 | Self::new_inner( | 381 | Self::new_inner( |
| 374 | peri, | 382 | peri, |
| 375 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 383 | new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 376 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 384 | new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 377 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 385 | new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 378 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 386 | new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 379 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 387 | new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 380 | new_pin!( | 388 | new_pin!( |
| 381 | nss, | 389 | nss, |
| 382 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | 390 | AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up) |
| 383 | ), | 391 | ), |
| 384 | new_dma!(dma), | 392 | new_dma!(dma), |
| 385 | config, | 393 | config, |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 96e628b1a..59ccc8cb5 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -94,7 +94,7 @@ impl<'d, T: McoInstance> Mco<'d, T> { | |||
| 94 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { | 94 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { |
| 95 | critical_section::with(|_| unsafe { | 95 | critical_section::with(|_| unsafe { |
| 96 | T::_apply_clock_settings(source, prescaler); | 96 | T::_apply_clock_settings(source, prescaler); |
| 97 | pin.set_as_af(pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 97 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 98 | }); | 98 | }); |
| 99 | 99 | ||
| 100 | Self { phantom: PhantomData } | 100 | Self { phantom: PhantomData } |
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 4965f8b04..fb8b23b79 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs | |||
| @@ -1,15 +1,14 @@ | |||
| 1 | //! Serial Audio Interface (SAI) | 1 | //! Serial Audio Interface (SAI) |
| 2 | #![macro_use] | 2 | #![macro_use] |
| 3 | #![cfg_attr(gpdma, allow(unused))] | ||
| 4 | 3 | ||
| 5 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 6 | 5 | ||
| 7 | use embassy_hal_internal::PeripheralType; | 6 | use embassy_hal_internal::PeripheralType; |
| 8 | 7 | ||
| 9 | pub use crate::dma::word; | 8 | pub use crate::dma::word; |
| 10 | #[cfg(not(gpdma))] | ||
| 11 | use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; | 9 | use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; |
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 11 | pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider; | ||
| 13 | use crate::pac::sai::{vals, Sai as Regs}; | 12 | use crate::pac::sai::{vals, Sai as Regs}; |
| 14 | use crate::rcc::{self, RccPeripheral}; | 13 | use crate::rcc::{self, RccPeripheral}; |
| 15 | use crate::{peripherals, Peri}; | 14 | use crate::{peripherals, Peri}; |
| @@ -26,7 +25,6 @@ pub enum Error { | |||
| 26 | Overrun, | 25 | Overrun, |
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | #[cfg(not(gpdma))] | ||
| 30 | impl From<ringbuffer::Error> for Error { | 28 | impl From<ringbuffer::Error> for Error { |
| 31 | fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { | 29 | fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { |
| 32 | #[cfg(feature = "defmt")] | 30 | #[cfg(feature = "defmt")] |
| @@ -48,7 +46,6 @@ pub enum Mode { | |||
| 48 | } | 46 | } |
| 49 | 47 | ||
| 50 | impl Mode { | 48 | impl Mode { |
| 51 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 52 | const fn mode(&self, tx_rx: TxRx) -> vals::Mode { | 49 | const fn mode(&self, tx_rx: TxRx) -> vals::Mode { |
| 53 | match tx_rx { | 50 | match tx_rx { |
| 54 | TxRx::Transmitter => match self { | 51 | TxRx::Transmitter => match self { |
| @@ -83,7 +80,6 @@ pub enum SlotSize { | |||
| 83 | } | 80 | } |
| 84 | 81 | ||
| 85 | impl SlotSize { | 82 | impl SlotSize { |
| 86 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 87 | const fn slotsz(&self) -> vals::Slotsz { | 83 | const fn slotsz(&self) -> vals::Slotsz { |
| 88 | match self { | 84 | match self { |
| 89 | SlotSize::DataSize => vals::Slotsz::DATA_SIZE, | 85 | SlotSize::DataSize => vals::Slotsz::DATA_SIZE, |
| @@ -106,7 +102,6 @@ pub enum DataSize { | |||
| 106 | } | 102 | } |
| 107 | 103 | ||
| 108 | impl DataSize { | 104 | impl DataSize { |
| 109 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 110 | const fn ds(&self) -> vals::Ds { | 105 | const fn ds(&self) -> vals::Ds { |
| 111 | match self { | 106 | match self { |
| 112 | DataSize::Data8 => vals::Ds::BIT8, | 107 | DataSize::Data8 => vals::Ds::BIT8, |
| @@ -131,7 +126,6 @@ pub enum FifoThreshold { | |||
| 131 | } | 126 | } |
| 132 | 127 | ||
| 133 | impl FifoThreshold { | 128 | impl FifoThreshold { |
| 134 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 135 | const fn fth(&self) -> vals::Fth { | 129 | const fn fth(&self) -> vals::Fth { |
| 136 | match self { | 130 | match self { |
| 137 | FifoThreshold::Empty => vals::Fth::EMPTY, | 131 | FifoThreshold::Empty => vals::Fth::EMPTY, |
| @@ -152,7 +146,6 @@ pub enum MuteValue { | |||
| 152 | } | 146 | } |
| 153 | 147 | ||
| 154 | impl MuteValue { | 148 | impl MuteValue { |
| 155 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 156 | const fn muteval(&self) -> vals::Muteval { | 149 | const fn muteval(&self) -> vals::Muteval { |
| 157 | match self { | 150 | match self { |
| 158 | MuteValue::Zero => vals::Muteval::SEND_ZERO, | 151 | MuteValue::Zero => vals::Muteval::SEND_ZERO, |
| @@ -171,7 +164,6 @@ pub enum Protocol { | |||
| 171 | } | 164 | } |
| 172 | 165 | ||
| 173 | impl Protocol { | 166 | impl Protocol { |
| 174 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 175 | const fn prtcfg(&self) -> vals::Prtcfg { | 167 | const fn prtcfg(&self) -> vals::Prtcfg { |
| 176 | match self { | 168 | match self { |
| 177 | Protocol::Free => vals::Prtcfg::FREE, | 169 | Protocol::Free => vals::Prtcfg::FREE, |
| @@ -190,7 +182,7 @@ pub enum SyncInput { | |||
| 190 | /// Syncs with the other A/B sub-block within the SAI unit | 182 | /// Syncs with the other A/B sub-block within the SAI unit |
| 191 | Internal, | 183 | Internal, |
| 192 | /// Syncs with a sub-block in the other SAI unit | 184 | /// Syncs with a sub-block in the other SAI unit |
| 193 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 185 | #[cfg(any(sai_v3, sai_v4))] |
| 194 | External(SyncInputInstance), | 186 | External(SyncInputInstance), |
| 195 | } | 187 | } |
| 196 | 188 | ||
| @@ -199,14 +191,14 @@ impl SyncInput { | |||
| 199 | match self { | 191 | match self { |
| 200 | SyncInput::None => vals::Syncen::ASYNCHRONOUS, | 192 | SyncInput::None => vals::Syncen::ASYNCHRONOUS, |
| 201 | SyncInput::Internal => vals::Syncen::INTERNAL, | 193 | SyncInput::Internal => vals::Syncen::INTERNAL, |
| 202 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 194 | #[cfg(any(sai_v3, sai_v4))] |
| 203 | SyncInput::External(_) => vals::Syncen::EXTERNAL, | 195 | SyncInput::External(_) => vals::Syncen::EXTERNAL, |
| 204 | } | 196 | } |
| 205 | } | 197 | } |
| 206 | } | 198 | } |
| 207 | 199 | ||
| 208 | /// SAI instance to sync from. | 200 | /// SAI instance to sync from. |
| 209 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 201 | #[cfg(any(sai_v3, sai_v4))] |
| 210 | #[derive(Copy, Clone, PartialEq)] | 202 | #[derive(Copy, Clone, PartialEq)] |
| 211 | #[allow(missing_docs)] | 203 | #[allow(missing_docs)] |
| 212 | pub enum SyncInputInstance { | 204 | pub enum SyncInputInstance { |
| @@ -229,7 +221,6 @@ pub enum StereoMono { | |||
| 229 | } | 221 | } |
| 230 | 222 | ||
| 231 | impl StereoMono { | 223 | impl StereoMono { |
| 232 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 233 | const fn mono(&self) -> vals::Mono { | 224 | const fn mono(&self) -> vals::Mono { |
| 234 | match self { | 225 | match self { |
| 235 | StereoMono::Stereo => vals::Mono::STEREO, | 226 | StereoMono::Stereo => vals::Mono::STEREO, |
| @@ -248,7 +239,6 @@ pub enum BitOrder { | |||
| 248 | } | 239 | } |
| 249 | 240 | ||
| 250 | impl BitOrder { | 241 | impl BitOrder { |
| 251 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 252 | const fn lsbfirst(&self) -> vals::Lsbfirst { | 242 | const fn lsbfirst(&self) -> vals::Lsbfirst { |
| 253 | match self { | 243 | match self { |
| 254 | BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST, | 244 | BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST, |
| @@ -267,7 +257,6 @@ pub enum FrameSyncOffset { | |||
| 267 | } | 257 | } |
| 268 | 258 | ||
| 269 | impl FrameSyncOffset { | 259 | impl FrameSyncOffset { |
| 270 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 271 | const fn fsoff(&self) -> vals::Fsoff { | 260 | const fn fsoff(&self) -> vals::Fsoff { |
| 272 | match self { | 261 | match self { |
| 273 | FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST, | 262 | FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST, |
| @@ -286,7 +275,6 @@ pub enum FrameSyncPolarity { | |||
| 286 | } | 275 | } |
| 287 | 276 | ||
| 288 | impl FrameSyncPolarity { | 277 | impl FrameSyncPolarity { |
| 289 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 290 | const fn fspol(&self) -> vals::Fspol { | 278 | const fn fspol(&self) -> vals::Fspol { |
| 291 | match self { | 279 | match self { |
| 292 | FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE, | 280 | FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE, |
| @@ -304,7 +292,6 @@ pub enum FrameSyncDefinition { | |||
| 304 | } | 292 | } |
| 305 | 293 | ||
| 306 | impl FrameSyncDefinition { | 294 | impl FrameSyncDefinition { |
| 307 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 308 | const fn fsdef(&self) -> bool { | 295 | const fn fsdef(&self) -> bool { |
| 309 | match self { | 296 | match self { |
| 310 | FrameSyncDefinition::StartOfFrame => false, | 297 | FrameSyncDefinition::StartOfFrame => false, |
| @@ -322,7 +309,6 @@ pub enum ClockStrobe { | |||
| 322 | } | 309 | } |
| 323 | 310 | ||
| 324 | impl ClockStrobe { | 311 | impl ClockStrobe { |
| 325 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 326 | const fn ckstr(&self) -> vals::Ckstr { | 312 | const fn ckstr(&self) -> vals::Ckstr { |
| 327 | match self { | 313 | match self { |
| 328 | ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE, | 314 | ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE, |
| @@ -340,7 +326,6 @@ pub enum ComplementFormat { | |||
| 340 | } | 326 | } |
| 341 | 327 | ||
| 342 | impl ComplementFormat { | 328 | impl ComplementFormat { |
| 343 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 344 | const fn cpl(&self) -> vals::Cpl { | 329 | const fn cpl(&self) -> vals::Cpl { |
| 345 | match self { | 330 | match self { |
| 346 | ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT, | 331 | ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT, |
| @@ -359,7 +344,6 @@ pub enum Companding { | |||
| 359 | } | 344 | } |
| 360 | 345 | ||
| 361 | impl Companding { | 346 | impl Companding { |
| 362 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 363 | const fn comp(&self) -> vals::Comp { | 347 | const fn comp(&self) -> vals::Comp { |
| 364 | match self { | 348 | match self { |
| 365 | Companding::None => vals::Comp::NO_COMPANDING, | 349 | Companding::None => vals::Comp::NO_COMPANDING, |
| @@ -378,7 +362,6 @@ pub enum OutputDrive { | |||
| 378 | } | 362 | } |
| 379 | 363 | ||
| 380 | impl OutputDrive { | 364 | impl OutputDrive { |
| 381 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 382 | const fn outdriv(&self) -> vals::Outdriv { | 365 | const fn outdriv(&self) -> vals::Outdriv { |
| 383 | match self { | 366 | match self { |
| 384 | OutputDrive::OnStart => vals::Outdriv::ON_START, | 367 | OutputDrive::OnStart => vals::Outdriv::ON_START, |
| @@ -387,196 +370,6 @@ impl OutputDrive { | |||
| 387 | } | 370 | } |
| 388 | } | 371 | } |
| 389 | 372 | ||
| 390 | /// Master clock divider. | ||
| 391 | #[derive(Copy, Clone, PartialEq)] | ||
| 392 | #[allow(missing_docs)] | ||
| 393 | #[cfg(any(sai_v1, sai_v2))] | ||
| 394 | pub enum MasterClockDivider { | ||
| 395 | MasterClockDisabled, | ||
| 396 | Div1, | ||
| 397 | Div2, | ||
| 398 | Div4, | ||
| 399 | Div6, | ||
| 400 | Div8, | ||
| 401 | Div10, | ||
| 402 | Div12, | ||
| 403 | Div14, | ||
| 404 | Div16, | ||
| 405 | Div18, | ||
| 406 | Div20, | ||
| 407 | Div22, | ||
| 408 | Div24, | ||
| 409 | Div26, | ||
| 410 | Div28, | ||
| 411 | Div30, | ||
| 412 | } | ||
| 413 | |||
| 414 | /// Master clock divider. | ||
| 415 | #[derive(Copy, Clone, PartialEq)] | ||
| 416 | #[allow(missing_docs)] | ||
| 417 | #[cfg(any(sai_v1_4pdm, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 418 | pub enum MasterClockDivider { | ||
| 419 | MasterClockDisabled, | ||
| 420 | Div1, | ||
| 421 | Div2, | ||
| 422 | Div3, | ||
| 423 | Div4, | ||
| 424 | Div5, | ||
| 425 | Div6, | ||
| 426 | Div7, | ||
| 427 | Div8, | ||
| 428 | Div9, | ||
| 429 | Div10, | ||
| 430 | Div11, | ||
| 431 | Div12, | ||
| 432 | Div13, | ||
| 433 | Div14, | ||
| 434 | Div15, | ||
| 435 | Div16, | ||
| 436 | Div17, | ||
| 437 | Div18, | ||
| 438 | Div19, | ||
| 439 | Div20, | ||
| 440 | Div21, | ||
| 441 | Div22, | ||
| 442 | Div23, | ||
| 443 | Div24, | ||
| 444 | Div25, | ||
| 445 | Div26, | ||
| 446 | Div27, | ||
| 447 | Div28, | ||
| 448 | Div29, | ||
| 449 | Div30, | ||
| 450 | Div31, | ||
| 451 | Div32, | ||
| 452 | Div33, | ||
| 453 | Div34, | ||
| 454 | Div35, | ||
| 455 | Div36, | ||
| 456 | Div37, | ||
| 457 | Div38, | ||
| 458 | Div39, | ||
| 459 | Div40, | ||
| 460 | Div41, | ||
| 461 | Div42, | ||
| 462 | Div43, | ||
| 463 | Div44, | ||
| 464 | Div45, | ||
| 465 | Div46, | ||
| 466 | Div47, | ||
| 467 | Div48, | ||
| 468 | Div49, | ||
| 469 | Div50, | ||
| 470 | Div51, | ||
| 471 | Div52, | ||
| 472 | Div53, | ||
| 473 | Div54, | ||
| 474 | Div55, | ||
| 475 | Div56, | ||
| 476 | Div57, | ||
| 477 | Div58, | ||
| 478 | Div59, | ||
| 479 | Div60, | ||
| 480 | Div61, | ||
| 481 | Div62, | ||
| 482 | Div63, | ||
| 483 | } | ||
| 484 | |||
| 485 | impl MasterClockDivider { | ||
| 486 | #[cfg(any(sai_v1, sai_v2))] | ||
| 487 | const fn mckdiv(&self) -> u8 { | ||
| 488 | match self { | ||
| 489 | MasterClockDivider::MasterClockDisabled => 0, | ||
| 490 | MasterClockDivider::Div1 => 0, | ||
| 491 | MasterClockDivider::Div2 => 1, | ||
| 492 | MasterClockDivider::Div4 => 2, | ||
| 493 | MasterClockDivider::Div6 => 3, | ||
| 494 | MasterClockDivider::Div8 => 4, | ||
| 495 | MasterClockDivider::Div10 => 5, | ||
| 496 | MasterClockDivider::Div12 => 6, | ||
| 497 | MasterClockDivider::Div14 => 7, | ||
| 498 | MasterClockDivider::Div16 => 8, | ||
| 499 | MasterClockDivider::Div18 => 9, | ||
| 500 | MasterClockDivider::Div20 => 10, | ||
| 501 | MasterClockDivider::Div22 => 11, | ||
| 502 | MasterClockDivider::Div24 => 12, | ||
| 503 | MasterClockDivider::Div26 => 13, | ||
| 504 | MasterClockDivider::Div28 => 14, | ||
| 505 | MasterClockDivider::Div30 => 15, | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | #[cfg(any(sai_v1_4pdm, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 510 | const fn mckdiv(&self) -> u8 { | ||
| 511 | match self { | ||
| 512 | MasterClockDivider::MasterClockDisabled => 0, | ||
| 513 | MasterClockDivider::Div1 => 1, | ||
| 514 | MasterClockDivider::Div2 => 2, | ||
| 515 | MasterClockDivider::Div3 => 3, | ||
| 516 | MasterClockDivider::Div4 => 4, | ||
| 517 | MasterClockDivider::Div5 => 5, | ||
| 518 | MasterClockDivider::Div6 => 6, | ||
| 519 | MasterClockDivider::Div7 => 7, | ||
| 520 | MasterClockDivider::Div8 => 8, | ||
| 521 | MasterClockDivider::Div9 => 9, | ||
| 522 | MasterClockDivider::Div10 => 10, | ||
| 523 | MasterClockDivider::Div11 => 11, | ||
| 524 | MasterClockDivider::Div12 => 12, | ||
| 525 | MasterClockDivider::Div13 => 13, | ||
| 526 | MasterClockDivider::Div14 => 14, | ||
| 527 | MasterClockDivider::Div15 => 15, | ||
| 528 | MasterClockDivider::Div16 => 16, | ||
| 529 | MasterClockDivider::Div17 => 17, | ||
| 530 | MasterClockDivider::Div18 => 18, | ||
| 531 | MasterClockDivider::Div19 => 19, | ||
| 532 | MasterClockDivider::Div20 => 20, | ||
| 533 | MasterClockDivider::Div21 => 21, | ||
| 534 | MasterClockDivider::Div22 => 22, | ||
| 535 | MasterClockDivider::Div23 => 23, | ||
| 536 | MasterClockDivider::Div24 => 24, | ||
| 537 | MasterClockDivider::Div25 => 25, | ||
| 538 | MasterClockDivider::Div26 => 26, | ||
| 539 | MasterClockDivider::Div27 => 27, | ||
| 540 | MasterClockDivider::Div28 => 28, | ||
| 541 | MasterClockDivider::Div29 => 29, | ||
| 542 | MasterClockDivider::Div30 => 30, | ||
| 543 | MasterClockDivider::Div31 => 31, | ||
| 544 | MasterClockDivider::Div32 => 32, | ||
| 545 | MasterClockDivider::Div33 => 33, | ||
| 546 | MasterClockDivider::Div34 => 34, | ||
| 547 | MasterClockDivider::Div35 => 35, | ||
| 548 | MasterClockDivider::Div36 => 36, | ||
| 549 | MasterClockDivider::Div37 => 37, | ||
| 550 | MasterClockDivider::Div38 => 38, | ||
| 551 | MasterClockDivider::Div39 => 39, | ||
| 552 | MasterClockDivider::Div40 => 40, | ||
| 553 | MasterClockDivider::Div41 => 41, | ||
| 554 | MasterClockDivider::Div42 => 42, | ||
| 555 | MasterClockDivider::Div43 => 43, | ||
| 556 | MasterClockDivider::Div44 => 44, | ||
| 557 | MasterClockDivider::Div45 => 45, | ||
| 558 | MasterClockDivider::Div46 => 46, | ||
| 559 | MasterClockDivider::Div47 => 47, | ||
| 560 | MasterClockDivider::Div48 => 48, | ||
| 561 | MasterClockDivider::Div49 => 49, | ||
| 562 | MasterClockDivider::Div50 => 50, | ||
| 563 | MasterClockDivider::Div51 => 51, | ||
| 564 | MasterClockDivider::Div52 => 52, | ||
| 565 | MasterClockDivider::Div53 => 53, | ||
| 566 | MasterClockDivider::Div54 => 54, | ||
| 567 | MasterClockDivider::Div55 => 55, | ||
| 568 | MasterClockDivider::Div56 => 56, | ||
| 569 | MasterClockDivider::Div57 => 57, | ||
| 570 | MasterClockDivider::Div58 => 58, | ||
| 571 | MasterClockDivider::Div59 => 59, | ||
| 572 | MasterClockDivider::Div60 => 60, | ||
| 573 | MasterClockDivider::Div61 => 61, | ||
| 574 | MasterClockDivider::Div62 => 62, | ||
| 575 | MasterClockDivider::Div63 => 63, | ||
| 576 | } | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | /// [`SAI`] configuration. | 373 | /// [`SAI`] configuration. |
| 581 | #[allow(missing_docs)] | 374 | #[allow(missing_docs)] |
| 582 | #[non_exhaustive] | 375 | #[non_exhaustive] |
| @@ -601,8 +394,7 @@ pub struct Config { | |||
| 601 | pub frame_length: u8, | 394 | pub frame_length: u8, |
| 602 | pub clock_strobe: ClockStrobe, | 395 | pub clock_strobe: ClockStrobe, |
| 603 | pub output_drive: OutputDrive, | 396 | pub output_drive: OutputDrive, |
| 604 | pub master_clock_divider: MasterClockDivider, | 397 | pub master_clock_divider: Option<MasterClockDivider>, |
| 605 | pub nodiv: bool, | ||
| 606 | pub is_high_impedance_on_inactive_slot: bool, | 398 | pub is_high_impedance_on_inactive_slot: bool, |
| 607 | pub fifo_threshold: FifoThreshold, | 399 | pub fifo_threshold: FifoThreshold, |
| 608 | pub companding: Companding, | 400 | pub companding: Companding, |
| @@ -631,8 +423,7 @@ impl Default for Config { | |||
| 631 | frame_sync_active_level_length: word::U7(16), | 423 | frame_sync_active_level_length: word::U7(16), |
| 632 | frame_sync_definition: FrameSyncDefinition::ChannelIdentification, | 424 | frame_sync_definition: FrameSyncDefinition::ChannelIdentification, |
| 633 | frame_length: 32, | 425 | frame_length: 32, |
| 634 | master_clock_divider: MasterClockDivider::MasterClockDisabled, | 426 | master_clock_divider: None, |
| 635 | nodiv: false, | ||
| 636 | clock_strobe: ClockStrobe::Rising, | 427 | clock_strobe: ClockStrobe::Rising, |
| 637 | output_drive: OutputDrive::Immediately, | 428 | output_drive: OutputDrive::Immediately, |
| 638 | is_high_impedance_on_inactive_slot: false, | 429 | is_high_impedance_on_inactive_slot: false, |
| @@ -652,7 +443,6 @@ impl Config { | |||
| 652 | } | 443 | } |
| 653 | } | 444 | } |
| 654 | 445 | ||
| 655 | #[cfg(not(gpdma))] | ||
| 656 | enum RingBuffer<'d, W: word::Word> { | 446 | enum RingBuffer<'d, W: word::Word> { |
| 657 | Writable(WritableRingBuffer<'d, W>), | 447 | Writable(WritableRingBuffer<'d, W>), |
| 658 | Readable(ReadableRingBuffer<'d, W>), | 448 | Readable(ReadableRingBuffer<'d, W>), |
| @@ -679,7 +469,6 @@ fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) { | |||
| 679 | ) | 469 | ) |
| 680 | } | 470 | } |
| 681 | 471 | ||
| 682 | #[cfg(not(gpdma))] | ||
| 683 | fn get_ring_buffer<'d, T: Instance, W: word::Word>( | 472 | fn get_ring_buffer<'d, T: Instance, W: word::Word>( |
| 684 | dma: Peri<'d, impl Channel>, | 473 | dma: Peri<'d, impl Channel>, |
| 685 | dma_buf: &'d mut [W], | 474 | dma_buf: &'d mut [W], |
| @@ -711,7 +500,7 @@ fn update_synchronous_config(config: &mut Config) { | |||
| 711 | config.sync_input = SyncInput::Internal; | 500 | config.sync_input = SyncInput::Internal; |
| 712 | } | 501 | } |
| 713 | 502 | ||
| 714 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 503 | #[cfg(any(sai_v3, sai_v4))] |
| 715 | { | 504 | { |
| 716 | //this must either be Internal or External | 505 | //this must either be Internal or External |
| 717 | //The asynchronous sub-block on the same SAI needs to enable sync_output | 506 | //The asynchronous sub-block on the same SAI needs to enable sync_output |
| @@ -750,14 +539,10 @@ pub struct Sai<'d, T: Instance, W: word::Word> { | |||
| 750 | fs: Option<Peri<'d, AnyPin>>, | 539 | fs: Option<Peri<'d, AnyPin>>, |
| 751 | sck: Option<Peri<'d, AnyPin>>, | 540 | sck: Option<Peri<'d, AnyPin>>, |
| 752 | mclk: Option<Peri<'d, AnyPin>>, | 541 | mclk: Option<Peri<'d, AnyPin>>, |
| 753 | #[cfg(gpdma)] | ||
| 754 | ring_buffer: PhantomData<W>, | ||
| 755 | #[cfg(not(gpdma))] | ||
| 756 | ring_buffer: RingBuffer<'d, W>, | 542 | ring_buffer: RingBuffer<'d, W>, |
| 757 | sub_block: WhichSubBlock, | 543 | sub_block: WhichSubBlock, |
| 758 | } | 544 | } |
| 759 | 545 | ||
| 760 | #[cfg(not(gpdma))] | ||
| 761 | impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | 546 | impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { |
| 762 | /// Create a new SAI driver in asynchronous mode with MCLK. | 547 | /// Create a new SAI driver in asynchronous mode with MCLK. |
| 763 | /// | 548 | /// |
| @@ -770,14 +555,10 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 770 | mclk: Peri<'d, impl MclkPin<T, S>>, | 555 | mclk: Peri<'d, impl MclkPin<T, S>>, |
| 771 | dma: Peri<'d, impl Channel + Dma<T, S>>, | 556 | dma: Peri<'d, impl Channel + Dma<T, S>>, |
| 772 | dma_buf: &'d mut [W], | 557 | dma_buf: &'d mut [W], |
| 773 | mut config: Config, | 558 | config: Config, |
| 774 | ) -> Self { | 559 | ) -> Self { |
| 775 | let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); | 560 | let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); |
| 776 | mclk.set_as_af(mclk.af_num(), ck_af_type); | 561 | set_as_af!(mclk, ck_af_type); |
| 777 | |||
| 778 | if config.master_clock_divider == MasterClockDivider::MasterClockDisabled { | ||
| 779 | config.master_clock_divider = MasterClockDivider::Div1; | ||
| 780 | } | ||
| 781 | 562 | ||
| 782 | Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config) | 563 | Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config) |
| 783 | } | 564 | } |
| @@ -797,9 +578,9 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 797 | let peri = peri.peri; | 578 | let peri = peri.peri; |
| 798 | 579 | ||
| 799 | let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); | 580 | let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); |
| 800 | sd.set_as_af(sd.af_num(), sd_af_type); | 581 | set_as_af!(sd, sd_af_type); |
| 801 | sck.set_as_af(sck.af_num(), ck_af_type); | 582 | set_as_af!(sck, ck_af_type); |
| 802 | fs.set_as_af(fs.af_num(), ck_af_type); | 583 | set_as_af!(fs, ck_af_type); |
| 803 | 584 | ||
| 804 | let sub_block = S::WHICH; | 585 | let sub_block = S::WHICH; |
| 805 | let request = dma.request(); | 586 | let request = dma.request(); |
| @@ -831,7 +612,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 831 | let peri = peri.peri; | 612 | let peri = peri.peri; |
| 832 | 613 | ||
| 833 | let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); | 614 | let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); |
| 834 | sd.set_as_af(sd.af_num(), sd_af_type); | 615 | set_as_af!(sd, sd_af_type); |
| 835 | 616 | ||
| 836 | let sub_block = S::WHICH; | 617 | let sub_block = S::WHICH; |
| 837 | let request = dma.request(); | 618 | let request = dma.request(); |
| @@ -860,14 +641,11 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 860 | ) -> Self { | 641 | ) -> Self { |
| 861 | let ch = T::REGS.ch(sub_block as usize); | 642 | let ch = T::REGS.ch(sub_block as usize); |
| 862 | 643 | ||
| 863 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 644 | ch.cr1().modify(|w| w.set_saien(false)); |
| 864 | { | ||
| 865 | ch.cr1().modify(|w| w.set_saien(false)); | ||
| 866 | } | ||
| 867 | 645 | ||
| 868 | ch.cr2().modify(|w| w.set_fflush(true)); | 646 | ch.cr2().modify(|w| w.set_fflush(true)); |
| 869 | 647 | ||
| 870 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 648 | #[cfg(any(sai_v3, sai_v4))] |
| 871 | { | 649 | { |
| 872 | if let SyncInput::External(i) = config.sync_input { | 650 | if let SyncInput::External(i) = config.sync_input { |
| 873 | T::REGS.gcr().modify(|w| { | 651 | T::REGS.gcr().modify(|w| { |
| @@ -886,55 +664,52 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 886 | } | 664 | } |
| 887 | } | 665 | } |
| 888 | 666 | ||
| 889 | #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 667 | ch.cr1().modify(|w| { |
| 890 | { | 668 | w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { |
| 891 | ch.cr1().modify(|w| { | 669 | TxRx::Transmitter |
| 892 | w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { | 670 | } else { |
| 893 | TxRx::Transmitter | 671 | TxRx::Receiver |
| 894 | } else { | 672 | })); |
| 895 | TxRx::Receiver | 673 | w.set_prtcfg(config.protocol.prtcfg()); |
| 896 | })); | 674 | w.set_ds(config.data_size.ds()); |
| 897 | w.set_prtcfg(config.protocol.prtcfg()); | 675 | w.set_lsbfirst(config.bit_order.lsbfirst()); |
| 898 | w.set_ds(config.data_size.ds()); | 676 | w.set_ckstr(config.clock_strobe.ckstr()); |
| 899 | w.set_lsbfirst(config.bit_order.lsbfirst()); | 677 | w.set_syncen(config.sync_input.syncen()); |
| 900 | w.set_ckstr(config.clock_strobe.ckstr()); | 678 | w.set_mono(config.stereo_mono.mono()); |
| 901 | w.set_syncen(config.sync_input.syncen()); | 679 | w.set_outdriv(config.output_drive.outdriv()); |
| 902 | w.set_mono(config.stereo_mono.mono()); | 680 | w.set_mckdiv(config.master_clock_divider.unwrap_or(MasterClockDivider::DIV1)); |
| 903 | w.set_outdriv(config.output_drive.outdriv()); | 681 | w.set_nodiv(config.master_clock_divider.is_none()); |
| 904 | w.set_mckdiv(config.master_clock_divider.mckdiv().into()); | 682 | w.set_dmaen(true); |
| 905 | w.set_nodiv(config.nodiv); | 683 | }); |
| 906 | w.set_dmaen(true); | 684 | |
| 907 | }); | 685 | ch.cr2().modify(|w| { |
| 908 | 686 | w.set_fth(config.fifo_threshold.fth()); | |
| 909 | ch.cr2().modify(|w| { | 687 | w.set_comp(config.companding.comp()); |
| 910 | w.set_fth(config.fifo_threshold.fth()); | 688 | w.set_cpl(config.complement_format.cpl()); |
| 911 | w.set_comp(config.companding.comp()); | 689 | w.set_muteval(config.mute_value.muteval()); |
| 912 | w.set_cpl(config.complement_format.cpl()); | 690 | w.set_mutecnt(config.mute_detection_counter.0 as u8); |
| 913 | w.set_muteval(config.mute_value.muteval()); | 691 | w.set_tris(config.is_high_impedance_on_inactive_slot); |
| 914 | w.set_mutecnt(config.mute_detection_counter.0 as u8); | 692 | }); |
| 915 | w.set_tris(config.is_high_impedance_on_inactive_slot); | 693 | |
| 916 | }); | 694 | ch.frcr().modify(|w| { |
| 917 | 695 | w.set_fsoff(config.frame_sync_offset.fsoff()); | |
| 918 | ch.frcr().modify(|w| { | 696 | w.set_fspol(config.frame_sync_polarity.fspol()); |
| 919 | w.set_fsoff(config.frame_sync_offset.fsoff()); | 697 | w.set_fsdef(config.frame_sync_definition.fsdef()); |
| 920 | w.set_fspol(config.frame_sync_polarity.fspol()); | 698 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); |
| 921 | w.set_fsdef(config.frame_sync_definition.fsdef()); | 699 | w.set_frl(config.frame_length - 1); |
| 922 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); | 700 | }); |
| 923 | w.set_frl(config.frame_length - 1); | 701 | |
| 924 | }); | 702 | ch.slotr().modify(|w| { |
| 925 | 703 | w.set_nbslot(config.slot_count.0 as u8 - 1); | |
| 926 | ch.slotr().modify(|w| { | 704 | w.set_slotsz(config.slot_size.slotsz()); |
| 927 | w.set_nbslot(config.slot_count.0 as u8 - 1); | 705 | w.set_fboff(config.first_bit_offset.0 as u8); |
| 928 | w.set_slotsz(config.slot_size.slotsz()); | 706 | w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16)); |
| 929 | w.set_fboff(config.first_bit_offset.0 as u8); | 707 | }); |
| 930 | w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16)); | 708 | |
| 931 | }); | 709 | ch.cr1().modify(|w| w.set_saien(true)); |
| 932 | 710 | ||
| 933 | ch.cr1().modify(|w| w.set_saien(true)); | 711 | if ch.cr1().read().saien() == false { |
| 934 | 712 | panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)"); | |
| 935 | if ch.cr1().read().saien() == false { | ||
| 936 | panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)"); | ||
| 937 | } | ||
| 938 | } | 713 | } |
| 939 | 714 | ||
| 940 | Self { | 715 | Self { |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 6e5d735d7..ccbd16cbf 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -428,9 +428,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 428 | config: Config, | 428 | config: Config, |
| 429 | ) -> Self { | 429 | ) -> Self { |
| 430 | critical_section::with(|_| { | 430 | critical_section::with(|_| { |
| 431 | clk.set_as_af(clk.af_num(), CLK_AF); | 431 | set_as_af!(clk, CLK_AF); |
| 432 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 432 | set_as_af!(cmd, CMD_AF); |
| 433 | d0.set_as_af(d0.af_num(), DATA_AF); | 433 | set_as_af!(d0, DATA_AF); |
| 434 | }); | 434 | }); |
| 435 | 435 | ||
| 436 | Self::new_inner( | 436 | Self::new_inner( |
| @@ -464,12 +464,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 464 | config: Config, | 464 | config: Config, |
| 465 | ) -> Self { | 465 | ) -> Self { |
| 466 | critical_section::with(|_| { | 466 | critical_section::with(|_| { |
| 467 | clk.set_as_af(clk.af_num(), CLK_AF); | 467 | set_as_af!(clk, CLK_AF); |
| 468 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 468 | set_as_af!(cmd, CMD_AF); |
| 469 | d0.set_as_af(d0.af_num(), DATA_AF); | 469 | set_as_af!(d0, DATA_AF); |
| 470 | d1.set_as_af(d1.af_num(), DATA_AF); | 470 | set_as_af!(d1, DATA_AF); |
| 471 | d2.set_as_af(d2.af_num(), DATA_AF); | 471 | set_as_af!(d2, DATA_AF); |
| 472 | d3.set_as_af(d3.af_num(), DATA_AF); | 472 | set_as_af!(d3, DATA_AF); |
| 473 | }); | 473 | }); |
| 474 | 474 | ||
| 475 | Self::new_inner( | 475 | Self::new_inner( |
| @@ -510,16 +510,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 510 | config: Config, | 510 | config: Config, |
| 511 | ) -> Self { | 511 | ) -> Self { |
| 512 | critical_section::with(|_| { | 512 | critical_section::with(|_| { |
| 513 | clk.set_as_af(clk.af_num(), CLK_AF); | 513 | set_as_af!(clk, CLK_AF); |
| 514 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 514 | set_as_af!(cmd, CMD_AF); |
| 515 | d0.set_as_af(d0.af_num(), DATA_AF); | 515 | set_as_af!(d0, DATA_AF); |
| 516 | d1.set_as_af(d1.af_num(), DATA_AF); | 516 | set_as_af!(d1, DATA_AF); |
| 517 | d2.set_as_af(d2.af_num(), DATA_AF); | 517 | set_as_af!(d2, DATA_AF); |
| 518 | d3.set_as_af(d3.af_num(), DATA_AF); | 518 | set_as_af!(d3, DATA_AF); |
| 519 | d4.set_as_af(d4.af_num(), DATA_AF); | 519 | set_as_af!(d4, DATA_AF); |
| 520 | d5.set_as_af(d5.af_num(), DATA_AF); | 520 | set_as_af!(d5, DATA_AF); |
| 521 | d6.set_as_af(d6.af_num(), DATA_AF); | 521 | set_as_af!(d6, DATA_AF); |
| 522 | d7.set_as_af(d7.af_num(), DATA_AF); | 522 | set_as_af!(d7, DATA_AF); |
| 523 | }); | 523 | }); |
| 524 | 524 | ||
| 525 | Self::new_inner( | 525 | Self::new_inner( |
| @@ -552,9 +552,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 552 | config: Config, | 552 | config: Config, |
| 553 | ) -> Self { | 553 | ) -> Self { |
| 554 | critical_section::with(|_| { | 554 | critical_section::with(|_| { |
| 555 | clk.set_as_af(clk.af_num(), CLK_AF); | 555 | set_as_af!(clk, CLK_AF); |
| 556 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 556 | set_as_af!(cmd, CMD_AF); |
| 557 | d0.set_as_af(d0.af_num(), DATA_AF); | 557 | set_as_af!(d0, DATA_AF); |
| 558 | }); | 558 | }); |
| 559 | 559 | ||
| 560 | Self::new_inner( | 560 | Self::new_inner( |
| @@ -586,12 +586,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 586 | config: Config, | 586 | config: Config, |
| 587 | ) -> Self { | 587 | ) -> Self { |
| 588 | critical_section::with(|_| { | 588 | critical_section::with(|_| { |
| 589 | clk.set_as_af(clk.af_num(), CLK_AF); | 589 | set_as_af!(clk, CLK_AF); |
| 590 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 590 | set_as_af!(cmd, CMD_AF); |
| 591 | d0.set_as_af(d0.af_num(), DATA_AF); | 591 | set_as_af!(d0, DATA_AF); |
| 592 | d1.set_as_af(d1.af_num(), DATA_AF); | 592 | set_as_af!(d1, DATA_AF); |
| 593 | d2.set_as_af(d2.af_num(), DATA_AF); | 593 | set_as_af!(d2, DATA_AF); |
| 594 | d3.set_as_af(d3.af_num(), DATA_AF); | 594 | set_as_af!(d3, DATA_AF); |
| 595 | }); | 595 | }); |
| 596 | 596 | ||
| 597 | Self::new_inner( | 597 | Self::new_inner( |
| @@ -630,16 +630,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 630 | config: Config, | 630 | config: Config, |
| 631 | ) -> Self { | 631 | ) -> Self { |
| 632 | critical_section::with(|_| { | 632 | critical_section::with(|_| { |
| 633 | clk.set_as_af(clk.af_num(), CLK_AF); | 633 | set_as_af!(clk, CLK_AF); |
| 634 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 634 | set_as_af!(cmd, CMD_AF); |
| 635 | d0.set_as_af(d0.af_num(), DATA_AF); | 635 | set_as_af!(d0, DATA_AF); |
| 636 | d1.set_as_af(d1.af_num(), DATA_AF); | 636 | set_as_af!(d1, DATA_AF); |
| 637 | d2.set_as_af(d2.af_num(), DATA_AF); | 637 | set_as_af!(d2, DATA_AF); |
| 638 | d3.set_as_af(d3.af_num(), DATA_AF); | 638 | set_as_af!(d3, DATA_AF); |
| 639 | d4.set_as_af(d4.af_num(), DATA_AF); | 639 | set_as_af!(d4, DATA_AF); |
| 640 | d5.set_as_af(d5.af_num(), DATA_AF); | 640 | set_as_af!(d5, DATA_AF); |
| 641 | d6.set_as_af(d6.af_num(), DATA_AF); | 641 | set_as_af!(d6, DATA_AF); |
| 642 | d7.set_as_af(d7.af_num(), DATA_AF); | 642 | set_as_af!(d7, DATA_AF); |
| 643 | }); | 643 | }); |
| 644 | 644 | ||
| 645 | Self::new_inner( | 645 | Self::new_inner( |
diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index 9c42217f0..b0a32d5d1 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs | |||
| @@ -8,9 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; | |||
| 8 | 8 | ||
| 9 | use crate::dma::ringbuffer::Error as RingbufferError; | 9 | use crate::dma::ringbuffer::Error as RingbufferError; |
| 10 | pub use crate::dma::word; | 10 | pub use crate::dma::word; |
| 11 | #[cfg(not(gpdma))] | 11 | use crate::dma::{Channel, ReadableRingBuffer, TransferOptions}; |
| 12 | use crate::dma::ReadableRingBuffer; | ||
| 13 | use crate::dma::{Channel, TransferOptions}; | ||
| 14 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; | 12 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; |
| 15 | use crate::interrupt::typelevel::Interrupt; | 13 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::pac::spdifrx::Spdifrx as Regs; | 14 | use crate::pac::spdifrx::Spdifrx as Regs; |
| @@ -37,7 +35,7 @@ macro_rules! new_spdifrx_pin { | |||
| 37 | ($name:ident, $af_type:expr) => {{ | 35 | ($name:ident, $af_type:expr) => {{ |
| 38 | let pin = $name; | 36 | let pin = $name; |
| 39 | let input_sel = pin.input_sel(); | 37 | let input_sel = pin.input_sel(); |
| 40 | pin.set_as_af(pin.af_num(), $af_type); | 38 | set_as_af!(pin, $af_type); |
| 41 | (Some(pin.into()), input_sel) | 39 | (Some(pin.into()), input_sel) |
| 42 | }}; | 40 | }}; |
| 43 | } | 41 | } |
| @@ -58,7 +56,6 @@ macro_rules! impl_spdifrx_pin { | |||
| 58 | /// Ring-buffered SPDIFRX driver. | 56 | /// Ring-buffered SPDIFRX driver. |
| 59 | /// | 57 | /// |
| 60 | /// Data is read by DMAs and stored in a ring buffer. | 58 | /// Data is read by DMAs and stored in a ring buffer. |
| 61 | #[cfg(not(gpdma))] | ||
| 62 | pub struct Spdifrx<'d, T: Instance> { | 59 | pub struct Spdifrx<'d, T: Instance> { |
| 63 | _peri: Peri<'d, T>, | 60 | _peri: Peri<'d, T>, |
| 64 | spdifrx_in: Option<Peri<'d, AnyPin>>, | 61 | spdifrx_in: Option<Peri<'d, AnyPin>>, |
| @@ -118,7 +115,6 @@ impl Default for Config { | |||
| 118 | } | 115 | } |
| 119 | } | 116 | } |
| 120 | 117 | ||
| 121 | #[cfg(not(gpdma))] | ||
| 122 | impl<'d, T: Instance> Spdifrx<'d, T> { | 118 | impl<'d, T: Instance> Spdifrx<'d, T> { |
| 123 | fn dma_opts() -> TransferOptions { | 119 | fn dma_opts() -> TransferOptions { |
| 124 | TransferOptions { | 120 | TransferOptions { |
| @@ -236,7 +232,6 @@ impl<'d, T: Instance> Spdifrx<'d, T> { | |||
| 236 | } | 232 | } |
| 237 | } | 233 | } |
| 238 | 234 | ||
| 239 | #[cfg(not(gpdma))] | ||
| 240 | impl<'d, T: Instance> Drop for Spdifrx<'d, T> { | 235 | impl<'d, T: Instance> Drop for Spdifrx<'d, T> { |
| 241 | fn drop(&mut self) { | 236 | fn drop(&mut self) { |
| 242 | T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00)); | 237 | T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00)); |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 4c5308eba..c5373a54d 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -174,7 +174,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 174 | self.info.rcc.enable_and_reset(); | 174 | self.info.rcc.enable_and_reset(); |
| 175 | 175 | ||
| 176 | let regs = self.info.regs; | 176 | let regs = self.info.regs; |
| 177 | #[cfg(any(spi_v1, spi_f1))] | 177 | #[cfg(any(spi_v1, spi_v2))] |
| 178 | { | 178 | { |
| 179 | regs.cr2().modify(|w| { | 179 | regs.cr2().modify(|w| { |
| 180 | w.set_ssoe(false); | 180 | w.set_ssoe(false); |
| @@ -198,7 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 198 | w.set_dff(<u8 as SealedWord>::CONFIG) | 198 | w.set_dff(<u8 as SealedWord>::CONFIG) |
| 199 | }); | 199 | }); |
| 200 | } | 200 | } |
| 201 | #[cfg(spi_v2)] | 201 | #[cfg(spi_v3)] |
| 202 | { | 202 | { |
| 203 | regs.cr2().modify(|w| { | 203 | regs.cr2().modify(|w| { |
| 204 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; | 204 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; |
| @@ -220,7 +220,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 220 | w.set_spe(true); | 220 | w.set_spe(true); |
| 221 | }); | 221 | }); |
| 222 | } | 222 | } |
| 223 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 223 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 224 | { | 224 | { |
| 225 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); | 225 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); |
| 226 | regs.cfg2().modify(|w| { | 226 | regs.cfg2().modify(|w| { |
| @@ -274,7 +274,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 274 | } | 274 | } |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | #[cfg(any(spi_v1, spi_f1, spi_v2))] | 277 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 278 | self.info.regs.cr1().modify(|w| { | 278 | self.info.regs.cr1().modify(|w| { |
| 279 | w.set_cpha(cpha); | 279 | w.set_cpha(cpha); |
| 280 | w.set_cpol(cpol); | 280 | w.set_cpol(cpol); |
| @@ -282,7 +282,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 282 | w.set_lsbfirst(lsbfirst); | 282 | w.set_lsbfirst(lsbfirst); |
| 283 | }); | 283 | }); |
| 284 | 284 | ||
| 285 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 285 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 286 | { | 286 | { |
| 287 | self.info.regs.cr1().modify(|w| { | 287 | self.info.regs.cr1().modify(|w| { |
| 288 | w.set_spe(false); | 288 | w.set_spe(false); |
| @@ -306,11 +306,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 306 | 306 | ||
| 307 | /// Get current SPI configuration. | 307 | /// Get current SPI configuration. |
| 308 | pub fn get_current_config(&self) -> Config { | 308 | pub fn get_current_config(&self) -> Config { |
| 309 | #[cfg(any(spi_v1, spi_f1, spi_v2))] | 309 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 310 | let cfg = self.info.regs.cr1().read(); | 310 | let cfg = self.info.regs.cr1().read(); |
| 311 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 311 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 312 | let cfg = self.info.regs.cfg2().read(); | 312 | let cfg = self.info.regs.cfg2().read(); |
| 313 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 313 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 314 | let cfg1 = self.info.regs.cfg1().read(); | 314 | let cfg1 = self.info.regs.cfg1().read(); |
| 315 | 315 | ||
| 316 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { | 316 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { |
| @@ -335,9 +335,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 335 | Some(pin) => pin.pull(), | 335 | Some(pin) => pin.pull(), |
| 336 | }; | 336 | }; |
| 337 | 337 | ||
| 338 | #[cfg(any(spi_v1, spi_f1, spi_v2))] | 338 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 339 | let br = cfg.br(); | 339 | let br = cfg.br(); |
| 340 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 340 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 341 | let br = cfg1.mbr(); | 341 | let br = cfg1.mbr(); |
| 342 | 342 | ||
| 343 | let frequency = compute_frequency(self.kernel_clock, br); | 343 | let frequency = compute_frequency(self.kernel_clock, br); |
| @@ -360,16 +360,16 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 360 | w.set_spe(false); | 360 | w.set_spe(false); |
| 361 | }); | 361 | }); |
| 362 | 362 | ||
| 363 | #[cfg(any(spi_v1, spi_f1))] | 363 | #[cfg(any(spi_v1, spi_v2))] |
| 364 | self.info.regs.cr1().modify(|reg| { | 364 | self.info.regs.cr1().modify(|reg| { |
| 365 | reg.set_dff(word_size); | 365 | reg.set_dff(word_size); |
| 366 | }); | 366 | }); |
| 367 | #[cfg(spi_v2)] | 367 | #[cfg(spi_v3)] |
| 368 | self.info.regs.cr2().modify(|w| { | 368 | self.info.regs.cr2().modify(|w| { |
| 369 | w.set_frxth(word_size.1); | 369 | w.set_frxth(word_size.1); |
| 370 | w.set_ds(word_size.0); | 370 | w.set_ds(word_size.0); |
| 371 | }); | 371 | }); |
| 372 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 372 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 373 | self.info.regs.cfg1().modify(|w| { | 373 | self.info.regs.cfg1().modify(|w| { |
| 374 | w.set_dsize(word_size); | 374 | w.set_dsize(word_size); |
| 375 | }); | 375 | }); |
| @@ -380,7 +380,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 380 | /// Blocking write. | 380 | /// Blocking write. |
| 381 | pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { | 381 | pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { |
| 382 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 382 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 383 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 383 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 384 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 384 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 385 | self.set_word_size(W::CONFIG); | 385 | self.set_word_size(W::CONFIG); |
| 386 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 386 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| @@ -391,7 +391,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 391 | // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`. | 391 | // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`. |
| 392 | // See https://github.com/embassy-rs/embassy/issues/2902 | 392 | // See https://github.com/embassy-rs/embassy/issues/2902 |
| 393 | // This is not documented as an errata by ST, and I've been unable to find anything online... | 393 | // This is not documented as an errata by ST, and I've been unable to find anything online... |
| 394 | #[cfg(not(any(spi_v1, spi_f1)))] | 394 | #[cfg(not(any(spi_v1, spi_v2)))] |
| 395 | write_word(self.info.regs, *word)?; | 395 | write_word(self.info.regs, *word)?; |
| 396 | 396 | ||
| 397 | // if we're doing tx only, after writing the last byte to FIFO we have to wait | 397 | // if we're doing tx only, after writing the last byte to FIFO we have to wait |
| @@ -401,14 +401,14 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 401 | // Luckily this doesn't affect SPIv2+. | 401 | // Luckily this doesn't affect SPIv2+. |
| 402 | // See http://efton.sk/STM32/gotcha/g68.html | 402 | // See http://efton.sk/STM32/gotcha/g68.html |
| 403 | // ST doesn't seem to document this in errata sheets (?) | 403 | // ST doesn't seem to document this in errata sheets (?) |
| 404 | #[cfg(any(spi_v1, spi_f1))] | 404 | #[cfg(any(spi_v1, spi_v2))] |
| 405 | transfer_word(self.info.regs, *word)?; | 405 | transfer_word(self.info.regs, *word)?; |
| 406 | } | 406 | } |
| 407 | 407 | ||
| 408 | // wait until last word is transmitted. (except on v1, see above) | 408 | // wait until last word is transmitted. (except on v1, see above) |
| 409 | #[cfg(not(any(spi_v1, spi_f1, spi_v2)))] | 409 | #[cfg(not(any(spi_v1, spi_v2, spi_v3)))] |
| 410 | while !self.info.regs.sr().read().txc() {} | 410 | while !self.info.regs.sr().read().txc() {} |
| 411 | #[cfg(spi_v2)] | 411 | #[cfg(spi_v3)] |
| 412 | while self.info.regs.sr().read().bsy() {} | 412 | while self.info.regs.sr().read().bsy() {} |
| 413 | 413 | ||
| 414 | Ok(()) | 414 | Ok(()) |
| @@ -417,7 +417,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 417 | /// Blocking read. | 417 | /// Blocking read. |
| 418 | pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { | 418 | pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |
| 419 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 419 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 420 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 420 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 421 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 421 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 422 | self.set_word_size(W::CONFIG); | 422 | self.set_word_size(W::CONFIG); |
| 423 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 423 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| @@ -433,7 +433,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 433 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. | 433 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. |
| 434 | pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { | 434 | pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |
| 435 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 435 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 436 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 436 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 437 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 437 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 438 | self.set_word_size(W::CONFIG); | 438 | self.set_word_size(W::CONFIG); |
| 439 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 439 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| @@ -452,7 +452,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 452 | /// If `write` is shorter it is padded with zero bytes. | 452 | /// If `write` is shorter it is padded with zero bytes. |
| 453 | pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 453 | pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
| 454 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 454 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 455 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 455 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 456 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 456 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 457 | self.set_word_size(W::CONFIG); | 457 | self.set_word_size(W::CONFIG); |
| 458 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 458 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| @@ -471,11 +471,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 471 | 471 | ||
| 472 | impl<'d> Spi<'d, Blocking> { | 472 | impl<'d> Spi<'d, Blocking> { |
| 473 | /// Create a new blocking SPI driver. | 473 | /// Create a new blocking SPI driver. |
| 474 | pub fn new_blocking<T: Instance>( | 474 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 475 | peri: Peri<'d, T>, | 475 | peri: Peri<'d, T>, |
| 476 | sck: Peri<'d, impl SckPin<T>>, | 476 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, |
| 477 | mosi: Peri<'d, impl MosiPin<T>>, | 477 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 478 | miso: Peri<'d, impl MisoPin<T>>, | 478 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, |
| 479 | config: Config, | 479 | config: Config, |
| 480 | ) -> Self { | 480 | ) -> Self { |
| 481 | Self::new_inner( | 481 | Self::new_inner( |
| @@ -490,10 +490,10 @@ impl<'d> Spi<'d, Blocking> { | |||
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). | 492 | /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). |
| 493 | pub fn new_blocking_rxonly<T: Instance>( | 493 | pub fn new_blocking_rxonly<T: Instance, #[cfg(afio)] A>( |
| 494 | peri: Peri<'d, T>, | 494 | peri: Peri<'d, T>, |
| 495 | sck: Peri<'d, impl SckPin<T>>, | 495 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, |
| 496 | miso: Peri<'d, impl MisoPin<T>>, | 496 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, |
| 497 | config: Config, | 497 | config: Config, |
| 498 | ) -> Self { | 498 | ) -> Self { |
| 499 | Self::new_inner( | 499 | Self::new_inner( |
| @@ -508,10 +508,10 @@ impl<'d> Spi<'d, Blocking> { | |||
| 508 | } | 508 | } |
| 509 | 509 | ||
| 510 | /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). | 510 | /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). |
| 511 | pub fn new_blocking_txonly<T: Instance>( | 511 | pub fn new_blocking_txonly<T: Instance, #[cfg(afio)] A>( |
| 512 | peri: Peri<'d, T>, | 512 | peri: Peri<'d, T>, |
| 513 | sck: Peri<'d, impl SckPin<T>>, | 513 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, |
| 514 | mosi: Peri<'d, impl MosiPin<T>>, | 514 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 515 | config: Config, | 515 | config: Config, |
| 516 | ) -> Self { | 516 | ) -> Self { |
| 517 | Self::new_inner( | 517 | Self::new_inner( |
| @@ -528,9 +528,9 @@ impl<'d> Spi<'d, Blocking> { | |||
| 528 | /// Create a new SPI driver, in TX-only mode, without SCK pin. | 528 | /// Create a new SPI driver, in TX-only mode, without SCK pin. |
| 529 | /// | 529 | /// |
| 530 | /// This can be useful for bit-banging non-SPI protocols. | 530 | /// This can be useful for bit-banging non-SPI protocols. |
| 531 | pub fn new_blocking_txonly_nosck<T: Instance>( | 531 | pub fn new_blocking_txonly_nosck<T: Instance, #[cfg(afio)] A>( |
| 532 | peri: Peri<'d, T>, | 532 | peri: Peri<'d, T>, |
| 533 | mosi: Peri<'d, impl MosiPin<T>>, | 533 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 534 | config: Config, | 534 | config: Config, |
| 535 | ) -> Self { | 535 | ) -> Self { |
| 536 | Self::new_inner( | 536 | Self::new_inner( |
| @@ -547,11 +547,11 @@ impl<'d> Spi<'d, Blocking> { | |||
| 547 | 547 | ||
| 548 | impl<'d> Spi<'d, Async> { | 548 | impl<'d> Spi<'d, Async> { |
| 549 | /// Create a new SPI driver. | 549 | /// Create a new SPI driver. |
| 550 | pub fn new<T: Instance>( | 550 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 551 | peri: Peri<'d, T>, | 551 | peri: Peri<'d, T>, |
| 552 | sck: Peri<'d, impl SckPin<T>>, | 552 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, |
| 553 | mosi: Peri<'d, impl MosiPin<T>>, | 553 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 554 | miso: Peri<'d, impl MisoPin<T>>, | 554 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, |
| 555 | tx_dma: Peri<'d, impl TxDma<T>>, | 555 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 556 | rx_dma: Peri<'d, impl RxDma<T>>, | 556 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 557 | config: Config, | 557 | config: Config, |
| @@ -568,11 +568,11 @@ impl<'d> Spi<'d, Async> { | |||
| 568 | } | 568 | } |
| 569 | 569 | ||
| 570 | /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). | 570 | /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). |
| 571 | pub fn new_rxonly<T: Instance>( | 571 | pub fn new_rxonly<T: Instance, #[cfg(afio)] A>( |
| 572 | peri: Peri<'d, T>, | 572 | peri: Peri<'d, T>, |
| 573 | sck: Peri<'d, impl SckPin<T>>, | 573 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, |
| 574 | miso: Peri<'d, impl MisoPin<T>>, | 574 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, |
| 575 | #[cfg(any(spi_v1, spi_f1, spi_v2))] tx_dma: Peri<'d, impl TxDma<T>>, | 575 | #[cfg(any(spi_v1, spi_v2, spi_v3))] tx_dma: Peri<'d, impl TxDma<T>>, |
| 576 | rx_dma: Peri<'d, impl RxDma<T>>, | 576 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 577 | config: Config, | 577 | config: Config, |
| 578 | ) -> Self { | 578 | ) -> Self { |
| @@ -581,9 +581,9 @@ impl<'d> Spi<'d, Async> { | |||
| 581 | new_pin!(sck, config.sck_af()), | 581 | new_pin!(sck, config.sck_af()), |
| 582 | None, | 582 | None, |
| 583 | new_pin!(miso, AfType::input(config.miso_pull)), | 583 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 584 | #[cfg(any(spi_v1, spi_f1, spi_v2))] | 584 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 585 | new_dma!(tx_dma), | 585 | new_dma!(tx_dma), |
| 586 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 586 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 587 | None, | 587 | None, |
| 588 | new_dma!(rx_dma), | 588 | new_dma!(rx_dma), |
| 589 | config, | 589 | config, |
| @@ -591,10 +591,10 @@ impl<'d> Spi<'d, Async> { | |||
| 591 | } | 591 | } |
| 592 | 592 | ||
| 593 | /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). | 593 | /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). |
| 594 | pub fn new_txonly<T: Instance>( | 594 | pub fn new_txonly<T: Instance, #[cfg(afio)] A>( |
| 595 | peri: Peri<'d, T>, | 595 | peri: Peri<'d, T>, |
| 596 | sck: Peri<'d, impl SckPin<T>>, | 596 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, |
| 597 | mosi: Peri<'d, impl MosiPin<T>>, | 597 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 598 | tx_dma: Peri<'d, impl TxDma<T>>, | 598 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 599 | config: Config, | 599 | config: Config, |
| 600 | ) -> Self { | 600 | ) -> Self { |
| @@ -612,9 +612,9 @@ impl<'d> Spi<'d, Async> { | |||
| 612 | /// Create a new SPI driver, in TX-only mode, without SCK pin. | 612 | /// Create a new SPI driver, in TX-only mode, without SCK pin. |
| 613 | /// | 613 | /// |
| 614 | /// This can be useful for bit-banging non-SPI protocols. | 614 | /// This can be useful for bit-banging non-SPI protocols. |
| 615 | pub fn new_txonly_nosck<T: Instance>( | 615 | pub fn new_txonly_nosck<T: Instance, #[cfg(afio)] A>( |
| 616 | peri: Peri<'d, T>, | 616 | peri: Peri<'d, T>, |
| 617 | mosi: Peri<'d, impl MosiPin<T>>, | 617 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, |
| 618 | tx_dma: Peri<'d, impl TxDma<T>>, | 618 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 619 | config: Config, | 619 | config: Config, |
| 620 | ) -> Self { | 620 | ) -> Self { |
| @@ -677,7 +677,7 @@ impl<'d> Spi<'d, Async> { | |||
| 677 | self.info.regs.cr1().modify(|w| { | 677 | self.info.regs.cr1().modify(|w| { |
| 678 | w.set_spe(true); | 678 | w.set_spe(true); |
| 679 | }); | 679 | }); |
| 680 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 680 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 681 | self.info.regs.cr1().modify(|w| { | 681 | self.info.regs.cr1().modify(|w| { |
| 682 | w.set_cstart(true); | 682 | w.set_cstart(true); |
| 683 | }); | 683 | }); |
| @@ -690,7 +690,7 @@ impl<'d> Spi<'d, Async> { | |||
| 690 | } | 690 | } |
| 691 | 691 | ||
| 692 | /// SPI read, using DMA. | 692 | /// SPI read, using DMA. |
| 693 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 693 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 694 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 694 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 695 | if data.is_empty() { | 695 | if data.is_empty() { |
| 696 | return Ok(()); | 696 | return Ok(()); |
| @@ -710,7 +710,7 @@ impl<'d> Spi<'d, Async> { | |||
| 710 | prev | 710 | prev |
| 711 | }); | 711 | }); |
| 712 | 712 | ||
| 713 | #[cfg(spi_v3)] | 713 | #[cfg(spi_v4)] |
| 714 | let i2scfg = regs.i2scfgr().modify(|w| { | 714 | let i2scfg = regs.i2scfgr().modify(|w| { |
| 715 | w.i2smod().then(|| { | 715 | w.i2smod().then(|| { |
| 716 | let prev = w.i2scfg(); | 716 | let prev = w.i2scfg(); |
| @@ -766,7 +766,7 @@ impl<'d> Spi<'d, Async> { | |||
| 766 | w.set_tsize(0); | 766 | w.set_tsize(0); |
| 767 | }); | 767 | }); |
| 768 | 768 | ||
| 769 | #[cfg(spi_v3)] | 769 | #[cfg(spi_v4)] |
| 770 | if let Some(i2scfg) = i2scfg { | 770 | if let Some(i2scfg) = i2scfg { |
| 771 | regs.i2scfgr().modify(|w| { | 771 | regs.i2scfgr().modify(|w| { |
| 772 | w.set_i2scfg(i2scfg); | 772 | w.set_i2scfg(i2scfg); |
| @@ -777,7 +777,7 @@ impl<'d> Spi<'d, Async> { | |||
| 777 | } | 777 | } |
| 778 | 778 | ||
| 779 | /// SPI read, using DMA. | 779 | /// SPI read, using DMA. |
| 780 | #[cfg(any(spi_v1, spi_f1, spi_v2))] | 780 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 781 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 781 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 782 | if data.is_empty() { | 782 | if data.is_empty() { |
| 783 | return Ok(()); | 783 | return Ok(()); |
| @@ -790,7 +790,7 @@ impl<'d> Spi<'d, Async> { | |||
| 790 | self.set_word_size(W::CONFIG); | 790 | self.set_word_size(W::CONFIG); |
| 791 | 791 | ||
| 792 | // SPIv3 clears rxfifo on SPE=0 | 792 | // SPIv3 clears rxfifo on SPE=0 |
| 793 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 793 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 794 | flush_rx_fifo(self.info.regs); | 794 | flush_rx_fifo(self.info.regs); |
| 795 | 795 | ||
| 796 | set_rxdmaen(self.info.regs, true); | 796 | set_rxdmaen(self.info.regs, true); |
| @@ -813,7 +813,7 @@ impl<'d> Spi<'d, Async> { | |||
| 813 | self.info.regs.cr1().modify(|w| { | 813 | self.info.regs.cr1().modify(|w| { |
| 814 | w.set_spe(true); | 814 | w.set_spe(true); |
| 815 | }); | 815 | }); |
| 816 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 816 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 817 | self.info.regs.cr1().modify(|w| { | 817 | self.info.regs.cr1().modify(|w| { |
| 818 | w.set_cstart(true); | 818 | w.set_cstart(true); |
| 819 | }); | 819 | }); |
| @@ -838,7 +838,7 @@ impl<'d> Spi<'d, Async> { | |||
| 838 | self.set_word_size(W::CONFIG); | 838 | self.set_word_size(W::CONFIG); |
| 839 | 839 | ||
| 840 | // SPIv3 clears rxfifo on SPE=0 | 840 | // SPIv3 clears rxfifo on SPE=0 |
| 841 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 841 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 842 | flush_rx_fifo(self.info.regs); | 842 | flush_rx_fifo(self.info.regs); |
| 843 | 843 | ||
| 844 | set_rxdmaen(self.info.regs, true); | 844 | set_rxdmaen(self.info.regs, true); |
| @@ -858,7 +858,7 @@ impl<'d> Spi<'d, Async> { | |||
| 858 | self.info.regs.cr1().modify(|w| { | 858 | self.info.regs.cr1().modify(|w| { |
| 859 | w.set_spe(true); | 859 | w.set_spe(true); |
| 860 | }); | 860 | }); |
| 861 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 861 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 862 | self.info.regs.cr1().modify(|w| { | 862 | self.info.regs.cr1().modify(|w| { |
| 863 | w.set_cstart(true); | 863 | w.set_cstart(true); |
| 864 | }); | 864 | }); |
| @@ -898,9 +898,9 @@ impl<'d, M: PeriMode> Drop for Spi<'d, M> { | |||
| 898 | } | 898 | } |
| 899 | } | 899 | } |
| 900 | 900 | ||
| 901 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 901 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 902 | use vals::Br; | 902 | use vals::Br; |
| 903 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 903 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 904 | use vals::Mbr as Br; | 904 | use vals::Mbr as Br; |
| 905 | 905 | ||
| 906 | fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br { | 906 | fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br { |
| @@ -941,21 +941,21 @@ pub(crate) trait RegsExt { | |||
| 941 | 941 | ||
| 942 | impl RegsExt for Regs { | 942 | impl RegsExt for Regs { |
| 943 | fn tx_ptr<W>(&self) -> *mut W { | 943 | fn tx_ptr<W>(&self) -> *mut W { |
| 944 | #[cfg(any(spi_v1, spi_f1))] | 944 | #[cfg(any(spi_v1, spi_v2))] |
| 945 | let dr = self.dr(); | 945 | let dr = self.dr(); |
| 946 | #[cfg(spi_v2)] | 946 | #[cfg(spi_v3)] |
| 947 | let dr = self.dr16(); | 947 | let dr = self.dr16(); |
| 948 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 948 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 949 | let dr = self.txdr32(); | 949 | let dr = self.txdr32(); |
| 950 | dr.as_ptr() as *mut W | 950 | dr.as_ptr() as *mut W |
| 951 | } | 951 | } |
| 952 | 952 | ||
| 953 | fn rx_ptr<W>(&self) -> *mut W { | 953 | fn rx_ptr<W>(&self) -> *mut W { |
| 954 | #[cfg(any(spi_v1, spi_f1))] | 954 | #[cfg(any(spi_v1, spi_v2))] |
| 955 | let dr = self.dr(); | 955 | let dr = self.dr(); |
| 956 | #[cfg(spi_v2)] | 956 | #[cfg(spi_v3)] |
| 957 | let dr = self.dr16(); | 957 | let dr = self.dr16(); |
| 958 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 958 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 959 | let dr = self.rxdr32(); | 959 | let dr = self.rxdr32(); |
| 960 | dr.as_ptr() as *mut W | 960 | dr.as_ptr() as *mut W |
| 961 | } | 961 | } |
| @@ -965,22 +965,22 @@ fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> { | |||
| 965 | if sr.ovr() && ovr { | 965 | if sr.ovr() && ovr { |
| 966 | return Err(Error::Overrun); | 966 | return Err(Error::Overrun); |
| 967 | } | 967 | } |
| 968 | #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] | 968 | #[cfg(not(any(spi_v1, spi_v4, spi_v5, spi_v6)))] |
| 969 | if sr.fre() { | 969 | if sr.fre() { |
| 970 | return Err(Error::Framing); | 970 | return Err(Error::Framing); |
| 971 | } | 971 | } |
| 972 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 972 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 973 | if sr.tifre() { | 973 | if sr.tifre() { |
| 974 | return Err(Error::Framing); | 974 | return Err(Error::Framing); |
| 975 | } | 975 | } |
| 976 | if sr.modf() { | 976 | if sr.modf() { |
| 977 | return Err(Error::ModeFault); | 977 | return Err(Error::ModeFault); |
| 978 | } | 978 | } |
| 979 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 979 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 980 | if sr.crcerr() { | 980 | if sr.crcerr() { |
| 981 | return Err(Error::Crc); | 981 | return Err(Error::Crc); |
| 982 | } | 982 | } |
| 983 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 983 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 984 | if sr.crce() { | 984 | if sr.crce() { |
| 985 | return Err(Error::Crc); | 985 | return Err(Error::Crc); |
| 986 | } | 986 | } |
| @@ -994,11 +994,11 @@ fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> { | |||
| 994 | 994 | ||
| 995 | check_error_flags(sr, ovr)?; | 995 | check_error_flags(sr, ovr)?; |
| 996 | 996 | ||
| 997 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 997 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 998 | if sr.txe() { | 998 | if sr.txe() { |
| 999 | return Ok(()); | 999 | return Ok(()); |
| 1000 | } | 1000 | } |
| 1001 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1001 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1002 | if sr.txp() { | 1002 | if sr.txp() { |
| 1003 | return Ok(()); | 1003 | return Ok(()); |
| 1004 | } | 1004 | } |
| @@ -1011,11 +1011,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { | |||
| 1011 | 1011 | ||
| 1012 | check_error_flags(sr, true)?; | 1012 | check_error_flags(sr, true)?; |
| 1013 | 1013 | ||
| 1014 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1014 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 1015 | if sr.rxne() { | 1015 | if sr.rxne() { |
| 1016 | return Ok(()); | 1016 | return Ok(()); |
| 1017 | } | 1017 | } |
| 1018 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1018 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1019 | if sr.rxp() { | 1019 | if sr.rxp() { |
| 1020 | return Ok(()); | 1020 | return Ok(()); |
| 1021 | } | 1021 | } |
| @@ -1023,46 +1023,46 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { | |||
| 1023 | } | 1023 | } |
| 1024 | 1024 | ||
| 1025 | pub(crate) fn flush_rx_fifo(regs: Regs) { | 1025 | pub(crate) fn flush_rx_fifo(regs: Regs) { |
| 1026 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1026 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 1027 | while regs.sr().read().rxne() { | 1027 | while regs.sr().read().rxne() { |
| 1028 | #[cfg(not(spi_v2))] | 1028 | #[cfg(not(spi_v3))] |
| 1029 | let _ = regs.dr().read(); | 1029 | let _ = regs.dr().read(); |
| 1030 | #[cfg(spi_v2)] | 1030 | #[cfg(spi_v3)] |
| 1031 | let _ = regs.dr16().read(); | 1031 | let _ = regs.dr16().read(); |
| 1032 | } | 1032 | } |
| 1033 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1033 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1034 | while regs.sr().read().rxp() { | 1034 | while regs.sr().read().rxp() { |
| 1035 | let _ = regs.rxdr32().read(); | 1035 | let _ = regs.rxdr32().read(); |
| 1036 | } | 1036 | } |
| 1037 | } | 1037 | } |
| 1038 | 1038 | ||
| 1039 | pub(crate) fn set_txdmaen(regs: Regs, val: bool) { | 1039 | pub(crate) fn set_txdmaen(regs: Regs, val: bool) { |
| 1040 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1040 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 1041 | regs.cr2().modify(|reg| { | 1041 | regs.cr2().modify(|reg| { |
| 1042 | reg.set_txdmaen(val); | 1042 | reg.set_txdmaen(val); |
| 1043 | }); | 1043 | }); |
| 1044 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1044 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1045 | regs.cfg1().modify(|reg| { | 1045 | regs.cfg1().modify(|reg| { |
| 1046 | reg.set_txdmaen(val); | 1046 | reg.set_txdmaen(val); |
| 1047 | }); | 1047 | }); |
| 1048 | } | 1048 | } |
| 1049 | 1049 | ||
| 1050 | pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { | 1050 | pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { |
| 1051 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1051 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 1052 | regs.cr2().modify(|reg| { | 1052 | regs.cr2().modify(|reg| { |
| 1053 | reg.set_rxdmaen(val); | 1053 | reg.set_rxdmaen(val); |
| 1054 | }); | 1054 | }); |
| 1055 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1055 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1056 | regs.cfg1().modify(|reg| { | 1056 | regs.cfg1().modify(|reg| { |
| 1057 | reg.set_rxdmaen(val); | 1057 | reg.set_rxdmaen(val); |
| 1058 | }); | 1058 | }); |
| 1059 | } | 1059 | } |
| 1060 | 1060 | ||
| 1061 | fn finish_dma(regs: Regs) { | 1061 | fn finish_dma(regs: Regs) { |
| 1062 | #[cfg(spi_v2)] | 1062 | #[cfg(spi_v3)] |
| 1063 | while regs.sr().read().ftlvl().to_bits() > 0 {} | 1063 | while regs.sr().read().ftlvl().to_bits() > 0 {} |
| 1064 | 1064 | ||
| 1065 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1065 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1066 | { | 1066 | { |
| 1067 | if regs.cr2().read().tsize() == 0 { | 1067 | if regs.cr2().read().tsize() == 0 { |
| 1068 | while !regs.sr().read().txc() {} | 1068 | while !regs.sr().read().txc() {} |
| @@ -1070,7 +1070,7 @@ fn finish_dma(regs: Regs) { | |||
| 1070 | while !regs.sr().read().eot() {} | 1070 | while !regs.sr().read().eot() {} |
| 1071 | } | 1071 | } |
| 1072 | } | 1072 | } |
| 1073 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1073 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 1074 | while regs.sr().read().bsy() {} | 1074 | while regs.sr().read().bsy() {} |
| 1075 | 1075 | ||
| 1076 | // Disable the spi peripheral | 1076 | // Disable the spi peripheral |
| @@ -1080,12 +1080,12 @@ fn finish_dma(regs: Regs) { | |||
| 1080 | 1080 | ||
| 1081 | // The peripheral automatically disables the DMA stream on completion without error, | 1081 | // The peripheral automatically disables the DMA stream on completion without error, |
| 1082 | // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. | 1082 | // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. |
| 1083 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1083 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] |
| 1084 | regs.cr2().modify(|reg| { | 1084 | regs.cr2().modify(|reg| { |
| 1085 | reg.set_txdmaen(false); | 1085 | reg.set_txdmaen(false); |
| 1086 | reg.set_rxdmaen(false); | 1086 | reg.set_rxdmaen(false); |
| 1087 | }); | 1087 | }); |
| 1088 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1088 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1089 | regs.cfg1().modify(|reg| { | 1089 | regs.cfg1().modify(|reg| { |
| 1090 | reg.set_txdmaen(false); | 1090 | reg.set_txdmaen(false); |
| 1091 | reg.set_rxdmaen(false); | 1091 | reg.set_rxdmaen(false); |
| @@ -1098,7 +1098,7 @@ fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> { | |||
| 1098 | unsafe { | 1098 | unsafe { |
| 1099 | ptr::write_volatile(regs.tx_ptr(), tx_word); | 1099 | ptr::write_volatile(regs.tx_ptr(), tx_word); |
| 1100 | 1100 | ||
| 1101 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1101 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1102 | regs.cr1().modify(|reg| reg.set_cstart(true)); | 1102 | regs.cr1().modify(|reg| reg.set_cstart(true)); |
| 1103 | } | 1103 | } |
| 1104 | 1104 | ||
| @@ -1117,7 +1117,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> { | |||
| 1117 | unsafe { | 1117 | unsafe { |
| 1118 | ptr::write_volatile(regs.tx_ptr(), tx_word); | 1118 | ptr::write_volatile(regs.tx_ptr(), tx_word); |
| 1119 | 1119 | ||
| 1120 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1120 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1121 | regs.cr1().modify(|reg| reg.set_cstart(true)); | 1121 | regs.cr1().modify(|reg| reg.set_cstart(true)); |
| 1122 | } | 1122 | } |
| 1123 | Ok(()) | 1123 | Ok(()) |
| @@ -1225,7 +1225,7 @@ macro_rules! impl_word { | |||
| 1225 | }; | 1225 | }; |
| 1226 | } | 1226 | } |
| 1227 | 1227 | ||
| 1228 | #[cfg(any(spi_v1, spi_f1))] | 1228 | #[cfg(any(spi_v1, spi_v2))] |
| 1229 | mod word_impl { | 1229 | mod word_impl { |
| 1230 | use super::*; | 1230 | use super::*; |
| 1231 | 1231 | ||
| @@ -1235,7 +1235,7 @@ mod word_impl { | |||
| 1235 | impl_word!(u16, vals::Dff::BITS16); | 1235 | impl_word!(u16, vals::Dff::BITS16); |
| 1236 | } | 1236 | } |
| 1237 | 1237 | ||
| 1238 | #[cfg(spi_v2)] | 1238 | #[cfg(spi_v3)] |
| 1239 | mod word_impl { | 1239 | mod word_impl { |
| 1240 | use super::*; | 1240 | use super::*; |
| 1241 | 1241 | ||
| @@ -1256,7 +1256,7 @@ mod word_impl { | |||
| 1256 | impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF)); | 1256 | impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF)); |
| 1257 | } | 1257 | } |
| 1258 | 1258 | ||
| 1259 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 1259 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 1260 | mod word_impl { | 1260 | mod word_impl { |
| 1261 | use super::*; | 1261 | use super::*; |
| 1262 | 1262 | ||
| @@ -1309,13 +1309,13 @@ impl State { | |||
| 1309 | 1309 | ||
| 1310 | peri_trait!(); | 1310 | peri_trait!(); |
| 1311 | 1311 | ||
| 1312 | pin_trait!(SckPin, Instance); | 1312 | pin_trait!(SckPin, Instance, @A); |
| 1313 | pin_trait!(MosiPin, Instance); | 1313 | pin_trait!(MosiPin, Instance, @A); |
| 1314 | pin_trait!(MisoPin, Instance); | 1314 | pin_trait!(MisoPin, Instance, @A); |
| 1315 | pin_trait!(CsPin, Instance); | 1315 | pin_trait!(CsPin, Instance, @A); |
| 1316 | pin_trait!(MckPin, Instance); | 1316 | pin_trait!(MckPin, Instance, @A); |
| 1317 | pin_trait!(CkPin, Instance); | 1317 | pin_trait!(CkPin, Instance, @A); |
| 1318 | pin_trait!(WsPin, Instance); | 1318 | pin_trait!(WsPin, Instance, @A); |
| 1319 | dma_trait!(RxDma, Instance); | 1319 | dma_trait!(RxDma, Instance); |
| 1320 | dma_trait!(TxDma, Instance); | 1320 | dma_trait!(TxDma, Instance); |
| 1321 | 1321 | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index b291fc155..484aae1d0 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -16,23 +16,24 @@ use crate::Peri; | |||
| 16 | /// Complementary PWM pin wrapper. | 16 | /// Complementary PWM pin wrapper. |
| 17 | /// | 17 | /// |
| 18 | /// This wraps a pin to make it usable with PWM. | 18 | /// This wraps a pin to make it usable with PWM. |
| 19 | pub struct ComplementaryPwmPin<'d, T, C> { | 19 | pub struct ComplementaryPwmPin<'d, T, C, #[cfg(afio)] A> { |
| 20 | _pin: Peri<'d, AnyPin>, | 20 | #[allow(unused)] |
| 21 | phantom: PhantomData<(T, C)>, | 21 | pin: Peri<'d, AnyPin>, |
| 22 | phantom: PhantomData<if_afio!((T, C, A))>, | ||
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> { | 25 | impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(ComplementaryPwmPin<'d, T, C, A>) { |
| 25 | /// Create a new complementary PWM pin instance. | 26 | /// Create a new complementary PWM pin instance. |
| 26 | pub fn new(pin: Peri<'d, impl TimerComplementaryPin<T, C>>, output_type: OutputType) -> Self { | 27 | pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self { |
| 27 | critical_section::with(|_| { | 28 | critical_section::with(|_| { |
| 28 | pin.set_low(); | 29 | pin.set_low(); |
| 29 | pin.set_as_af( | 30 | set_as_af!( |
| 30 | pin.af_num(), | 31 | pin, |
| 31 | crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), | 32 | crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh) |
| 32 | ); | 33 | ); |
| 33 | }); | 34 | }); |
| 34 | ComplementaryPwmPin { | 35 | ComplementaryPwmPin { |
| 35 | _pin: pin.into(), | 36 | pin: pin.into(), |
| 36 | phantom: PhantomData, | 37 | phantom: PhantomData, |
| 37 | } | 38 | } |
| 38 | } | 39 | } |
| @@ -54,17 +55,17 @@ pub enum IdlePolarity { | |||
| 54 | 55 | ||
| 55 | impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | 56 | impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { |
| 56 | /// Create a new complementary PWM driver. | 57 | /// Create a new complementary PWM driver. |
| 57 | #[allow(clippy::too_many_arguments)] | 58 | #[allow(clippy::too_many_arguments, unused)] |
| 58 | pub fn new( | 59 | pub fn new<#[cfg(afio)] A>( |
| 59 | tim: Peri<'d, T>, | 60 | tim: Peri<'d, T>, |
| 60 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 61 | ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>, |
| 61 | _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>, | 62 | ch1n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch1, A>)>, |
| 62 | _ch2: Option<PwmPin<'d, T, Ch2>>, | 63 | ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>, |
| 63 | _ch2n: Option<ComplementaryPwmPin<'d, T, Ch2>>, | 64 | ch2n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch2, A>)>, |
| 64 | _ch3: Option<PwmPin<'d, T, Ch3>>, | 65 | ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>, |
| 65 | _ch3n: Option<ComplementaryPwmPin<'d, T, Ch3>>, | 66 | ch3n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch3, A>)>, |
| 66 | _ch4: Option<PwmPin<'d, T, Ch4>>, | 67 | ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>, |
| 67 | _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>, | 68 | ch4n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch4, A>)>, |
| 68 | freq: Hertz, | 69 | freq: Hertz, |
| 69 | counting_mode: CountingMode, | 70 | counting_mode: CountingMode, |
| 70 | ) -> Self { | 71 | ) -> Self { |
| @@ -185,6 +186,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 185 | self.inner.set_complementary_output_polarity(channel, polarity); | 186 | self.inner.set_complementary_output_polarity(channel, polarity); |
| 186 | } | 187 | } |
| 187 | 188 | ||
| 189 | /// Set the main output polarity for a given channel. | ||
| 190 | pub fn set_main_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | ||
| 191 | self.inner.set_output_polarity(channel, polarity); | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Set the complementary output polarity for a given channel. | ||
| 195 | pub fn set_complementary_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | ||
| 196 | self.inner.set_complementary_output_polarity(channel, polarity); | ||
| 197 | } | ||
| 198 | |||
| 188 | /// Set the dead time as a proportion of max_duty | 199 | /// Set the dead time as a proportion of max_duty |
| 189 | pub fn set_dead_time(&mut self, value: u16) { | 200 | pub fn set_dead_time(&mut self, value: u16) { |
| 190 | let (ckd, value) = compute_dead_time_value(value); | 201 | let (ckd, value) = compute_dead_time_value(value); |
| @@ -192,6 +203,66 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 192 | self.inner.set_dead_time_clock_division(ckd); | 203 | self.inner.set_dead_time_clock_division(ckd); |
| 193 | self.inner.set_dead_time_value(value); | 204 | self.inner.set_dead_time_value(value); |
| 194 | } | 205 | } |
| 206 | |||
| 207 | /// Generate a sequence of PWM waveform | ||
| 208 | /// | ||
| 209 | /// Note: | ||
| 210 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 211 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { | ||
| 212 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 213 | let req = dma.request(); | ||
| 214 | |||
| 215 | let original_duty_state = self.inner.get_compare_value(channel); | ||
| 216 | let original_enable_state = self.inner.get_channel_enable_state(channel); | ||
| 217 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 218 | |||
| 219 | if !original_update_dma_state { | ||
| 220 | self.inner.enable_update_dma(true); | ||
| 221 | } | ||
| 222 | |||
| 223 | if !original_enable_state { | ||
| 224 | self.inner.enable_channel(channel, true); | ||
| 225 | } | ||
| 226 | |||
| 227 | unsafe { | ||
| 228 | #[cfg(not(any(bdma, gpdma)))] | ||
| 229 | use crate::dma::{Burst, FifoThreshold}; | ||
| 230 | use crate::dma::{Transfer, TransferOptions}; | ||
| 231 | |||
| 232 | let dma_transfer_option = TransferOptions { | ||
| 233 | #[cfg(not(any(bdma, gpdma)))] | ||
| 234 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 235 | #[cfg(not(any(bdma, gpdma)))] | ||
| 236 | mburst: Burst::Incr8, | ||
| 237 | ..Default::default() | ||
| 238 | }; | ||
| 239 | |||
| 240 | Transfer::new_write( | ||
| 241 | dma, | ||
| 242 | req, | ||
| 243 | duty, | ||
| 244 | self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, | ||
| 245 | dma_transfer_option, | ||
| 246 | ) | ||
| 247 | .await | ||
| 248 | }; | ||
| 249 | |||
| 250 | // restore output compare state | ||
| 251 | if !original_enable_state { | ||
| 252 | self.inner.enable_channel(channel, false); | ||
| 253 | } | ||
| 254 | |||
| 255 | self.inner.set_compare_value(channel, original_duty_state); | ||
| 256 | |||
| 257 | // Since DMA is closed before timer update event trigger DMA is turn off, | ||
| 258 | // this can almost always trigger a DMA FIFO error. | ||
| 259 | // | ||
| 260 | // optional TODO: | ||
| 261 | // clean FEIF after disable UDE | ||
| 262 | if !original_update_dma_state { | ||
| 263 | self.inner.enable_update_dma(false); | ||
| 264 | } | ||
| 265 | } | ||
| 195 | } | 266 | } |
| 196 | 267 | ||
| 197 | impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { | 268 | impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { |
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index dda33e7f1..7a25e6c21 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -17,16 +17,17 @@ use crate::Peri; | |||
| 17 | /// Capture pin wrapper. | 17 | /// Capture pin wrapper. |
| 18 | /// | 18 | /// |
| 19 | /// This wraps a pin to make it usable with capture. | 19 | /// This wraps a pin to make it usable with capture. |
| 20 | pub struct CapturePin<'d, T, C> { | 20 | pub struct CapturePin<'d, T, C, #[cfg(afio)] A> { |
| 21 | _pin: Peri<'d, AnyPin>, | 21 | #[allow(unused)] |
| 22 | phantom: PhantomData<(T, C)>, | 22 | pin: Peri<'d, AnyPin>, |
| 23 | phantom: PhantomData<if_afio!((T, C, A))>, | ||
| 23 | } | 24 | } |
| 24 | impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { | 25 | impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(CapturePin<'d, T, C, A>) { |
| 25 | /// Create a new capture pin instance. | 26 | /// Create a new capture pin instance. |
| 26 | pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, pull: Pull) -> Self { | 27 | pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self { |
| 27 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 28 | set_as_af!(pin, AfType::input(pull)); |
| 28 | CapturePin { | 29 | CapturePin { |
| 29 | _pin: pin.into(), | 30 | pin: pin.into(), |
| 30 | phantom: PhantomData, | 31 | phantom: PhantomData, |
| 31 | } | 32 | } |
| 32 | } | 33 | } |
| @@ -39,12 +40,13 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> { | |||
| 39 | 40 | ||
| 40 | impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | 41 | impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { |
| 41 | /// Create a new input capture driver. | 42 | /// Create a new input capture driver. |
| 42 | pub fn new( | 43 | #[allow(unused)] |
| 44 | pub fn new<#[cfg(afio)] A>( | ||
| 43 | tim: Peri<'d, T>, | 45 | tim: Peri<'d, T>, |
| 44 | _ch1: Option<CapturePin<'d, T, Ch1>>, | 46 | ch1: Option<if_afio!(CapturePin<'d, T, Ch1, A>)>, |
| 45 | _ch2: Option<CapturePin<'d, T, Ch2>>, | 47 | ch2: Option<if_afio!(CapturePin<'d, T, Ch2, A>)>, |
| 46 | _ch3: Option<CapturePin<'d, T, Ch3>>, | 48 | ch3: Option<if_afio!(CapturePin<'d, T, Ch3, A>)>, |
| 47 | _ch4: Option<CapturePin<'d, T, Ch4>>, | 49 | ch4: Option<if_afio!(CapturePin<'d, T, Ch4, A>)>, |
| 48 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | 50 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, |
| 49 | freq: Hertz, | 51 | freq: Hertz, |
| 50 | counting_mode: CountingMode, | 52 | counting_mode: CountingMode, |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 7062f5f4c..b09bc7166 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -223,15 +223,15 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad | |||
| 223 | /// Advanced 16-bit timer with 4 channels instance. | 223 | /// Advanced 16-bit timer with 4 channels instance. |
| 224 | pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} | 224 | pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} |
| 225 | 225 | ||
| 226 | pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel); | 226 | pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel, @A); |
| 227 | pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); | 227 | pin_trait!(ExternalTriggerPin, GeneralInstance4Channel, @A); |
| 228 | 228 | ||
| 229 | pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel); | 229 | pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel, @A); |
| 230 | 230 | ||
| 231 | pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput); | 231 | pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput, @A); |
| 232 | 232 | ||
| 233 | pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput); | 233 | pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput, @A); |
| 234 | pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput); | 234 | pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput, @A); |
| 235 | 235 | ||
| 236 | // Update Event trigger DMA for every timer | 236 | // Update Event trigger DMA for every timer |
| 237 | dma_trait!(UpDma, BasicInstance); | 237 | dma_trait!(UpDma, BasicInstance); |
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 498d9c082..a75b41bd7 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs | |||
| @@ -15,6 +15,7 @@ use crate::gpio::{AfType, AnyPin, Pull}; | |||
| 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 16 | use crate::pac::timer::vals::Etp; | 16 | use crate::pac::timer::vals::Etp; |
| 17 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 18 | use crate::timer::TimerChannel; | ||
| 18 | use crate::Peri; | 19 | use crate::Peri; |
| 19 | 20 | ||
| 20 | /// External input marker type. | 21 | /// External input marker type. |
| @@ -42,7 +43,8 @@ impl From<ExternalTriggerPolarity> for Etp { | |||
| 42 | /// | 43 | /// |
| 43 | /// This wraps a pin to make it usable as a timer trigger. | 44 | /// This wraps a pin to make it usable as a timer trigger. |
| 44 | pub struct TriggerPin<'d, T, C> { | 45 | pub struct TriggerPin<'d, T, C> { |
| 45 | _pin: Peri<'d, AnyPin>, | 46 | #[allow(unused)] |
| 47 | pin: Peri<'d, AnyPin>, | ||
| 46 | phantom: PhantomData<(T, C)>, | 48 | phantom: PhantomData<(T, C)>, |
| 47 | } | 49 | } |
| 48 | 50 | ||
| @@ -60,60 +62,23 @@ impl SealedTriggerSource for Ch1 {} | |||
| 60 | impl SealedTriggerSource for Ch2 {} | 62 | impl SealedTriggerSource for Ch2 {} |
| 61 | impl SealedTriggerSource for Ext {} | 63 | impl SealedTriggerSource for Ext {} |
| 62 | 64 | ||
| 63 | trait SealedTimerTriggerPin<T, S>: crate::gpio::Pin {} | 65 | impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> { |
| 64 | 66 | /// Create a new Channel trigger pin instance. | |
| 65 | /// Marker trait for a trigger pin. | 67 | pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self { |
| 66 | #[expect(private_bounds)] | 68 | set_as_af!(pin, AfType::input(pull)); |
| 67 | // TODO: find better naming scheme than prefixing all pin traits with "Timer". | 69 | TriggerPin { |
| 68 | // The trait name cannot conflict with the corresponding type's name. | 70 | pin: pin.into(), |
| 69 | // Applies to other timer submodules as well. | 71 | phantom: PhantomData, |
| 70 | pub trait TimerTriggerPin<T, S>: SealedTimerTriggerPin<T, S> { | 72 | } |
| 71 | /// Get the AF number needed to use this pin as a trigger source. | ||
| 72 | fn af_num(&self) -> u8; | ||
| 73 | } | ||
| 74 | |||
| 75 | impl<T, P, C> TimerTriggerPin<T, C> for P | ||
| 76 | where | ||
| 77 | T: GeneralInstance4Channel, | ||
| 78 | P: TimerPin<T, C>, | ||
| 79 | C: super::TimerChannel + TriggerSource, | ||
| 80 | { | ||
| 81 | fn af_num(&self) -> u8 { | ||
| 82 | TimerPin::af_num(self) | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | impl<T, P> TimerTriggerPin<T, Ext> for P | ||
| 87 | where | ||
| 88 | T: GeneralInstance4Channel, | ||
| 89 | P: ExternalTriggerPin<T>, | ||
| 90 | { | ||
| 91 | fn af_num(&self) -> u8 { | ||
| 92 | ExternalTriggerPin::af_num(self) | ||
| 93 | } | 73 | } |
| 94 | } | 74 | } |
| 95 | 75 | ||
| 96 | impl<T, P, C> SealedTimerTriggerPin<T, C> for P | 76 | impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { |
| 97 | where | 77 | /// Create a new external trigger pin instance. |
| 98 | T: GeneralInstance4Channel, | 78 | pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin<T, A>)>, pull: Pull) -> Self { |
| 99 | P: TimerPin<T, C>, | 79 | set_as_af!(pin, AfType::input(pull)); |
| 100 | C: super::TimerChannel + TriggerSource, | ||
| 101 | { | ||
| 102 | } | ||
| 103 | |||
| 104 | impl<T, P> SealedTimerTriggerPin<T, Ext> for P | ||
| 105 | where | ||
| 106 | T: GeneralInstance4Channel, | ||
| 107 | P: ExternalTriggerPin<T>, | ||
| 108 | { | ||
| 109 | } | ||
| 110 | |||
| 111 | impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> { | ||
| 112 | /// "Create a new Ch1 trigger pin instance. | ||
| 113 | pub fn new(pin: Peri<'d, impl TimerTriggerPin<T, C>>, pull: Pull) -> Self { | ||
| 114 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | ||
| 115 | TriggerPin { | 80 | TriggerPin { |
| 116 | _pin: pin.into(), | 81 | pin: pin.into(), |
| 117 | phantom: PhantomData, | 82 | phantom: PhantomData, |
| 118 | } | 83 | } |
| 119 | } | 84 | } |
| @@ -131,9 +96,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | |||
| 131 | /// | 96 | /// |
| 132 | /// The pulse is triggered by a channel 1 input pin on both rising and | 97 | /// The pulse is triggered by a channel 1 input pin on both rising and |
| 133 | /// falling edges. Channel 1 will unusable as an output. | 98 | /// falling edges. Channel 1 will unusable as an output. |
| 99 | #[allow(unused)] | ||
| 134 | pub fn new_ch1_edge_detect( | 100 | pub fn new_ch1_edge_detect( |
| 135 | tim: Peri<'d, T>, | 101 | tim: Peri<'d, T>, |
| 136 | _pin: TriggerPin<'d, T, Ch1>, | 102 | pin: TriggerPin<'d, T, Ch1>, |
| 137 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | 103 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, |
| 138 | freq: Hertz, | 104 | freq: Hertz, |
| 139 | pulse_end: u32, | 105 | pulse_end: u32, |
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 1e55f2919..159b5a177 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -18,15 +18,25 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> { | |||
| 18 | 18 | ||
| 19 | impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | 19 | impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { |
| 20 | /// Create a new PWM input driver. | 20 | /// Create a new PWM input driver. |
| 21 | pub fn new_ch1(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch1>>, pull: Pull, freq: Hertz) -> Self { | 21 | pub fn new_ch1<#[cfg(afio)] A>( |
| 22 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 22 | tim: Peri<'d, T>, |
| 23 | pin: Peri<'d, if_afio!(impl TimerPin<T, Ch1, A>)>, | ||
| 24 | pull: Pull, | ||
| 25 | freq: Hertz, | ||
| 26 | ) -> Self { | ||
| 27 | set_as_af!(pin, AfType::input(pull)); | ||
| 23 | 28 | ||
| 24 | Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) | 29 | Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) |
| 25 | } | 30 | } |
| 26 | 31 | ||
| 27 | /// Create a new PWM input driver. | 32 | /// Create a new PWM input driver. |
| 28 | pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch2>>, pull: Pull, freq: Hertz) -> Self { | 33 | pub fn new_ch2<#[cfg(afio)] A>( |
| 29 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 34 | tim: Peri<'d, T>, |
| 35 | pin: Peri<'d, if_afio!(impl TimerPin<T, Ch2, A>)>, | ||
| 36 | pull: Pull, | ||
| 37 | freq: Hertz, | ||
| 38 | ) -> Self { | ||
| 39 | set_as_af!(pin, AfType::input(pull)); | ||
| 30 | 40 | ||
| 31 | Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) | 41 | Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) |
| 32 | } | 42 | } |
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index eabe1b22a..82b5968b0 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -20,20 +20,21 @@ pub enum Direction { | |||
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /// Wrapper for using a pin with QEI. | 22 | /// Wrapper for using a pin with QEI. |
| 23 | pub struct QeiPin<'d, T, Channel> { | 23 | pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> { |
| 24 | _pin: Peri<'d, AnyPin>, | 24 | #[allow(unused)] |
| 25 | phantom: PhantomData<(T, Channel)>, | 25 | pin: Peri<'d, AnyPin>, |
| 26 | phantom: PhantomData<if_afio!((T, Channel, A))>, | ||
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { | 29 | impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) { |
| 29 | /// Create a new QEI pin instance. | 30 | /// Create a new QEI pin instance. |
| 30 | pub fn new(pin: Peri<'d, impl TimerPin<T, C>>) -> Self { | 31 | pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>) -> Self { |
| 31 | critical_section::with(|_| { | 32 | critical_section::with(|_| { |
| 32 | pin.set_low(); | 33 | pin.set_low(); |
| 33 | pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); | 34 | set_as_af!(pin, AfType::input(Pull::None)); |
| 34 | }); | 35 | }); |
| 35 | QeiPin { | 36 | QeiPin { |
| 36 | _pin: pin.into(), | 37 | pin: pin.into(), |
| 37 | phantom: PhantomData, | 38 | phantom: PhantomData, |
| 38 | } | 39 | } |
| 39 | } | 40 | } |
| @@ -58,7 +59,12 @@ pub struct Qei<'d, T: GeneralInstance4Channel> { | |||
| 58 | 59 | ||
| 59 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { | 60 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { |
| 60 | /// Create a new quadrature decoder driver. | 61 | /// Create a new quadrature decoder driver. |
| 61 | pub fn new(tim: Peri<'d, T>, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { | 62 | #[allow(unused)] |
| 63 | pub fn new<#[cfg(afio)] A>( | ||
| 64 | tim: Peri<'d, T>, | ||
| 65 | ch1: if_afio!(QeiPin<'d, T, Ch1, A>), | ||
| 66 | ch2: if_afio!(QeiPin<'d, T, Ch2, A>), | ||
| 67 | ) -> Self { | ||
| 62 | Self::new_inner(tim) | 68 | Self::new_inner(tim) |
| 63 | } | 69 | } |
| 64 | 70 | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index c04b1ab97..e6165e42b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -14,9 +14,10 @@ use crate::Peri; | |||
| 14 | /// PWM pin wrapper. | 14 | /// PWM pin wrapper. |
| 15 | /// | 15 | /// |
| 16 | /// This wraps a pin to make it usable with PWM. | 16 | /// This wraps a pin to make it usable with PWM. |
| 17 | pub struct PwmPin<'d, T, C> { | 17 | pub struct PwmPin<'d, T, C, #[cfg(afio)] A> { |
| 18 | _pin: Peri<'d, AnyPin>, | 18 | #[allow(unused)] |
| 19 | phantom: PhantomData<(T, C)>, | 19 | pub(crate) pin: Peri<'d, AnyPin>, |
| 20 | phantom: PhantomData<if_afio!((T, C, A))>, | ||
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | /// PWM pin config | 23 | /// PWM pin config |
| @@ -34,33 +35,33 @@ pub struct PwmPinConfig { | |||
| 34 | pub pull: Pull, | 35 | pub pull: Pull, |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { | 38 | impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(PwmPin<'d, T, C, A>) { |
| 38 | /// Create a new PWM pin instance. | 39 | /// Create a new PWM pin instance. |
| 39 | pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self { | 40 | pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, output_type: OutputType) -> Self { |
| 40 | critical_section::with(|_| { | 41 | critical_section::with(|_| { |
| 41 | pin.set_low(); | 42 | pin.set_low(); |
| 42 | pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); | 43 | set_as_af!(pin, AfType::output(output_type, Speed::VeryHigh)); |
| 43 | }); | 44 | }); |
| 44 | PwmPin { | 45 | PwmPin { |
| 45 | _pin: pin.into(), | 46 | pin: pin.into(), |
| 46 | phantom: PhantomData, | 47 | phantom: PhantomData, |
| 47 | } | 48 | } |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | /// Create a new PWM pin instance with config. | 51 | /// Create a new PWM pin instance with a specific configuration. |
| 51 | pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self { | 52 | pub fn new_with_config(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pin_config: PwmPinConfig) -> Self { |
| 52 | critical_section::with(|_| { | 53 | critical_section::with(|_| { |
| 53 | pin.set_low(); | 54 | pin.set_low(); |
| 54 | pin.set_as_af( | 55 | #[cfg(gpio_v1)] |
| 55 | pin.af_num(), | 56 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); |
| 56 | #[cfg(gpio_v1)] | 57 | #[cfg(gpio_v2)] |
| 57 | AfType::output(pin_config.output_type, pin_config.speed), | 58 | set_as_af!( |
| 58 | #[cfg(gpio_v2)] | 59 | pin, |
| 59 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | 60 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull) |
| 60 | ); | 61 | ); |
| 61 | }); | 62 | }); |
| 62 | PwmPin { | 63 | PwmPin { |
| 63 | _pin: pin.into(), | 64 | pin: pin.into(), |
| 64 | phantom: PhantomData, | 65 | phantom: PhantomData, |
| 65 | } | 66 | } |
| 66 | } | 67 | } |
| @@ -178,12 +179,13 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> { | |||
| 178 | 179 | ||
| 179 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | 180 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { |
| 180 | /// Create a new simple PWM driver. | 181 | /// Create a new simple PWM driver. |
| 181 | pub fn new( | 182 | #[allow(unused)] |
| 183 | pub fn new<#[cfg(afio)] A>( | ||
| 182 | tim: Peri<'d, T>, | 184 | tim: Peri<'d, T>, |
| 183 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 185 | ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>, |
| 184 | _ch2: Option<PwmPin<'d, T, Ch2>>, | 186 | ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>, |
| 185 | _ch3: Option<PwmPin<'d, T, Ch3>>, | 187 | ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>, |
| 186 | _ch4: Option<PwmPin<'d, T, Ch4>>, | 188 | ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>, |
| 187 | freq: Hertz, | 189 | freq: Hertz, |
| 188 | counting_mode: CountingMode, | 190 | counting_mode: CountingMode, |
| 189 | ) -> Self { | 191 | ) -> Self { |
diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs index 6f914a94e..84421f7ff 100644 --- a/embassy-stm32/src/tsc/pin_groups.rs +++ b/embassy-stm32/src/tsc/pin_groups.rs | |||
| @@ -427,7 +427,7 @@ macro_rules! impl_set_io { | |||
| 427 | pub fn $method<Role: pin_roles::Role>(&mut self, pin: Peri<'d, impl $trait<T>>) -> IOPinWithRole<$group, Role> { | 427 | pub fn $method<Role: pin_roles::Role>(&mut self, pin: Peri<'d, impl $trait<T>>) -> IOPinWithRole<$group, Role> { |
| 428 | critical_section::with(|_| { | 428 | critical_section::with(|_| { |
| 429 | pin.set_low(); | 429 | pin.set_low(); |
| 430 | pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh)); | 430 | set_as_af!(pin, AfType::output(Role::output_type(), Speed::VeryHigh)); |
| 431 | let tsc_io_pin = trait_to_io_pin!($trait); | 431 | let tsc_io_pin = trait_to_io_pin!($trait); |
| 432 | let new_pin = Pin { | 432 | let new_pin = Pin { |
| 433 | _pin: pin.into(), | 433 | _pin: pin.into(), |
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 0a80adb8f..18aff4fbd 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -490,14 +490,14 @@ impl<'d, T: Instance> PdPhy<'d, T> { | |||
| 490 | let sr = r.sr().read(); | 490 | let sr = r.sr().read(); |
| 491 | 491 | ||
| 492 | if sr.rxhrstdet() { | 492 | if sr.rxhrstdet() { |
| 493 | dma.request_stop(); | 493 | dma.request_pause(); |
| 494 | 494 | ||
| 495 | // Clean and re-enable hard reset receive interrupt. | 495 | // Clean and re-enable hard reset receive interrupt. |
| 496 | r.icr().write(|w| w.set_rxhrstdetcf(true)); | 496 | r.icr().write(|w| w.set_rxhrstdetcf(true)); |
| 497 | r.imr().modify(|w| w.set_rxhrstdetie(true)); | 497 | r.imr().modify(|w| w.set_rxhrstdetie(true)); |
| 498 | Poll::Ready(Err(RxError::HardReset)) | 498 | Poll::Ready(Err(RxError::HardReset)) |
| 499 | } else if sr.rxmsgend() { | 499 | } else if sr.rxmsgend() { |
| 500 | dma.request_stop(); | 500 | dma.request_pause(); |
| 501 | // Should be read immediately on interrupt. | 501 | // Should be read immediately on interrupt. |
| 502 | rxpaysz = r.rx_payszr().read().rxpaysz().into(); | 502 | rxpaysz = r.rx_payszr().read().rxpaysz().into(); |
| 503 | 503 | ||
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 729440c46..c734eed49 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -208,10 +208,10 @@ impl<'d> SetConfig for BufferedUartTx<'d> { | |||
| 208 | 208 | ||
| 209 | impl<'d> BufferedUart<'d> { | 209 | impl<'d> BufferedUart<'d> { |
| 210 | /// Create a new bidirectional buffered UART driver | 210 | /// Create a new bidirectional buffered UART driver |
| 211 | pub fn new<T: Instance>( | 211 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 212 | peri: Peri<'d, T>, | 212 | peri: Peri<'d, T>, |
| 213 | rx: Peri<'d, impl RxPin<T>>, | 213 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 214 | tx: Peri<'d, impl TxPin<T>>, | 214 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 215 | tx_buffer: &'d mut [u8], | 215 | tx_buffer: &'d mut [u8], |
| 216 | rx_buffer: &'d mut [u8], | 216 | rx_buffer: &'d mut [u8], |
| 217 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 217 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| @@ -231,12 +231,12 @@ impl<'d> BufferedUart<'d> { | |||
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins | 233 | /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins |
| 234 | pub fn new_with_rtscts<T: Instance>( | 234 | pub fn new_with_rtscts<T: Instance, #[cfg(afio)] A>( |
| 235 | peri: Peri<'d, T>, | 235 | peri: Peri<'d, T>, |
| 236 | rx: Peri<'d, impl RxPin<T>>, | 236 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 237 | tx: Peri<'d, impl TxPin<T>>, | 237 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 238 | rts: Peri<'d, impl RtsPin<T>>, | 238 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 239 | cts: Peri<'d, impl CtsPin<T>>, | 239 | cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>, |
| 240 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 240 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 241 | tx_buffer: &'d mut [u8], | 241 | tx_buffer: &'d mut [u8], |
| 242 | rx_buffer: &'d mut [u8], | 242 | rx_buffer: &'d mut [u8], |
| @@ -256,11 +256,11 @@ impl<'d> BufferedUart<'d> { | |||
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin | 258 | /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin |
| 259 | pub fn new_with_rts_as_de<T: Instance>( | 259 | pub fn new_with_rts_as_de<T: Instance, #[cfg(afio)] A>( |
| 260 | peri: Peri<'d, T>, | 260 | peri: Peri<'d, T>, |
| 261 | rx: Peri<'d, impl RxPin<T>>, | 261 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 262 | tx: Peri<'d, impl TxPin<T>>, | 262 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 263 | rts: Peri<'d, impl RtsPin<T>>, | 263 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 264 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 264 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 265 | tx_buffer: &'d mut [u8], | 265 | tx_buffer: &'d mut [u8], |
| 266 | rx_buffer: &'d mut [u8], | 266 | rx_buffer: &'d mut [u8], |
| @@ -280,11 +280,11 @@ impl<'d> BufferedUart<'d> { | |||
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | /// Create a new bidirectional buffered UART driver with only the request-to-send pin | 282 | /// Create a new bidirectional buffered UART driver with only the request-to-send pin |
| 283 | pub fn new_with_rts<T: Instance>( | 283 | pub fn new_with_rts<T: Instance, #[cfg(afio)] A>( |
| 284 | peri: Peri<'d, T>, | 284 | peri: Peri<'d, T>, |
| 285 | rx: Peri<'d, impl RxPin<T>>, | 285 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 286 | tx: Peri<'d, impl TxPin<T>>, | 286 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 287 | rts: Peri<'d, impl RtsPin<T>>, | 287 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 288 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 288 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 289 | tx_buffer: &'d mut [u8], | 289 | tx_buffer: &'d mut [u8], |
| 290 | rx_buffer: &'d mut [u8], | 290 | rx_buffer: &'d mut [u8], |
| @@ -305,11 +305,11 @@ impl<'d> BufferedUart<'d> { | |||
| 305 | 305 | ||
| 306 | /// Create a new bidirectional buffered UART driver with a driver-enable pin | 306 | /// Create a new bidirectional buffered UART driver with a driver-enable pin |
| 307 | #[cfg(not(any(usart_v1, usart_v2)))] | 307 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 308 | pub fn new_with_de<T: Instance>( | 308 | pub fn new_with_de<T: Instance, #[cfg(afio)] A>( |
| 309 | peri: Peri<'d, T>, | 309 | peri: Peri<'d, T>, |
| 310 | rx: Peri<'d, impl RxPin<T>>, | 310 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 311 | tx: Peri<'d, impl TxPin<T>>, | 311 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 312 | de: Peri<'d, impl DePin<T>>, | 312 | de: Peri<'d, if_afio!(impl DePin<T, A>)>, |
| 313 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 313 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 314 | tx_buffer: &'d mut [u8], | 314 | tx_buffer: &'d mut [u8], |
| 315 | rx_buffer: &'d mut [u8], | 315 | rx_buffer: &'d mut [u8], |
| @@ -340,9 +340,9 @@ impl<'d> BufferedUart<'d> { | |||
| 340 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict | 340 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict |
| 341 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 341 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 342 | #[doc(alias("HDSEL"))] | 342 | #[doc(alias("HDSEL"))] |
| 343 | pub fn new_half_duplex<T: Instance>( | 343 | pub fn new_half_duplex<T: Instance, #[cfg(afio)] A>( |
| 344 | peri: Peri<'d, T>, | 344 | peri: Peri<'d, T>, |
| 345 | tx: Peri<'d, impl TxPin<T>>, | 345 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 346 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 346 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 347 | tx_buffer: &'d mut [u8], | 347 | tx_buffer: &'d mut [u8], |
| 348 | rx_buffer: &'d mut [u8], | 348 | rx_buffer: &'d mut [u8], |
| @@ -379,9 +379,9 @@ impl<'d> BufferedUart<'d> { | |||
| 379 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 379 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 380 | #[cfg(not(any(usart_v1, usart_v2)))] | 380 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 381 | #[doc(alias("HDSEL"))] | 381 | #[doc(alias("HDSEL"))] |
| 382 | pub fn new_half_duplex_on_rx<T: Instance>( | 382 | pub fn new_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>( |
| 383 | peri: Peri<'d, T>, | 383 | peri: Peri<'d, T>, |
| 384 | rx: Peri<'d, impl RxPin<T>>, | 384 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 385 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 385 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 386 | tx_buffer: &'d mut [u8], | 386 | tx_buffer: &'d mut [u8], |
| 387 | rx_buffer: &'d mut [u8], | 387 | rx_buffer: &'d mut [u8], |
| @@ -692,6 +692,8 @@ impl<'d> BufferedUartTx<'d> { | |||
| 692 | fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> { | 692 | fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> { |
| 693 | loop { | 693 | loop { |
| 694 | let state = self.state; | 694 | let state = self.state; |
| 695 | state.tx_done.store(false, Ordering::Release); | ||
| 696 | |||
| 695 | let empty = state.tx_buf.is_empty(); | 697 | let empty = state.tx_buf.is_empty(); |
| 696 | 698 | ||
| 697 | let mut tx_writer = unsafe { state.tx_buf.writer() }; | 699 | let mut tx_writer = unsafe { state.tx_buf.writer() }; |
| @@ -713,7 +715,7 @@ impl<'d> BufferedUartTx<'d> { | |||
| 713 | fn blocking_flush(&self) -> Result<(), Error> { | 715 | fn blocking_flush(&self) -> Result<(), Error> { |
| 714 | loop { | 716 | loop { |
| 715 | let state = self.state; | 717 | let state = self.state; |
| 716 | if state.tx_buf.is_empty() { | 718 | if state.tx_done.load(Ordering::Acquire) { |
| 717 | return Ok(()); | 719 | return Ok(()); |
| 718 | } | 720 | } |
| 719 | } | 721 | } |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 5bece6d66..ff211e0c9 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -429,9 +429,9 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> { | |||
| 429 | 429 | ||
| 430 | impl<'d> UartTx<'d, Async> { | 430 | impl<'d> UartTx<'d, Async> { |
| 431 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. | 431 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. |
| 432 | pub fn new<T: Instance>( | 432 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 433 | peri: Peri<'d, T>, | 433 | peri: Peri<'d, T>, |
| 434 | tx: Peri<'d, impl TxPin<T>>, | 434 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 435 | tx_dma: Peri<'d, impl TxDma<T>>, | 435 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 436 | config: Config, | 436 | config: Config, |
| 437 | ) -> Result<Self, ConfigError> { | 437 | ) -> Result<Self, ConfigError> { |
| @@ -439,10 +439,10 @@ impl<'d> UartTx<'d, Async> { | |||
| 439 | } | 439 | } |
| 440 | 440 | ||
| 441 | /// Create a new tx-only UART with a clear-to-send pin | 441 | /// Create a new tx-only UART with a clear-to-send pin |
| 442 | pub fn new_with_cts<T: Instance>( | 442 | pub fn new_with_cts<T: Instance, #[cfg(afio)] A>( |
| 443 | peri: Peri<'d, T>, | 443 | peri: Peri<'d, T>, |
| 444 | tx: Peri<'d, impl TxPin<T>>, | 444 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 445 | cts: Peri<'d, impl CtsPin<T>>, | 445 | cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>, |
| 446 | tx_dma: Peri<'d, impl TxDma<T>>, | 446 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 447 | config: Config, | 447 | config: Config, |
| 448 | ) -> Result<Self, ConfigError> { | 448 | ) -> Result<Self, ConfigError> { |
| @@ -482,19 +482,19 @@ impl<'d> UartTx<'d, Blocking> { | |||
| 482 | /// Create a new blocking tx-only UART with no hardware flow control. | 482 | /// Create a new blocking tx-only UART with no hardware flow control. |
| 483 | /// | 483 | /// |
| 484 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. | 484 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. |
| 485 | pub fn new_blocking<T: Instance>( | 485 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 486 | peri: Peri<'d, T>, | 486 | peri: Peri<'d, T>, |
| 487 | tx: Peri<'d, impl TxPin<T>>, | 487 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 488 | config: Config, | 488 | config: Config, |
| 489 | ) -> Result<Self, ConfigError> { | 489 | ) -> Result<Self, ConfigError> { |
| 490 | Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config) | 490 | Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config) |
| 491 | } | 491 | } |
| 492 | 492 | ||
| 493 | /// Create a new blocking tx-only UART with a clear-to-send pin | 493 | /// Create a new blocking tx-only UART with a clear-to-send pin |
| 494 | pub fn new_blocking_with_cts<T: Instance>( | 494 | pub fn new_blocking_with_cts<T: Instance, #[cfg(afio)] A>( |
| 495 | peri: Peri<'d, T>, | 495 | peri: Peri<'d, T>, |
| 496 | tx: Peri<'d, impl TxPin<T>>, | 496 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 497 | cts: Peri<'d, impl CtsPin<T>>, | 497 | cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>, |
| 498 | config: Config, | 498 | config: Config, |
| 499 | ) -> Result<Self, ConfigError> { | 499 | ) -> Result<Self, ConfigError> { |
| 500 | Self::new_inner( | 500 | Self::new_inner( |
| @@ -662,10 +662,10 @@ impl<'d> UartRx<'d, Async> { | |||
| 662 | /// Create a new rx-only UART with no hardware flow control. | 662 | /// Create a new rx-only UART with no hardware flow control. |
| 663 | /// | 663 | /// |
| 664 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. | 664 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. |
| 665 | pub fn new<T: Instance>( | 665 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 666 | peri: Peri<'d, T>, | 666 | peri: Peri<'d, T>, |
| 667 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 667 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 668 | rx: Peri<'d, impl RxPin<T>>, | 668 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 669 | rx_dma: Peri<'d, impl RxDma<T>>, | 669 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 670 | config: Config, | 670 | config: Config, |
| 671 | ) -> Result<Self, ConfigError> { | 671 | ) -> Result<Self, ConfigError> { |
| @@ -673,11 +673,11 @@ impl<'d> UartRx<'d, Async> { | |||
| 673 | } | 673 | } |
| 674 | 674 | ||
| 675 | /// Create a new rx-only UART with a request-to-send pin | 675 | /// Create a new rx-only UART with a request-to-send pin |
| 676 | pub fn new_with_rts<T: Instance>( | 676 | pub fn new_with_rts<T: Instance, #[cfg(afio)] A>( |
| 677 | peri: Peri<'d, T>, | 677 | peri: Peri<'d, T>, |
| 678 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 678 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 679 | rx: Peri<'d, impl RxPin<T>>, | 679 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 680 | rts: Peri<'d, impl RtsPin<T>>, | 680 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 681 | rx_dma: Peri<'d, impl RxDma<T>>, | 681 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 682 | config: Config, | 682 | config: Config, |
| 683 | ) -> Result<Self, ConfigError> { | 683 | ) -> Result<Self, ConfigError> { |
| @@ -913,19 +913,19 @@ impl<'d> UartRx<'d, Blocking> { | |||
| 913 | /// Create a new rx-only UART with no hardware flow control. | 913 | /// Create a new rx-only UART with no hardware flow control. |
| 914 | /// | 914 | /// |
| 915 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. | 915 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. |
| 916 | pub fn new_blocking<T: Instance>( | 916 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 917 | peri: Peri<'d, T>, | 917 | peri: Peri<'d, T>, |
| 918 | rx: Peri<'d, impl RxPin<T>>, | 918 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 919 | config: Config, | 919 | config: Config, |
| 920 | ) -> Result<Self, ConfigError> { | 920 | ) -> Result<Self, ConfigError> { |
| 921 | Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config) | 921 | Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config) |
| 922 | } | 922 | } |
| 923 | 923 | ||
| 924 | /// Create a new rx-only UART with a request-to-send pin | 924 | /// Create a new rx-only UART with a request-to-send pin |
| 925 | pub fn new_blocking_with_rts<T: Instance>( | 925 | pub fn new_blocking_with_rts<T: Instance, #[cfg(afio)] A>( |
| 926 | peri: Peri<'d, T>, | 926 | peri: Peri<'d, T>, |
| 927 | rx: Peri<'d, impl RxPin<T>>, | 927 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 928 | rts: Peri<'d, impl RtsPin<T>>, | 928 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 929 | config: Config, | 929 | config: Config, |
| 930 | ) -> Result<Self, ConfigError> { | 930 | ) -> Result<Self, ConfigError> { |
| 931 | Self::new_inner( | 931 | Self::new_inner( |
| @@ -1109,10 +1109,10 @@ fn drop_tx_rx(info: &Info, state: &State) { | |||
| 1109 | 1109 | ||
| 1110 | impl<'d> Uart<'d, Async> { | 1110 | impl<'d> Uart<'d, Async> { |
| 1111 | /// Create a new bidirectional UART | 1111 | /// Create a new bidirectional UART |
| 1112 | pub fn new<T: Instance>( | 1112 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 1113 | peri: Peri<'d, T>, | 1113 | peri: Peri<'d, T>, |
| 1114 | rx: Peri<'d, impl RxPin<T>>, | 1114 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1115 | tx: Peri<'d, impl TxPin<T>>, | 1115 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1116 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1116 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1117 | tx_dma: Peri<'d, impl TxDma<T>>, | 1117 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1118 | rx_dma: Peri<'d, impl RxDma<T>>, | 1118 | rx_dma: Peri<'d, impl RxDma<T>>, |
| @@ -1132,13 +1132,13 @@ impl<'d> Uart<'d, Async> { | |||
| 1132 | } | 1132 | } |
| 1133 | 1133 | ||
| 1134 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins | 1134 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins |
| 1135 | pub fn new_with_rtscts<T: Instance>( | 1135 | pub fn new_with_rtscts<T: Instance, #[cfg(afio)] A>( |
| 1136 | peri: Peri<'d, T>, | 1136 | peri: Peri<'d, T>, |
| 1137 | rx: Peri<'d, impl RxPin<T>>, | 1137 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1138 | tx: Peri<'d, impl TxPin<T>>, | 1138 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1139 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1139 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1140 | rts: Peri<'d, impl RtsPin<T>>, | 1140 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 1141 | cts: Peri<'d, impl CtsPin<T>>, | 1141 | cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>, |
| 1142 | tx_dma: Peri<'d, impl TxDma<T>>, | 1142 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1143 | rx_dma: Peri<'d, impl RxDma<T>>, | 1143 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 1144 | config: Config, | 1144 | config: Config, |
| @@ -1158,12 +1158,12 @@ impl<'d> Uart<'d, Async> { | |||
| 1158 | 1158 | ||
| 1159 | #[cfg(not(any(usart_v1, usart_v2)))] | 1159 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1160 | /// Create a new bidirectional UART with a driver-enable pin | 1160 | /// Create a new bidirectional UART with a driver-enable pin |
| 1161 | pub fn new_with_de<T: Instance>( | 1161 | pub fn new_with_de<T: Instance, #[cfg(afio)] A>( |
| 1162 | peri: Peri<'d, T>, | 1162 | peri: Peri<'d, T>, |
| 1163 | rx: Peri<'d, impl RxPin<T>>, | 1163 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1164 | tx: Peri<'d, impl TxPin<T>>, | 1164 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1165 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1165 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1166 | de: Peri<'d, impl DePin<T>>, | 1166 | de: Peri<'d, if_afio!(impl DePin<T, A>)>, |
| 1167 | tx_dma: Peri<'d, impl TxDma<T>>, | 1167 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1168 | rx_dma: Peri<'d, impl RxDma<T>>, | 1168 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 1169 | config: Config, | 1169 | config: Config, |
| @@ -1193,9 +1193,9 @@ impl<'d> Uart<'d, Async> { | |||
| 1193 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict | 1193 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict |
| 1194 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 1194 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 1195 | #[doc(alias("HDSEL"))] | 1195 | #[doc(alias("HDSEL"))] |
| 1196 | pub fn new_half_duplex<T: Instance>( | 1196 | pub fn new_half_duplex<T: Instance, #[cfg(afio)] A>( |
| 1197 | peri: Peri<'d, T>, | 1197 | peri: Peri<'d, T>, |
| 1198 | tx: Peri<'d, impl TxPin<T>>, | 1198 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1199 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1199 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1200 | tx_dma: Peri<'d, impl TxDma<T>>, | 1200 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1201 | rx_dma: Peri<'d, impl RxDma<T>>, | 1201 | rx_dma: Peri<'d, impl RxDma<T>>, |
| @@ -1232,9 +1232,9 @@ impl<'d> Uart<'d, Async> { | |||
| 1232 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 1232 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 1233 | #[cfg(not(any(usart_v1, usart_v2)))] | 1233 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1234 | #[doc(alias("HDSEL"))] | 1234 | #[doc(alias("HDSEL"))] |
| 1235 | pub fn new_half_duplex_on_rx<T: Instance>( | 1235 | pub fn new_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>( |
| 1236 | peri: Peri<'d, T>, | 1236 | peri: Peri<'d, T>, |
| 1237 | rx: Peri<'d, impl RxPin<T>>, | 1237 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1238 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1238 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1239 | tx_dma: Peri<'d, impl TxDma<T>>, | 1239 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1240 | rx_dma: Peri<'d, impl RxDma<T>>, | 1240 | rx_dma: Peri<'d, impl RxDma<T>>, |
| @@ -1280,10 +1280,10 @@ impl<'d> Uart<'d, Async> { | |||
| 1280 | 1280 | ||
| 1281 | impl<'d> Uart<'d, Blocking> { | 1281 | impl<'d> Uart<'d, Blocking> { |
| 1282 | /// Create a new blocking bidirectional UART. | 1282 | /// Create a new blocking bidirectional UART. |
| 1283 | pub fn new_blocking<T: Instance>( | 1283 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 1284 | peri: Peri<'d, T>, | 1284 | peri: Peri<'d, T>, |
| 1285 | rx: Peri<'d, impl RxPin<T>>, | 1285 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1286 | tx: Peri<'d, impl TxPin<T>>, | 1286 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1287 | config: Config, | 1287 | config: Config, |
| 1288 | ) -> Result<Self, ConfigError> { | 1288 | ) -> Result<Self, ConfigError> { |
| 1289 | Self::new_inner( | 1289 | Self::new_inner( |
| @@ -1300,12 +1300,12 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1300 | } | 1300 | } |
| 1301 | 1301 | ||
| 1302 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins | 1302 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins |
| 1303 | pub fn new_blocking_with_rtscts<T: Instance>( | 1303 | pub fn new_blocking_with_rtscts<T: Instance, #[cfg(afio)] A>( |
| 1304 | peri: Peri<'d, T>, | 1304 | peri: Peri<'d, T>, |
| 1305 | rx: Peri<'d, impl RxPin<T>>, | 1305 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1306 | tx: Peri<'d, impl TxPin<T>>, | 1306 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1307 | rts: Peri<'d, impl RtsPin<T>>, | 1307 | rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>, |
| 1308 | cts: Peri<'d, impl CtsPin<T>>, | 1308 | cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>, |
| 1309 | config: Config, | 1309 | config: Config, |
| 1310 | ) -> Result<Self, ConfigError> { | 1310 | ) -> Result<Self, ConfigError> { |
| 1311 | Self::new_inner( | 1311 | Self::new_inner( |
| @@ -1323,11 +1323,11 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1323 | 1323 | ||
| 1324 | #[cfg(not(any(usart_v1, usart_v2)))] | 1324 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1325 | /// Create a new bidirectional UART with a driver-enable pin | 1325 | /// Create a new bidirectional UART with a driver-enable pin |
| 1326 | pub fn new_blocking_with_de<T: Instance>( | 1326 | pub fn new_blocking_with_de<T: Instance, #[cfg(afio)] A>( |
| 1327 | peri: Peri<'d, T>, | 1327 | peri: Peri<'d, T>, |
| 1328 | rx: Peri<'d, impl RxPin<T>>, | 1328 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1329 | tx: Peri<'d, impl TxPin<T>>, | 1329 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1330 | de: Peri<'d, impl DePin<T>>, | 1330 | de: Peri<'d, if_afio!(impl DePin<T, A>)>, |
| 1331 | config: Config, | 1331 | config: Config, |
| 1332 | ) -> Result<Self, ConfigError> { | 1332 | ) -> Result<Self, ConfigError> { |
| 1333 | Self::new_inner( | 1333 | Self::new_inner( |
| @@ -1354,9 +1354,9 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1354 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict | 1354 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict |
| 1355 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 1355 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 1356 | #[doc(alias("HDSEL"))] | 1356 | #[doc(alias("HDSEL"))] |
| 1357 | pub fn new_blocking_half_duplex<T: Instance>( | 1357 | pub fn new_blocking_half_duplex<T: Instance, #[cfg(afio)] A>( |
| 1358 | peri: Peri<'d, T>, | 1358 | peri: Peri<'d, T>, |
| 1359 | tx: Peri<'d, impl TxPin<T>>, | 1359 | tx: Peri<'d, if_afio!(impl TxPin<T, A>)>, |
| 1360 | mut config: Config, | 1360 | mut config: Config, |
| 1361 | readback: HalfDuplexReadback, | 1361 | readback: HalfDuplexReadback, |
| 1362 | ) -> Result<Self, ConfigError> { | 1362 | ) -> Result<Self, ConfigError> { |
| @@ -1390,9 +1390,9 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1390 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 1390 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 1391 | #[cfg(not(any(usart_v1, usart_v2)))] | 1391 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1392 | #[doc(alias("HDSEL"))] | 1392 | #[doc(alias("HDSEL"))] |
| 1393 | pub fn new_blocking_half_duplex_on_rx<T: Instance>( | 1393 | pub fn new_blocking_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>( |
| 1394 | peri: Peri<'d, T>, | 1394 | peri: Peri<'d, T>, |
| 1395 | rx: Peri<'d, impl RxPin<T>>, | 1395 | rx: Peri<'d, if_afio!(impl RxPin<T, A>)>, |
| 1396 | mut config: Config, | 1396 | mut config: Config, |
| 1397 | readback: HalfDuplexReadback, | 1397 | readback: HalfDuplexReadback, |
| 1398 | ) -> Result<Self, ConfigError> { | 1398 | ) -> Result<Self, ConfigError> { |
| @@ -1965,9 +1965,7 @@ pub use buffered::*; | |||
| 1965 | pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; | 1965 | pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; |
| 1966 | mod buffered; | 1966 | mod buffered; |
| 1967 | 1967 | ||
| 1968 | #[cfg(not(gpdma))] | ||
| 1969 | mod ringbuffered; | 1968 | mod ringbuffered; |
| 1970 | #[cfg(not(gpdma))] | ||
| 1971 | pub use ringbuffered::RingBufferedUartRx; | 1969 | pub use ringbuffered::RingBufferedUartRx; |
| 1972 | 1970 | ||
| 1973 | #[cfg(any(usart_v1, usart_v2))] | 1971 | #[cfg(any(usart_v1, usart_v2))] |
| @@ -2057,12 +2055,12 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send { | |||
| 2057 | type Interrupt: interrupt::typelevel::Interrupt; | 2055 | type Interrupt: interrupt::typelevel::Interrupt; |
| 2058 | } | 2056 | } |
| 2059 | 2057 | ||
| 2060 | pin_trait!(RxPin, Instance); | 2058 | pin_trait!(RxPin, Instance, @A); |
| 2061 | pin_trait!(TxPin, Instance); | 2059 | pin_trait!(TxPin, Instance, @A); |
| 2062 | pin_trait!(CtsPin, Instance); | 2060 | pin_trait!(CtsPin, Instance, @A); |
| 2063 | pin_trait!(RtsPin, Instance); | 2061 | pin_trait!(RtsPin, Instance, @A); |
| 2064 | pin_trait!(CkPin, Instance); | 2062 | pin_trait!(CkPin, Instance, @A); |
| 2065 | pin_trait!(DePin, Instance); | 2063 | pin_trait!(DePin, Instance, @A); |
| 2066 | 2064 | ||
| 2067 | dma_trait!(TxDma, Instance); | 2065 | dma_trait!(TxDma, Instance); |
| 2068 | dma_trait!(RxDma, Instance); | 2066 | dma_trait!(RxDma, Instance); |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 1d4a44896..5f4e87834 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -381,7 +381,7 @@ impl ReadReady for RingBufferedUartRx<'_> { | |||
| 381 | crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, | 381 | crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, |
| 382 | crate::dma::ringbuffer::Error::DmaUnsynced => { | 382 | crate::dma::ringbuffer::Error::DmaUnsynced => { |
| 383 | error!( | 383 | error!( |
| 384 | "Ringbuffer error: DmaUNsynced, driver implementation is | 384 | "Ringbuffer error: DmaUNsynced, driver implementation is |
| 385 | probably bugged please open an issue" | 385 | probably bugged please open an issue" |
| 386 | ); | 386 | ); |
| 387 | // we report this as overrun since its recoverable in the same way | 387 | // we report this as overrun since its recoverable in the same way |
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 1c3b99b93..5ce81b131 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs | |||
| @@ -34,7 +34,7 @@ macro_rules! config_ulpi_pins { | |||
| 34 | ($($pin:ident),*) => { | 34 | ($($pin:ident),*) => { |
| 35 | critical_section::with(|_| { | 35 | critical_section::with(|_| { |
| 36 | $( | 36 | $( |
| 37 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 37 | set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 38 | )* | 38 | )* |
| 39 | }) | 39 | }) |
| 40 | }; | 40 | }; |
| @@ -68,8 +68,8 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 68 | ep_out_buffer: &'d mut [u8], | 68 | ep_out_buffer: &'d mut [u8], |
| 69 | config: Config, | 69 | config: Config, |
| 70 | ) -> Self { | 70 | ) -> Self { |
| 71 | dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 71 | set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 72 | dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 72 | set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 73 | 73 | ||
| 74 | let regs = T::regs(); | 74 | let regs = T::regs(); |
| 75 | 75 | ||
| @@ -107,8 +107,8 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 107 | // For STM32U5 High speed pins need to be left in analog mode | 107 | // For STM32U5 High speed pins need to be left in analog mode |
| 108 | #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] | 108 | #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] |
| 109 | { | 109 | { |
| 110 | _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 110 | set_as_af!(_dp, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 111 | _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 111 | set_as_af!(_dm, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | let instance = OtgInstance { | 114 | let instance = OtgInstance { |
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 54596aeae..9e08d99b3 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -298,7 +298,7 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 298 | ) -> Self { | 298 | ) -> Self { |
| 299 | { | 299 | { |
| 300 | use crate::gpio::{AfType, OutputType, Speed}; | 300 | use crate::gpio::{AfType, OutputType, Speed}; |
| 301 | sof.set_as_af(sof.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 301 | set_as_af!(sof, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | Self::new(_usb, _irq, dp, dm) | 304 | Self::new(_usb, _irq, dp, dm) |
| @@ -329,8 +329,8 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 329 | #[cfg(not(stm32l1))] | 329 | #[cfg(not(stm32l1))] |
| 330 | { | 330 | { |
| 331 | use crate::gpio::{AfType, OutputType, Speed}; | 331 | use crate::gpio::{AfType, OutputType, Speed}; |
| 332 | dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 332 | set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 333 | dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 333 | set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 334 | } | 334 | } |
| 335 | #[cfg(stm32l1)] | 335 | #[cfg(stm32l1)] |
| 336 | let _ = (dp, dm); // suppress "unused" warnings. | 336 | let _ = (dp, dm); // suppress "unused" warnings. |
diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs index c96641180..2e510fada 100644 --- a/embassy-stm32/src/xspi/enums.rs +++ b/embassy-stm32/src/xspi/enums.rs | |||
| @@ -22,6 +22,7 @@ impl Into<u8> for XspiMode { | |||
| 22 | 22 | ||
| 23 | /// Xspi lane width | 23 | /// Xspi lane width |
| 24 | #[derive(Copy, Clone)] | 24 | #[derive(Copy, Clone)] |
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 25 | pub enum XspiWidth { | 26 | pub enum XspiWidth { |
| 26 | /// None | 27 | /// None |
| 27 | NONE, | 28 | NONE, |
| @@ -50,6 +51,7 @@ impl Into<u8> for XspiWidth { | |||
| 50 | /// Wrap Size | 51 | /// Wrap Size |
| 51 | #[allow(missing_docs)] | 52 | #[allow(missing_docs)] |
| 52 | #[derive(Copy, Clone)] | 53 | #[derive(Copy, Clone)] |
| 54 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 53 | pub enum WrapSize { | 55 | pub enum WrapSize { |
| 54 | None, | 56 | None, |
| 55 | _16Bytes, | 57 | _16Bytes, |
| @@ -73,6 +75,7 @@ impl Into<u8> for WrapSize { | |||
| 73 | /// Memory Type | 75 | /// Memory Type |
| 74 | #[allow(missing_docs)] | 76 | #[allow(missing_docs)] |
| 75 | #[derive(Copy, Clone)] | 77 | #[derive(Copy, Clone)] |
| 78 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 76 | pub enum MemoryType { | 79 | pub enum MemoryType { |
| 77 | Micron, | 80 | Micron, |
| 78 | Macronix, | 81 | Macronix, |
| @@ -98,6 +101,7 @@ impl Into<u8> for MemoryType { | |||
| 98 | /// Xspi memory size. | 101 | /// Xspi memory size. |
| 99 | #[allow(missing_docs)] | 102 | #[allow(missing_docs)] |
| 100 | #[derive(Copy, Clone)] | 103 | #[derive(Copy, Clone)] |
| 104 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 101 | pub enum MemorySize { | 105 | pub enum MemorySize { |
| 102 | _1KiB, | 106 | _1KiB, |
| 103 | _2KiB, | 107 | _2KiB, |
| @@ -158,6 +162,7 @@ impl Into<u8> for MemorySize { | |||
| 158 | 162 | ||
| 159 | /// Xspi Address size | 163 | /// Xspi Address size |
| 160 | #[derive(Copy, Clone)] | 164 | #[derive(Copy, Clone)] |
| 165 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 161 | pub enum AddressSize { | 166 | pub enum AddressSize { |
| 162 | /// 8-bit address | 167 | /// 8-bit address |
| 163 | _8bit, | 168 | _8bit, |
| @@ -183,6 +188,7 @@ impl Into<u8> for AddressSize { | |||
| 183 | /// Time the Chip Select line stays high. | 188 | /// Time the Chip Select line stays high. |
| 184 | #[allow(missing_docs)] | 189 | #[allow(missing_docs)] |
| 185 | #[derive(Copy, Clone)] | 190 | #[derive(Copy, Clone)] |
| 191 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 186 | pub enum ChipSelectHighTime { | 192 | pub enum ChipSelectHighTime { |
| 187 | _1Cycle, | 193 | _1Cycle, |
| 188 | _2Cycle, | 194 | _2Cycle, |
| @@ -212,6 +218,7 @@ impl Into<u8> for ChipSelectHighTime { | |||
| 212 | /// FIFO threshold. | 218 | /// FIFO threshold. |
| 213 | #[allow(missing_docs)] | 219 | #[allow(missing_docs)] |
| 214 | #[derive(Copy, Clone)] | 220 | #[derive(Copy, Clone)] |
| 221 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 215 | pub enum FIFOThresholdLevel { | 222 | pub enum FIFOThresholdLevel { |
| 216 | _1Bytes, | 223 | _1Bytes, |
| 217 | _2Bytes, | 224 | _2Bytes, |
| @@ -289,6 +296,7 @@ impl Into<u8> for FIFOThresholdLevel { | |||
| 289 | /// Dummy cycle count | 296 | /// Dummy cycle count |
| 290 | #[allow(missing_docs)] | 297 | #[allow(missing_docs)] |
| 291 | #[derive(Copy, Clone)] | 298 | #[derive(Copy, Clone)] |
| 299 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 292 | pub enum DummyCycles { | 300 | pub enum DummyCycles { |
| 293 | _0, | 301 | _0, |
| 294 | _1, | 302 | _1, |
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 60ccf3c97..901569f64 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs | |||
| @@ -23,6 +23,7 @@ use crate::{peripherals, Peri}; | |||
| 23 | 23 | ||
| 24 | /// XPSI driver config. | 24 | /// XPSI driver config. |
| 25 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub struct Config { | 27 | pub struct Config { |
| 27 | /// Fifo threshold used by the peripheral to generate the interrupt indicating data | 28 | /// Fifo threshold used by the peripheral to generate the interrupt indicating data |
| 28 | /// or space is available in the FIFO | 29 | /// or space is available in the FIFO |
| @@ -80,6 +81,8 @@ impl Default for Config { | |||
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | /// XSPI transfer configuration. | 83 | /// XSPI transfer configuration. |
| 84 | #[derive(Clone, Copy)] | ||
| 85 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 83 | pub struct TransferConfig { | 86 | pub struct TransferConfig { |
| 84 | /// Instruction width (IMODE) | 87 | /// Instruction width (IMODE) |
| 85 | pub iwidth: XspiWidth, | 88 | pub iwidth: XspiWidth, |
| @@ -110,7 +113,7 @@ pub struct TransferConfig { | |||
| 110 | 113 | ||
| 111 | /// Data width (DMODE) | 114 | /// Data width (DMODE) |
| 112 | pub dwidth: XspiWidth, | 115 | pub dwidth: XspiWidth, |
| 113 | /// Data buffer | 116 | /// Data Double Transfer rate enable |
| 114 | pub ddtr: bool, | 117 | pub ddtr: bool, |
| 115 | 118 | ||
| 116 | /// Number of dummy cycles (DCYC) | 119 | /// Number of dummy cycles (DCYC) |
| @@ -424,11 +427,6 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 424 | // Configure alternate bytes | 427 | // Configure alternate bytes |
| 425 | if let Some(ab) = command.alternate_bytes { | 428 | if let Some(ab) = command.alternate_bytes { |
| 426 | T::REGS.abr().write(|v| v.set_alternate(ab)); | 429 | T::REGS.abr().write(|v| v.set_alternate(ab)); |
| 427 | T::REGS.ccr().modify(|w| { | ||
| 428 | w.set_abmode(CcrAbmode::from_bits(command.abwidth.into())); | ||
| 429 | w.set_abdtr(command.abdtr); | ||
| 430 | w.set_absize(CcrAbsize::from_bits(command.absize.into())); | ||
| 431 | }) | ||
| 432 | } else { | 430 | } else { |
| 433 | T::REGS.ccr().modify(|w| { | 431 | T::REGS.ccr().modify(|w| { |
| 434 | // disable alternate bytes | 432 | // disable alternate bytes |
| @@ -445,14 +443,14 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 445 | if let Some(data_length) = data_len { | 443 | if let Some(data_length) = data_len { |
| 446 | T::REGS.dlr().write(|v| { | 444 | T::REGS.dlr().write(|v| { |
| 447 | v.set_dl((data_length - 1) as u32); | 445 | v.set_dl((data_length - 1) as u32); |
| 448 | }) | 446 | }); |
| 449 | } else { | 447 | } else { |
| 450 | T::REGS.dlr().write(|v| { | 448 | T::REGS.dlr().write(|v| { |
| 451 | v.set_dl((0) as u32); | 449 | v.set_dl((0) as u32); |
| 452 | }) | 450 | }); |
| 453 | } | 451 | } |
| 454 | 452 | ||
| 455 | // Configure instruction/address/data modes | 453 | // Configure instruction/address/alternate bytes/data modes |
| 456 | T::REGS.ccr().modify(|w| { | 454 | T::REGS.ccr().modify(|w| { |
| 457 | w.set_imode(CcrImode::from_bits(command.iwidth.into())); | 455 | w.set_imode(CcrImode::from_bits(command.iwidth.into())); |
| 458 | w.set_idtr(command.idtr); | 456 | w.set_idtr(command.idtr); |
| @@ -462,6 +460,10 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | |||
| 462 | w.set_addtr(command.addtr); | 460 | w.set_addtr(command.addtr); |
| 463 | w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); | 461 | w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); |
| 464 | 462 | ||
| 463 | w.set_abmode(CcrAbmode::from_bits(command.abwidth.into())); | ||
| 464 | w.set_abdtr(command.abdtr); | ||
| 465 | w.set_absize(CcrAbsize::from_bits(command.absize.into())); | ||
| 466 | |||
| 465 | w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); | 467 | w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); |
| 466 | w.set_ddtr(command.ddtr); | 468 | w.set_ddtr(command.ddtr); |
| 467 | }); | 469 | }); |
diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index a53d5f5b1..242b8b7ab 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md | |||
| @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | - Fix wakers getting dropped by `Signal::reset` | 10 | - Fix wakers getting dropped by `Signal::reset` |
| 11 | - Remove `Sized` trait bound from `MutexGuard::map` | ||
| 11 | 12 | ||
| 12 | ## 0.7.2 - 2025-08-26 | 13 | ## 0.7.2 - 2025-08-26 |
| 13 | 14 | ||
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 30a27c13f..6494da727 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml | |||
| @@ -17,6 +17,8 @@ categories = [ | |||
| 17 | [package.metadata.embassy] | 17 | [package.metadata.embassy] |
| 18 | build = [ | 18 | build = [ |
| 19 | {target = "thumbv6m-none-eabi", features = ["defmt"]}, | 19 | {target = "thumbv6m-none-eabi", features = ["defmt"]}, |
| 20 | # Xtensa builds | ||
| 21 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt"]}, | ||
| 20 | ] | 22 | ] |
| 21 | 23 | ||
| 22 | [package.metadata.embassy_docs] | 24 | [package.metadata.embassy_docs] |
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 4ce6dd987..aea682899 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs | |||
| @@ -187,7 +187,7 @@ where | |||
| 187 | T: ?Sized, | 187 | T: ?Sized, |
| 188 | { | 188 | { |
| 189 | /// Returns a locked view over a portion of the locked data. | 189 | /// Returns a locked view over a portion of the locked data. |
| 190 | pub fn map<U>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { | 190 | pub fn map<U: ?Sized>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { |
| 191 | let mutex = this.mutex; | 191 | let mutex = this.mutex; |
| 192 | let value = fun(unsafe { &mut *this.mutex.inner.get() }); | 192 | let value = fun(unsafe { &mut *this.mutex.inner.get() }); |
| 193 | // Don't run the `drop` method for MutexGuard. The ownership of the underlying | 193 | // Don't run the `drop` method for MutexGuard. The ownership of the underlying |
| @@ -279,7 +279,7 @@ where | |||
| 279 | T: ?Sized, | 279 | T: ?Sized, |
| 280 | { | 280 | { |
| 281 | /// Returns a locked view over a portion of the locked data. | 281 | /// Returns a locked view over a portion of the locked data. |
| 282 | pub fn map<U>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { | 282 | pub fn map<U: ?Sized>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { |
| 283 | let state = this.state; | 283 | let state = this.state; |
| 284 | let value = fun(unsafe { &mut *this.value }); | 284 | let value = fun(unsafe { &mut *this.value }); |
| 285 | // Don't run the `drop` method for MutexGuard. The ownership of the underlying | 285 | // Don't run the `drop` method for MutexGuard. The ownership of the underlying |
diff --git a/embassy-time-queue-utils/Cargo.toml b/embassy-time-queue-utils/Cargo.toml index e1abf1cd8..13da62874 100644 --- a/embassy-time-queue-utils/Cargo.toml +++ b/embassy-time-queue-utils/Cargo.toml | |||
| @@ -56,6 +56,9 @@ _generic-queue = [] | |||
| 56 | build = [ | 56 | build = [ |
| 57 | {target = "thumbv6m-none-eabi", features = []}, | 57 | {target = "thumbv6m-none-eabi", features = []}, |
| 58 | {target = "thumbv6m-none-eabi", features = ["generic-queue-8"]}, | 58 | {target = "thumbv6m-none-eabi", features = ["generic-queue-8"]}, |
| 59 | # Xtensa builds | ||
| 60 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = []}, | ||
| 61 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["generic-queue-8"]}, | ||
| 59 | ] | 62 | ] |
| 60 | 63 | ||
| 61 | [package.metadata.embassy_docs] | 64 | [package.metadata.embassy_docs] |
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 6ebf0a468..2d7c3c1fa 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml | |||
| @@ -17,6 +17,8 @@ categories = [ | |||
| 17 | [package.metadata.embassy] | 17 | [package.metadata.embassy] |
| 18 | build = [ | 18 | build = [ |
| 19 | {target = "thumbv6m-none-eabi", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]}, | 19 | {target = "thumbv6m-none-eabi", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]}, |
| 20 | # Xtensa builds | ||
| 21 | {group = "xtensa", build-std = ["core", "alloc"], target = "xtensa-esp32s2-none-elf", features = ["defmt", "defmt-timestamp-uptime", "mock-driver"]}, | ||
| 20 | ] | 22 | ] |
| 21 | 23 | ||
| 22 | [package.metadata.embassy_docs] | 24 | [package.metadata.embassy_docs] |
diff --git a/embassy-usb-dfu/CHANGELOG.md b/embassy-usb-dfu/CHANGELOG.md index 7e5adb1f2..0088e66fe 100644 --- a/embassy-usb-dfu/CHANGELOG.md +++ b/embassy-usb-dfu/CHANGELOG.md | |||
| @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - changed: Do not reset in the GetStatus request | 11 | - changed: Do not reset in the GetStatus request |
| 12 | - Allow enabling the `application` and `dfu` feature at the same time | ||
| 12 | 13 | ||
| 13 | ## 0.2.0 - 2025-08-27 | 14 | ## 0.2.0 - 2025-08-27 |
| 14 | 15 | ||
diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 4b7b72073..78eb2c083 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | //! Application part of DFU logic | ||
| 1 | use embassy_boot::BlockingFirmwareState; | 2 | use embassy_boot::BlockingFirmwareState; |
| 2 | use embassy_time::{Duration, Instant}; | 3 | use embassy_time::{Duration, Instant}; |
| 3 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; | 4 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index 3a390a37a..7c28d04cf 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | //! DFU bootloader part of DFU logic | ||
| 1 | use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; | 2 | use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; |
| 2 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; | 3 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
| 3 | use embassy_usb::driver::Driver; | 4 | use embassy_usb::driver::Driver; |
diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs index 54ffa7276..e9f4278b6 100644 --- a/embassy-usb-dfu/src/lib.rs +++ b/embassy-usb-dfu/src/lib.rs | |||
| @@ -6,21 +6,15 @@ mod fmt; | |||
| 6 | pub mod consts; | 6 | pub mod consts; |
| 7 | 7 | ||
| 8 | #[cfg(feature = "dfu")] | 8 | #[cfg(feature = "dfu")] |
| 9 | mod dfu; | 9 | pub mod dfu; |
| 10 | #[cfg(feature = "dfu")] | 10 | #[cfg(all(feature = "dfu", not(feature = "application")))] |
| 11 | pub use self::dfu::*; | 11 | pub use self::dfu::*; |
| 12 | 12 | ||
| 13 | #[cfg(feature = "application")] | 13 | #[cfg(feature = "application")] |
| 14 | mod application; | 14 | pub mod application; |
| 15 | #[cfg(feature = "application")] | 15 | #[cfg(all(feature = "application", not(feature = "dfu")))] |
| 16 | pub use self::application::*; | 16 | pub use self::application::*; |
| 17 | 17 | ||
| 18 | #[cfg(any( | ||
| 19 | all(feature = "dfu", feature = "application"), | ||
| 20 | not(any(feature = "dfu", feature = "application")) | ||
| 21 | ))] | ||
| 22 | compile_error!("usb-dfu must be compiled with exactly one of `dfu`, or `application` features"); | ||
| 23 | |||
| 24 | /// Provides a platform-agnostic interface for initiating a system reset. | 18 | /// Provides a platform-agnostic interface for initiating a system reset. |
| 25 | /// | 19 | /// |
| 26 | /// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a | 20 | /// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a |
diff --git a/embassy-usb-driver/CHANGELOG.md b/embassy-usb-driver/CHANGELOG.md index 15875e087..71768d7e5 100644 --- a/embassy-usb-driver/CHANGELOG.md +++ b/embassy-usb-driver/CHANGELOG.md | |||
| @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 8 | <!-- next-header --> | 8 | <!-- next-header --> |
| 9 | ## Unreleased - ReleaseDate | 9 | ## Unreleased - ReleaseDate |
| 10 | 10 | ||
| 11 | - Add `EndpointOut::read_data()` and `EndpointIn::write_data()` provided methods. | ||
| 12 | |||
| 11 | ## 0.2.0 - 2025-07-16 | 13 | ## 0.2.0 - 2025-07-16 |
| 12 | 14 | ||
| 13 | - Make USB endpoint allocator methods accept an optional `EndpointAddress`. | 15 | - Make USB endpoint allocator methods accept an optional `EndpointAddress`. |
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 99616f1ec..3ad96c61d 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs | |||
| @@ -236,6 +236,22 @@ pub trait EndpointOut: Endpoint { | |||
| 236 | /// | 236 | /// |
| 237 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. | 237 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. |
| 238 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; | 238 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; |
| 239 | |||
| 240 | /// Read until the buffer is full or we receive a short packet from the USB host returning the | ||
| 241 | /// actual length of the entire data block. | ||
| 242 | /// | ||
| 243 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet or | ||
| 244 | /// data block. | ||
| 245 | async fn read_transfer(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | ||
| 246 | let mut n = 0; | ||
| 247 | loop { | ||
| 248 | let i = self.read(&mut buf[n..]).await?; | ||
| 249 | n += i; | ||
| 250 | if i < self.info().max_packet_size as usize { | ||
| 251 | return Ok(n); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | } | ||
| 239 | } | 255 | } |
| 240 | 256 | ||
| 241 | /// USB control pipe trait. | 257 | /// USB control pipe trait. |
| @@ -349,6 +365,20 @@ pub trait ControlPipe { | |||
| 349 | pub trait EndpointIn: Endpoint { | 365 | pub trait EndpointIn: Endpoint { |
| 350 | /// Write a single packet of data to the endpoint. | 366 | /// Write a single packet of data to the endpoint. |
| 351 | async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; | 367 | async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; |
| 368 | |||
| 369 | /// Write all the data from buf to the endpoint one wMaxPacketSize chunk at a time. | ||
| 370 | /// | ||
| 371 | /// If the buffer size is evenly divisible by wMaxPacketSize, this will also ensure the | ||
| 372 | /// terminating zero-length-packet is transmitted. | ||
| 373 | async fn write_transfer(&mut self, buf: &[u8], needs_zlp: bool) -> Result<(), EndpointError> { | ||
| 374 | for chunk in buf.chunks(self.info().max_packet_size as usize) { | ||
| 375 | self.write(chunk).await?; | ||
| 376 | } | ||
| 377 | if needs_zlp && buf.len() % self.info().max_packet_size as usize == 0 { | ||
| 378 | self.write(&[]).await?; | ||
| 379 | } | ||
| 380 | Ok(()) | ||
| 381 | } | ||
| 352 | } | 382 | } |
| 353 | 383 | ||
| 354 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | 384 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] |
diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md index 9ca90f5e9..45353d907 100644 --- a/embassy-usb-synopsys-otg/CHANGELOG.md +++ b/embassy-usb-synopsys-otg/CHANGELOG.md | |||
| @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 10 | 10 | ||
| 11 | ## 0.3.1 - 2025-08-26 | 11 | ## 0.3.1 - 2025-08-26 |
| 12 | 12 | ||
| 13 | - Improve receive performance, more efficient copy from FIFO | ||
| 14 | |||
| 13 | ## 0.3.0 - 2025-07-22 | 15 | ## 0.3.0 - 2025-07-22 |
| 14 | 16 | ||
| 15 | - Bump `embassy-usb-driver` to v0.2.0 | 17 | - Bump `embassy-usb-driver` to v0.2.0 |
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 9d74c046d..6b4a87bdf 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs | |||
| @@ -76,10 +76,16 @@ pub unsafe fn on_interrupt<const MAX_EP_COUNT: usize>(r: Otg, state: &State<MAX_ | |||
| 76 | let buf = | 76 | let buf = |
| 77 | unsafe { core::slice::from_raw_parts_mut(*state.ep_states[ep_num].out_buffer.get(), len) }; | 77 | unsafe { core::slice::from_raw_parts_mut(*state.ep_states[ep_num].out_buffer.get(), len) }; |
| 78 | 78 | ||
| 79 | for chunk in buf.chunks_mut(4) { | 79 | let mut chunks = buf.chunks_exact_mut(4); |
| 80 | for chunk in &mut chunks { | ||
| 80 | // RX FIFO is shared so always read from fifo(0) | 81 | // RX FIFO is shared so always read from fifo(0) |
| 81 | let data = r.fifo(0).read().0; | 82 | let data = r.fifo(0).read().0; |
| 82 | chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]); | 83 | chunk.copy_from_slice(&data.to_ne_bytes()); |
| 84 | } | ||
| 85 | let rem = chunks.into_remainder(); | ||
| 86 | if !rem.is_empty() { | ||
| 87 | let data = r.fifo(0).read().0; | ||
| 88 | rem.copy_from_slice(&data.to_ne_bytes()[0..rem.len()]); | ||
| 83 | } | 89 | } |
| 84 | 90 | ||
| 85 | state.ep_states[ep_num].out_size.store(len as u16, Ordering::Release); | 91 | state.ep_states[ep_num].out_size.store(len as u16, Ordering::Release); |
| @@ -1229,23 +1235,19 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { | |||
| 1229 | }); | 1235 | }); |
| 1230 | 1236 | ||
| 1231 | // Write data to FIFO | 1237 | // Write data to FIFO |
| 1232 | let chunks = buf.chunks_exact(4); | ||
| 1233 | // Stash the last partial chunk | ||
| 1234 | let rem = chunks.remainder(); | ||
| 1235 | let last_chunk = (!rem.is_empty()).then(|| { | ||
| 1236 | let mut tmp = [0u8; 4]; | ||
| 1237 | tmp[0..rem.len()].copy_from_slice(rem); | ||
| 1238 | u32::from_ne_bytes(tmp) | ||
| 1239 | }); | ||
| 1240 | |||
| 1241 | let fifo = self.regs.fifo(index); | 1238 | let fifo = self.regs.fifo(index); |
| 1242 | for chunk in chunks { | 1239 | let mut chunks = buf.chunks_exact(4); |
| 1240 | for chunk in &mut chunks { | ||
| 1243 | let val = u32::from_ne_bytes(chunk.try_into().unwrap()); | 1241 | let val = u32::from_ne_bytes(chunk.try_into().unwrap()); |
| 1244 | fifo.write_value(regs::Fifo(val)); | 1242 | fifo.write_value(regs::Fifo(val)); |
| 1245 | } | 1243 | } |
| 1246 | // Write any last chunk | 1244 | // Write any last chunk |
| 1247 | if let Some(val) = last_chunk { | 1245 | let rem = chunks.remainder(); |
| 1248 | fifo.write_value(regs::Fifo(val)); | 1246 | if !rem.is_empty() { |
| 1247 | let mut tmp = [0u8; 4]; | ||
| 1248 | tmp[0..rem.len()].copy_from_slice(rem); | ||
| 1249 | let tmp = u32::from_ne_bytes(tmp); | ||
| 1250 | fifo.write_value(regs::Fifo(tmp)); | ||
| 1249 | } | 1251 | } |
| 1250 | }); | 1252 | }); |
| 1251 | 1253 | ||
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 79b27f269..579748595 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | publish = false | 7 | publish = false |
| 8 | 8 | ||
| 9 | [dependencies] | 9 | [dependencies] |
| 10 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt", "time-driver-rtc"] } | 10 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55-core0", "rt", "defmt", "time-driver-rtc"] } |
| 11 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | 11 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } |
| 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } |
diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml index 4daddbbb4..21434106a 100644 --- a/examples/mspm0c1104/Cargo.toml +++ b/examples/mspm0c1104/Cargo.toml | |||
| @@ -33,7 +33,6 @@ lto = true | |||
| 33 | codegen-units = 1 | 33 | codegen-units = 1 |
| 34 | 34 | ||
| 35 | [package.metadata.embassy] | 35 | [package.metadata.embassy] |
| 36 | skip = true # TODO: remove when we find a way to decrease the defmt buffer size in ci. | ||
| 37 | build = [ | 36 | build = [ |
| 38 | { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104" } | 37 | { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104", env = { DEFMT_RTT_BUFFER_SIZE = "72" }} |
| 39 | ] | 38 | ] |
diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs new file mode 100644 index 000000000..ceccc7c02 --- /dev/null +++ b/examples/mspm0g3507/src/bin/adc.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::adc::{self, Adc, Vrsel}; | ||
| 7 | use embassy_mspm0::{bind_interrupts, peripherals, Config}; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | ADC0 => adc::InterruptHandler<peripherals::ADC0>; | ||
| 13 | }); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) -> ! { | ||
| 17 | info!("Hello world!"); | ||
| 18 | let p = embassy_mspm0::init(Config::default()); | ||
| 19 | |||
| 20 | // Configure adc with sequence 0 to 1 | ||
| 21 | let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs); | ||
| 22 | let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PB20.into(), Vrsel::VddaVssa)]; | ||
| 23 | let mut readings = [0u16; 2]; | ||
| 24 | |||
| 25 | loop { | ||
| 26 | let r = adc.read_channel(&p.PA27).await; | ||
| 27 | info!("Raw adc PA27: {}", r); | ||
| 28 | // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be | ||
| 29 | // approximated to voltage (~0.0008 per step). | ||
| 30 | let mut x = r as u32; | ||
| 31 | x = x * 8; | ||
| 32 | info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000); | ||
| 33 | // Read a sequence of channels | ||
| 34 | adc.read_sequence(sequence.into_iter(), &mut readings).await; | ||
| 35 | info!("Raw adc sequence: {}", readings); | ||
| 36 | |||
| 37 | Timer::after_millis(400).await; | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs new file mode 100644 index 000000000..2806b98cc --- /dev/null +++ b/examples/mspm0l1306/src/bin/adc.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::adc::{self, Adc, Vrsel}; | ||
| 7 | use embassy_mspm0::{bind_interrupts, peripherals, Config}; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | ADC0 => adc::InterruptHandler<peripherals::ADC0>; | ||
| 13 | }); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) -> ! { | ||
| 17 | info!("Hello world!"); | ||
| 18 | let p = embassy_mspm0::init(Config::default()); | ||
| 19 | |||
| 20 | // Configure adc with sequence 0 to 1 | ||
| 21 | let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs); | ||
| 22 | let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)]; | ||
| 23 | let mut readings = [0u16; 2]; | ||
| 24 | |||
| 25 | loop { | ||
| 26 | let r = adc.read_channel(&p.PA27).await; | ||
| 27 | info!("Raw adc PA27: {}", r); | ||
| 28 | // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be | ||
| 29 | // approximated to voltage (~0.0008 per step). | ||
| 30 | let mut x = r as u32; | ||
| 31 | x = x * 8; | ||
| 32 | info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000); | ||
| 33 | // Read a sequence of channels | ||
| 34 | adc.read_sequence(sequence.into_iter(), &mut readings).await; | ||
| 35 | info!("Raw adc sequence: {}", readings); | ||
| 36 | |||
| 37 | Timer::after_millis(400).await; | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/examples/nrf52840-edf/.cargo/config.toml b/examples/nrf52840-edf/.cargo/config.toml new file mode 100644 index 000000000..e0b9ce59e --- /dev/null +++ b/examples/nrf52840-edf/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --chip nRF52840_xxAA" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv7em-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "debug" | ||
diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml new file mode 100644 index 000000000..1e8803233 --- /dev/null +++ b/examples/nrf52840-edf/Cargo.toml | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf52840-edf-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | publish = false | ||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | # NOTE: "scheduler-deadline" and "embassy-time-driver" features are enabled | ||
| 10 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "scheduler-deadline", "embassy-time-driver"] } | ||
| 11 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 12 | embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | ||
| 13 | |||
| 14 | defmt = "1.0.1" | ||
| 15 | defmt-rtt = "1.0.0" | ||
| 16 | |||
| 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 18 | cortex-m-rt = "0.7.0" | ||
| 19 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 20 | |||
| 21 | [profile.release] | ||
| 22 | debug = 2 | ||
| 23 | |||
| 24 | [package.metadata.embassy] | ||
| 25 | build = [ | ||
| 26 | { target = "thumbv7em-none-eabi", artifact-dir = "out/examples/nrf52840-edf" } | ||
| 27 | ] | ||
diff --git a/examples/nrf52840-edf/build.rs b/examples/nrf52840-edf/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf52840-edf/build.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
diff --git a/examples/nrf52840-edf/memory.x b/examples/nrf52840-edf/memory.x new file mode 100644 index 000000000..15b492bce --- /dev/null +++ b/examples/nrf52840-edf/memory.x | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | ||
| 5 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 6 | |||
| 7 | /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */ | ||
| 8 | /* | ||
| 9 | FLASH : ORIGIN = 0x00027000, LENGTH = 868K | ||
| 10 | RAM : ORIGIN = 0x20020000, LENGTH = 128K | ||
| 11 | */ | ||
| 12 | } | ||
diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs new file mode 100644 index 000000000..d888e17d1 --- /dev/null +++ b/examples/nrf52840-edf/src/bin/basic.rs | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | //! Basic side-by-side example of the Earliest Deadline First scheduler | ||
| 2 | //! | ||
| 3 | //! This test spawns a number of background "ambient system load" workers | ||
| 4 | //! that are constantly working, and runs two sets of trials. | ||
| 5 | //! | ||
| 6 | //! The first trial runs with no deadline set, so our trial task is at the | ||
| 7 | //! same prioritization level as the background worker tasks. | ||
| 8 | //! | ||
| 9 | //! The second trial sets a deadline, meaning that it will be given higher | ||
| 10 | //! scheduling priority than background tasks, that have no deadline set | ||
| 11 | |||
| 12 | #![no_std] | ||
| 13 | #![no_main] | ||
| 14 | |||
| 15 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 16 | |||
| 17 | use defmt::unwrap; | ||
| 18 | use embassy_executor::Spawner; | ||
| 19 | use embassy_time::{Duration, Instant, Timer}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(spawner: Spawner) { | ||
| 24 | embassy_nrf::init(Default::default()); | ||
| 25 | |||
| 26 | // Enable flash cache to remove some flash latency jitter | ||
| 27 | compiler_fence(Ordering::SeqCst); | ||
| 28 | embassy_nrf::pac::NVMC.icachecnf().write(|w| { | ||
| 29 | w.set_cacheen(true); | ||
| 30 | }); | ||
| 31 | compiler_fence(Ordering::SeqCst); | ||
| 32 | |||
| 33 | // | ||
| 34 | // Baseline system load tunables | ||
| 35 | // | ||
| 36 | |||
| 37 | // how many load tasks? More load tasks means more tasks contending | ||
| 38 | // for the runqueue | ||
| 39 | let tasks = 32; | ||
| 40 | // how long should each task work for? The longer the working time, | ||
| 41 | // the longer the max jitter possible, even when a task is prioritized, | ||
| 42 | // as EDF is still cooperative and not pre-emptive | ||
| 43 | // | ||
| 44 | // 33 ticks ~= 1ms | ||
| 45 | let work_time_ticks = 33; | ||
| 46 | // what fraction, 1/denominator, should the system be busy? | ||
| 47 | // bigger number means **less** busy | ||
| 48 | // | ||
| 49 | // 2 => 50% | ||
| 50 | // 4 => 25% | ||
| 51 | // 10 => 10% | ||
| 52 | let denominator = 2; | ||
| 53 | |||
| 54 | // Total time window, so each worker is working 1/denominator | ||
| 55 | // amount of the total time | ||
| 56 | let time_window = work_time_ticks * u64::from(tasks) * denominator; | ||
| 57 | |||
| 58 | // Spawn all of our load workers! | ||
| 59 | for i in 0..tasks { | ||
| 60 | spawner.spawn(unwrap!(load_task(i, work_time_ticks, time_window))); | ||
| 61 | } | ||
| 62 | |||
| 63 | // Let all the tasks spin up | ||
| 64 | defmt::println!("Spinning up load tasks..."); | ||
| 65 | Timer::after_secs(1).await; | ||
| 66 | |||
| 67 | // | ||
| 68 | // Trial task worker tunables | ||
| 69 | // | ||
| 70 | |||
| 71 | // How many steps should the workers under test run? | ||
| 72 | // More steps means more chances to have to wait for other tasks | ||
| 73 | // in line ahead of us. | ||
| 74 | let num_steps = 100; | ||
| 75 | |||
| 76 | // How many ticks should the worker take working on each step? | ||
| 77 | // | ||
| 78 | // 33 ticks ~= 1ms | ||
| 79 | let work_ticks = 33; | ||
| 80 | // How many ticks should the worker wait on each step? | ||
| 81 | // | ||
| 82 | // 66 ticks ~= 2ms | ||
| 83 | let idle_ticks = 66; | ||
| 84 | |||
| 85 | // How many times to repeat each trial? | ||
| 86 | let trials = 3; | ||
| 87 | |||
| 88 | // The total time a trial would take, in a perfect unloaded system | ||
| 89 | let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks); | ||
| 90 | |||
| 91 | defmt::println!(""); | ||
| 92 | defmt::println!("Starting UNPRIORITIZED worker trials"); | ||
| 93 | for _ in 0..trials { | ||
| 94 | // | ||
| 95 | // UNPRIORITIZED worker | ||
| 96 | // | ||
| 97 | defmt::println!(""); | ||
| 98 | defmt::println!("Starting unprioritized worker"); | ||
| 99 | let start = Instant::now(); | ||
| 100 | for _ in 0..num_steps { | ||
| 101 | let now = Instant::now(); | ||
| 102 | while now.elapsed().as_ticks() < work_ticks {} | ||
| 103 | Timer::after_ticks(idle_ticks).await; | ||
| 104 | } | ||
| 105 | let elapsed = start.elapsed().as_ticks(); | ||
| 106 | defmt::println!( | ||
| 107 | "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}", | ||
| 108 | theoretical, | ||
| 109 | elapsed | ||
| 110 | ); | ||
| 111 | let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0; | ||
| 112 | defmt::println!("Took {=f32}% of ideal time", ratio); | ||
| 113 | Timer::after_millis(500).await; | ||
| 114 | } | ||
| 115 | |||
| 116 | Timer::after_secs(1).await; | ||
| 117 | |||
| 118 | defmt::println!(""); | ||
| 119 | defmt::println!("Starting PRIORITIZED worker trials"); | ||
| 120 | for _ in 0..trials { | ||
| 121 | // | ||
| 122 | // PRIORITIZED worker | ||
| 123 | // | ||
| 124 | defmt::println!(""); | ||
| 125 | defmt::println!("Starting prioritized worker"); | ||
| 126 | let start = Instant::now(); | ||
| 127 | // Set the deadline to ~2x the theoretical time. In practice, setting any deadline | ||
| 128 | // here elevates the current task above all other worker tasks. | ||
| 129 | let meta = embassy_executor::Metadata::for_current_task().await; | ||
| 130 | meta.set_deadline_after(theoretical * 2); | ||
| 131 | |||
| 132 | // Perform the trial | ||
| 133 | for _ in 0..num_steps { | ||
| 134 | let now = Instant::now(); | ||
| 135 | while now.elapsed().as_ticks() < work_ticks {} | ||
| 136 | Timer::after_ticks(idle_ticks).await; | ||
| 137 | } | ||
| 138 | |||
| 139 | let elapsed = start.elapsed().as_ticks(); | ||
| 140 | defmt::println!( | ||
| 141 | "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}", | ||
| 142 | theoretical, | ||
| 143 | elapsed | ||
| 144 | ); | ||
| 145 | let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0; | ||
| 146 | defmt::println!("Took {=f32}% of ideal time", ratio); | ||
| 147 | |||
| 148 | // Unset the deadline, deadlines are not automatically cleared, and if our | ||
| 149 | // deadline is in the past, then we get very high priority! | ||
| 150 | meta.unset_deadline(); | ||
| 151 | |||
| 152 | Timer::after_millis(500).await; | ||
| 153 | } | ||
| 154 | |||
| 155 | defmt::println!(""); | ||
| 156 | defmt::println!("Trials Complete."); | ||
| 157 | } | ||
| 158 | |||
| 159 | #[embassy_executor::task(pool_size = 32)] | ||
| 160 | async fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) { | ||
| 161 | let mut last_print = Instant::now(); | ||
| 162 | let mut last_tick = last_print; | ||
| 163 | let mut variance = 0; | ||
| 164 | let mut max_variance = 0; | ||
| 165 | loop { | ||
| 166 | let tgt = last_tick + Duration::from_ticks(ttl_ticks); | ||
| 167 | assert!(tgt > Instant::now(), "fell too behind!"); | ||
| 168 | |||
| 169 | Timer::at(tgt).await; | ||
| 170 | let now = Instant::now(); | ||
| 171 | // How late are we from the target? | ||
| 172 | let var = now.duration_since(tgt).as_ticks(); | ||
| 173 | max_variance = max_variance.max(var); | ||
| 174 | variance += var; | ||
| 175 | |||
| 176 | // blocking work | ||
| 177 | while now.elapsed().as_ticks() < ticks_on {} | ||
| 178 | |||
| 179 | if last_print.elapsed() >= Duration::from_secs(1) { | ||
| 180 | defmt::trace!( | ||
| 181 | "Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}", | ||
| 182 | id, | ||
| 183 | variance, | ||
| 184 | max_variance, | ||
| 185 | ticks_on, | ||
| 186 | ); | ||
| 187 | max_variance = 0; | ||
| 188 | variance = 0; | ||
| 189 | last_print = Instant::now(); | ||
| 190 | } | ||
| 191 | |||
| 192 | last_tick = tgt; | ||
| 193 | } | ||
| 194 | } | ||
diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 719e22729..2adac7e0a 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | 3 | ||
| 5 | use {defmt_rtt as _, panic_probe as _}; | 4 | use {defmt_rtt as _, panic_probe as _}; |
| 6 | 5 | ||
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index a9339bcd3..452e83b7e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -10,8 +10,8 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } | |||
| 10 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | 11 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 13 | embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "net-driver"] } |
| 14 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 14 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet","udp", "medium-ieee802154", "proto-ipv6"] } |
| 15 | embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | 16 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } |
| 17 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | 17 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } |
diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs index d559d006a..fafa37f48 100644 --- a/examples/nrf52840/src/bin/nfct.rs +++ b/examples/nrf52840/src/bin/nfct.rs | |||
| @@ -1,11 +1,12 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::{todo, *}; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_nrf::config::HfclkSource; | 6 | use embassy_nrf::config::HfclkSource; |
| 7 | use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT}; | 7 | use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT}; |
| 8 | use embassy_nrf::{bind_interrupts, nfct}; | 8 | use embassy_nrf::{bind_interrupts, nfct}; |
| 9 | use iso14443_4::{Card, IsoDep}; | ||
| 9 | use {defmt_rtt as _, embassy_nrf as _, panic_probe as _}; | 10 | use {defmt_rtt as _, embassy_nrf as _, panic_probe as _}; |
| 10 | 11 | ||
| 11 | bind_interrupts!(struct Irqs { | 12 | bind_interrupts!(struct Irqs { |
| @@ -30,12 +31,28 @@ async fn main(_spawner: Spawner) { | |||
| 30 | 31 | ||
| 31 | let mut buf = [0u8; 256]; | 32 | let mut buf = [0u8; 256]; |
| 32 | 33 | ||
| 34 | let cc = &[ | ||
| 35 | 0x00, 0x0f, /* CCEN_HI, CCEN_LOW */ | ||
| 36 | 0x20, /* VERSION */ | ||
| 37 | 0x00, 0x7f, /* MLe_HI, MLe_LOW */ | ||
| 38 | 0x00, 0x7f, /* MLc_HI, MLc_LOW */ | ||
| 39 | /* TLV */ | ||
| 40 | 0x04, 0x06, 0xe1, 0x04, 0x00, 0x7f, 0x00, 0x00, | ||
| 41 | ]; | ||
| 42 | |||
| 43 | let ndef = &[ | ||
| 44 | 0x00, 0x10, 0xd1, 0x1, 0xc, 0x55, 0x4, 0x65, 0x6d, 0x62, 0x61, 0x73, 0x73, 0x79, 0x2e, 0x64, 0x65, 0x76, | ||
| 45 | ]; | ||
| 46 | let mut selected: &[u8] = cc; | ||
| 47 | |||
| 33 | loop { | 48 | loop { |
| 34 | info!("activating"); | 49 | info!("activating"); |
| 35 | nfc.activate().await; | 50 | nfc.activate().await; |
| 51 | info!("activated!"); | ||
| 52 | |||
| 53 | let mut nfc = IsoDep::new(iso14443_3::Logger(&mut nfc)); | ||
| 36 | 54 | ||
| 37 | loop { | 55 | loop { |
| 38 | info!("rxing"); | ||
| 39 | let n = match nfc.receive(&mut buf).await { | 56 | let n = match nfc.receive(&mut buf).await { |
| 40 | Ok(n) => n, | 57 | Ok(n) => n, |
| 41 | Err(e) => { | 58 | Err(e) => { |
| @@ -44,25 +61,51 @@ async fn main(_spawner: Spawner) { | |||
| 44 | } | 61 | } |
| 45 | }; | 62 | }; |
| 46 | let req = &buf[..n]; | 63 | let req = &buf[..n]; |
| 47 | info!("received frame {:02x}", req); | 64 | info!("iso-dep rx {:02x}", req); |
| 48 | 65 | ||
| 49 | let mut deselect = false; | 66 | let Ok(apdu) = Apdu::parse(req) else { |
| 50 | let resp = match req { | 67 | error!("apdu parse error"); |
| 51 | [0xe0, ..] => { | 68 | break; |
| 52 | info!("Got RATS, tx'ing ATS"); | 69 | }; |
| 53 | &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..] | 70 | |
| 71 | info!("apdu: {:?}", apdu); | ||
| 72 | |||
| 73 | let resp = match (apdu.cla, apdu.ins, apdu.p1, apdu.p2) { | ||
| 74 | (0, 0xa4, 4, 0) => { | ||
| 75 | info!("select app"); | ||
| 76 | &[0x90, 0x00][..] | ||
| 54 | } | 77 | } |
| 55 | [0xc2] => { | 78 | (0, 0xa4, 0, 12) => { |
| 56 | info!("Got deselect!"); | 79 | info!("select df"); |
| 57 | deselect = true; | 80 | match apdu.data { |
| 58 | &[0xc2] | 81 | [0xe1, 0x03] => { |
| 82 | selected = cc; | ||
| 83 | &[0x90, 0x00][..] | ||
| 84 | } | ||
| 85 | [0xe1, 0x04] => { | ||
| 86 | selected = ndef; | ||
| 87 | &[0x90, 0x00][..] | ||
| 88 | } | ||
| 89 | _ => todo!(), // return NOT FOUND | ||
| 90 | } | ||
| 91 | } | ||
| 92 | (0, 0xb0, p1, p2) => { | ||
| 93 | info!("read"); | ||
| 94 | let offs = u16::from_be_bytes([p1 & 0x7f, p2]) as usize; | ||
| 95 | let len = if apdu.le == 0 { usize::MAX } else { apdu.le as usize }; | ||
| 96 | let n = len.min(selected.len() - offs); | ||
| 97 | buf[..n].copy_from_slice(&selected[offs..][..n]); | ||
| 98 | buf[n..][..2].copy_from_slice(&[0x90, 0x00]); | ||
| 99 | &buf[..n + 2] | ||
| 59 | } | 100 | } |
| 60 | _ => { | 101 | _ => { |
| 61 | info!("Got unknown command!"); | 102 | info!("Got unknown command!"); |
| 62 | &[0xFF] | 103 | &[0xFF, 0xFF] |
| 63 | } | 104 | } |
| 64 | }; | 105 | }; |
| 65 | 106 | ||
| 107 | info!("iso-dep tx {:02x}", resp); | ||
| 108 | |||
| 66 | match nfc.transmit(resp).await { | 109 | match nfc.transmit(resp).await { |
| 67 | Ok(()) => {} | 110 | Ok(()) => {} |
| 68 | Err(e) => { | 111 | Err(e) => { |
| @@ -70,10 +113,211 @@ async fn main(_spawner: Spawner) { | |||
| 70 | break; | 113 | break; |
| 71 | } | 114 | } |
| 72 | } | 115 | } |
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 73 | 119 | ||
| 74 | if deselect { | 120 | #[derive(Debug, Clone, defmt::Format)] |
| 75 | break; | 121 | struct Apdu<'a> { |
| 122 | pub cla: u8, | ||
| 123 | pub ins: u8, | ||
| 124 | pub p1: u8, | ||
| 125 | pub p2: u8, | ||
| 126 | pub data: &'a [u8], | ||
| 127 | pub le: u16, | ||
| 128 | } | ||
| 129 | |||
| 130 | #[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)] | ||
| 131 | struct ApduParseError; | ||
| 132 | |||
| 133 | impl<'a> Apdu<'a> { | ||
| 134 | pub fn parse(apdu: &'a [u8]) -> Result<Self, ApduParseError> { | ||
| 135 | if apdu.len() < 4 { | ||
| 136 | return Err(ApduParseError); | ||
| 137 | } | ||
| 138 | |||
| 139 | let (data, le) = match apdu.len() - 4 { | ||
| 140 | 0 => (&[][..], 0), | ||
| 141 | 1 => (&[][..], apdu[4]), | ||
| 142 | n if n == 1 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], 0), | ||
| 143 | n if n == 2 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], apdu[apdu.len() - 1]), | ||
| 144 | _ => return Err(ApduParseError), | ||
| 145 | }; | ||
| 146 | |||
| 147 | Ok(Apdu { | ||
| 148 | cla: apdu[0], | ||
| 149 | ins: apdu[1], | ||
| 150 | p1: apdu[2], | ||
| 151 | p2: apdu[3], | ||
| 152 | data, | ||
| 153 | le: le as _, | ||
| 154 | }) | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | mod iso14443_3 { | ||
| 159 | use core::future::Future; | ||
| 160 | |||
| 161 | use defmt::info; | ||
| 162 | use embassy_nrf::nfct::{Error, NfcT}; | ||
| 163 | |||
| 164 | pub trait Card { | ||
| 165 | type Error; | ||
| 166 | async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>; | ||
| 167 | async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>; | ||
| 168 | } | ||
| 169 | |||
| 170 | impl<'a, T: Card> Card for &'a mut T { | ||
| 171 | type Error = T::Error; | ||
| 172 | |||
| 173 | fn receive(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> { | ||
| 174 | T::receive(self, buf) | ||
| 175 | } | ||
| 176 | |||
| 177 | fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> { | ||
| 178 | T::transmit(self, buf) | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | impl<'a> Card for NfcT<'a> { | ||
| 183 | type Error = Error; | ||
| 184 | |||
| 185 | fn receive(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> { | ||
| 186 | self.receive(buf) | ||
| 187 | } | ||
| 188 | |||
| 189 | fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> { | ||
| 190 | self.transmit(buf) | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | pub struct Logger<T: Card>(pub T); | ||
| 195 | |||
| 196 | impl<T: Card> Card for Logger<T> { | ||
| 197 | type Error = T::Error; | ||
| 198 | |||
| 199 | async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 200 | let n = T::receive(&mut self.0, buf).await?; | ||
| 201 | info!("<- {:02x}", &buf[..n]); | ||
| 202 | Ok(n) | ||
| 203 | } | ||
| 204 | |||
| 205 | fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> { | ||
| 206 | info!("-> {:02x}", buf); | ||
| 207 | T::transmit(&mut self.0, buf) | ||
| 208 | } | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | mod iso14443_4 { | ||
| 213 | use defmt::info; | ||
| 214 | |||
| 215 | use crate::iso14443_3; | ||
| 216 | |||
| 217 | #[derive(defmt::Format)] | ||
| 218 | pub enum Error<T> { | ||
| 219 | Deselected, | ||
| 220 | Protocol, | ||
| 221 | Lower(T), | ||
| 222 | } | ||
| 223 | |||
| 224 | pub trait Card { | ||
| 225 | type Error; | ||
| 226 | async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>; | ||
| 227 | async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>; | ||
| 228 | } | ||
| 229 | |||
| 230 | pub struct IsoDep<T: iso14443_3::Card> { | ||
| 231 | nfc: T, | ||
| 232 | |||
| 233 | /// Block count spin bit: 0 or 1 | ||
| 234 | block_num: u8, | ||
| 235 | |||
| 236 | /// true if deselected. This is permanent, you must create another IsoDep | ||
| 237 | /// instance if we get selected again. | ||
| 238 | deselected: bool, | ||
| 239 | |||
| 240 | /// last response, in case we need to retransmit. | ||
| 241 | resp: [u8; 256], | ||
| 242 | resp_len: usize, | ||
| 243 | } | ||
| 244 | |||
| 245 | impl<T: iso14443_3::Card> IsoDep<T> { | ||
| 246 | pub fn new(nfc: T) -> Self { | ||
| 247 | Self { | ||
| 248 | nfc, | ||
| 249 | block_num: 1, | ||
| 250 | deselected: false, | ||
| 251 | resp: [0u8; 256], | ||
| 252 | resp_len: 0, | ||
| 76 | } | 253 | } |
| 77 | } | 254 | } |
| 78 | } | 255 | } |
| 256 | |||
| 257 | impl<T: iso14443_3::Card> Card for IsoDep<T> { | ||
| 258 | type Error = Error<T::Error>; | ||
| 259 | |||
| 260 | async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 261 | if self.deselected { | ||
| 262 | return Err(Error::Deselected); | ||
| 263 | } | ||
| 264 | |||
| 265 | let mut temp = [0u8; 256]; | ||
| 266 | |||
| 267 | loop { | ||
| 268 | let n = self.nfc.receive(&mut temp).await.map_err(Error::Lower)?; | ||
| 269 | assert!(n != 0); | ||
| 270 | match temp[0] { | ||
| 271 | 0x02 | 0x03 => { | ||
| 272 | self.block_num ^= 0x01; | ||
| 273 | assert!(temp[0] == 0x02 | self.block_num); | ||
| 274 | buf[..n - 1].copy_from_slice(&temp[1..n]); | ||
| 275 | return Ok(n - 1); | ||
| 276 | } | ||
| 277 | 0xb2 | 0xb3 => { | ||
| 278 | if temp[0] & 0x01 != self.block_num { | ||
| 279 | info!("Got NAK, transmitting ACK."); | ||
| 280 | let resp = &[0xA2 | self.block_num]; | ||
| 281 | self.nfc.transmit(resp).await.map_err(Error::Lower)?; | ||
| 282 | } else { | ||
| 283 | info!("Got NAK, retransmitting."); | ||
| 284 | let resp: &[u8] = &self.resp[..self.resp_len]; | ||
| 285 | self.nfc.transmit(resp).await.map_err(Error::Lower)?; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | 0xe0 => { | ||
| 289 | info!("Got RATS, tx'ing ATS"); | ||
| 290 | let resp = &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80]; | ||
| 291 | self.nfc.transmit(resp).await.map_err(Error::Lower)?; | ||
| 292 | } | ||
| 293 | 0xc2 => { | ||
| 294 | info!("Got deselect!"); | ||
| 295 | self.deselected = true; | ||
| 296 | let resp = &[0xC2]; | ||
| 297 | self.nfc.transmit(resp).await.map_err(Error::Lower)?; | ||
| 298 | return Err(Error::Deselected); | ||
| 299 | } | ||
| 300 | _ => { | ||
| 301 | info!("Got unknown command {:02x}!", temp[0]); | ||
| 302 | return Err(Error::Protocol); | ||
| 303 | } | ||
| 304 | }; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error> { | ||
| 309 | if self.deselected { | ||
| 310 | return Err(Error::Deselected); | ||
| 311 | } | ||
| 312 | |||
| 313 | self.resp[0] = 0x02 | self.block_num; | ||
| 314 | self.resp[1..][..buf.len()].copy_from_slice(buf); | ||
| 315 | self.resp_len = 1 + buf.len(); | ||
| 316 | |||
| 317 | let resp: &[u8] = &self.resp[..self.resp_len]; | ||
| 318 | self.nfc.transmit(resp).await.map_err(Error::Lower)?; | ||
| 319 | |||
| 320 | Ok(()) | ||
| 321 | } | ||
| 322 | } | ||
| 79 | } | 323 | } |
diff --git a/examples/nrf52840/src/bin/sixlowpan.rs b/examples/nrf52840/src/bin/sixlowpan.rs new file mode 100644 index 000000000..00a597366 --- /dev/null +++ b/examples/nrf52840/src/bin/sixlowpan.rs | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::net::Ipv6Addr; | ||
| 5 | |||
| 6 | use defmt::{info, unwrap, warn}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_net::udp::{PacketMetadata, UdpMetadata, UdpSocket}; | ||
| 9 | use embassy_net::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv6Cidr, StackResources, StaticConfigV6}; | ||
| 10 | use embassy_nrf::config::{Config, HfclkSource}; | ||
| 11 | use embassy_nrf::rng::Rng; | ||
| 12 | use embassy_nrf::{bind_interrupts, embassy_net_802154_driver as net, peripherals, radio}; | ||
| 13 | use embassy_time::Delay; | ||
| 14 | use embedded_hal_async::delay::DelayNs; | ||
| 15 | use static_cell::StaticCell; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | RADIO => radio::InterruptHandler<peripherals::RADIO>; | ||
| 20 | RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[embassy_executor::task] | ||
| 24 | async fn ieee802154_task(runner: net::Runner<'static, peripherals::RADIO>) -> ! { | ||
| 25 | runner.run().await | ||
| 26 | } | ||
| 27 | |||
| 28 | #[embassy_executor::task] | ||
| 29 | async fn net_task(mut runner: embassy_net::Runner<'static, net::Device<'static>>) -> ! { | ||
| 30 | runner.run().await | ||
| 31 | } | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(spawner: Spawner) { | ||
| 35 | let mut config = Config::default(); | ||
| 36 | // Necessary to run the radio nrf52840 v1.11 5.4.1 | ||
| 37 | config.hfclk_source = HfclkSource::ExternalXtal; | ||
| 38 | let p = embassy_nrf::init(config); | ||
| 39 | |||
| 40 | let mac_addr: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9]; | ||
| 41 | static NRF802154_STATE: StaticCell<net::State<20, 20>> = StaticCell::new(); | ||
| 42 | let (device, runner) = net::new(mac_addr, p.RADIO, Irqs, NRF802154_STATE.init(net::State::new())) | ||
| 43 | .await | ||
| 44 | .unwrap(); | ||
| 45 | |||
| 46 | spawner.spawn(unwrap!(ieee802154_task(runner))); | ||
| 47 | |||
| 48 | // Swap these when flashing a second board | ||
| 49 | let peer = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a4); | ||
| 50 | let local = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a5); | ||
| 51 | |||
| 52 | let config = embassy_net::Config::ipv6_static(StaticConfigV6 { | ||
| 53 | address: Ipv6Cidr::new(local, 64), | ||
| 54 | gateway: None, | ||
| 55 | dns_servers: Default::default(), | ||
| 56 | }); | ||
| 57 | |||
| 58 | // Generate random seed | ||
| 59 | let mut rng = Rng::new(p.RNG, Irqs); | ||
| 60 | let mut seed = [0; 8]; | ||
| 61 | rng.blocking_fill_bytes(&mut seed); | ||
| 62 | let seed = u64::from_le_bytes(seed); | ||
| 63 | |||
| 64 | // Init network stack | ||
| 65 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | ||
| 66 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); | ||
| 67 | |||
| 68 | spawner.spawn(unwrap!(net_task(runner))); | ||
| 69 | |||
| 70 | let mut rx_buffer = [0; 2096]; | ||
| 71 | let mut tx_buffer = [0; 2096]; | ||
| 72 | let mut tx_m_buffer = [PacketMetadata::EMPTY; 5]; | ||
| 73 | let mut rx_m_buffer = [PacketMetadata::EMPTY; 5]; | ||
| 74 | |||
| 75 | let mut delay = Delay; | ||
| 76 | loop { | ||
| 77 | let mut socket = UdpSocket::new( | ||
| 78 | stack, | ||
| 79 | &mut tx_m_buffer, | ||
| 80 | &mut rx_buffer, | ||
| 81 | &mut rx_m_buffer, | ||
| 82 | &mut tx_buffer, | ||
| 83 | ); | ||
| 84 | socket | ||
| 85 | .bind(IpListenEndpoint { | ||
| 86 | addr: Some(IpAddress::Ipv6(local)), | ||
| 87 | port: 1234, | ||
| 88 | }) | ||
| 89 | .unwrap(); | ||
| 90 | let rep = UdpMetadata { | ||
| 91 | endpoint: IpEndpoint { | ||
| 92 | addr: IpAddress::Ipv6(peer), | ||
| 93 | port: 1234, | ||
| 94 | }, | ||
| 95 | local_address: Some(IpAddress::Ipv6(local)), | ||
| 96 | meta: Default::default(), | ||
| 97 | }; | ||
| 98 | |||
| 99 | info!("Listening on {:?} UDP:1234...", local); | ||
| 100 | |||
| 101 | let mut recv_buf = [0; 12]; | ||
| 102 | loop { | ||
| 103 | delay.delay_ms(2000).await; | ||
| 104 | if socket.may_recv() { | ||
| 105 | let n = match socket.recv_from(&mut recv_buf).await { | ||
| 106 | Ok((0, _)) => panic!(), | ||
| 107 | Ok((n, _)) => n, | ||
| 108 | Err(e) => { | ||
| 109 | warn!("read error: {:?}", e); | ||
| 110 | break; | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | info!("Received {:02x}", &recv_buf[..n]); | ||
| 114 | } | ||
| 115 | |||
| 116 | info!("Sending"); | ||
| 117 | socket.send_to(b"Hello World", rep).await.unwrap(); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs new file mode 100644 index 000000000..f51df2df9 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | //! This example implements a TCP echo server on port 1234 and using DHCP. | ||
| 2 | //! Send it some data, you should see it echoed back and printed in the console. | ||
| 3 | //! | ||
| 4 | //! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board. | ||
| 5 | //! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet | ||
| 6 | //! controller | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_futures::yield_now; | ||
| 14 | use embassy_net::{Stack, StackResources}; | ||
| 15 | use embassy_net_wiznet::chip::W5500; | ||
| 16 | use embassy_net_wiznet::*; | ||
| 17 | use embassy_rp::clocks::RoscRng; | ||
| 18 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 19 | use embassy_rp::peripherals::PIO0; | ||
| 20 | use embassy_rp::pio_programs::spi::Spi; | ||
| 21 | use embassy_rp::spi::{Async, Config as SpiConfig}; | ||
| 22 | use embassy_rp::{bind_interrupts, pio}; | ||
| 23 | use embassy_time::{Delay, Duration}; | ||
| 24 | use embedded_hal_bus::spi::ExclusiveDevice; | ||
| 25 | use embedded_io_async::Write; | ||
| 26 | use static_cell::StaticCell; | ||
| 27 | use {defmt_rtt as _, panic_probe as _}; | ||
| 28 | |||
| 29 | bind_interrupts!(struct Irqs { | ||
| 30 | PIO0_IRQ_0 => pio::InterruptHandler<PIO0>; | ||
| 31 | }); | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn ethernet_task( | ||
| 35 | runner: Runner< | ||
| 36 | 'static, | ||
| 37 | W5500, | ||
| 38 | ExclusiveDevice<Spi<'static, PIO0, 0, Async>, Output<'static>, Delay>, | ||
| 39 | Input<'static>, | ||
| 40 | Output<'static>, | ||
| 41 | >, | ||
| 42 | ) -> ! { | ||
| 43 | runner.run().await | ||
| 44 | } | ||
| 45 | |||
| 46 | #[embassy_executor::task] | ||
| 47 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { | ||
| 48 | runner.run().await | ||
| 49 | } | ||
| 50 | |||
| 51 | #[embassy_executor::main] | ||
| 52 | async fn main(spawner: Spawner) { | ||
| 53 | let p = embassy_rp::init(Default::default()); | ||
| 54 | let mut rng = RoscRng; | ||
| 55 | let mut led = Output::new(p.PIN_19, Level::Low); | ||
| 56 | |||
| 57 | // The W55RP20 uses a PIO unit for SPI communication, once the SPI bus has been formed using a | ||
| 58 | // PIO statemachine everything else is generally unchanged from the other examples that use the W5500 | ||
| 59 | let mosi = p.PIN_23; | ||
| 60 | let miso = p.PIN_22; | ||
| 61 | let clk = p.PIN_21; | ||
| 62 | |||
| 63 | let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); | ||
| 64 | |||
| 65 | // Construct an SPI driver backed by a PIO state machine | ||
| 66 | let mut spi_cfg = SpiConfig::default(); | ||
| 67 | spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI | ||
| 68 | // peripheral, use higher speeds at your peril | ||
| 69 | let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 70 | |||
| 71 | // Further control pins | ||
| 72 | let cs = Output::new(p.PIN_20, Level::High); | ||
| 73 | let w5500_int = Input::new(p.PIN_24, Pull::Up); | ||
| 74 | let w5500_reset = Output::new(p.PIN_25, Level::High); | ||
| 75 | |||
| 76 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 77 | static STATE: StaticCell<State<8, 8>> = StaticCell::new(); | ||
| 78 | let state = STATE.init(State::<8, 8>::new()); | ||
| 79 | let (device, runner) = embassy_net_wiznet::new( | ||
| 80 | mac_addr, | ||
| 81 | state, | ||
| 82 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 83 | w5500_int, | ||
| 84 | w5500_reset, | ||
| 85 | ) | ||
| 86 | .await | ||
| 87 | .unwrap(); | ||
| 88 | spawner.spawn(unwrap!(ethernet_task(runner))); | ||
| 89 | |||
| 90 | // Generate random seed | ||
| 91 | let seed = rng.next_u64(); | ||
| 92 | |||
| 93 | // Init network stack | ||
| 94 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | ||
| 95 | let (stack, runner) = embassy_net::new( | ||
| 96 | device, | ||
| 97 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 98 | RESOURCES.init(StackResources::new()), | ||
| 99 | seed, | ||
| 100 | ); | ||
| 101 | |||
| 102 | // Launch network task | ||
| 103 | spawner.spawn(unwrap!(net_task(runner))); | ||
| 104 | |||
| 105 | info!("Waiting for DHCP..."); | ||
| 106 | let cfg = wait_for_config(stack).await; | ||
| 107 | let local_addr = cfg.address.address(); | ||
| 108 | info!("IP address: {:?}", local_addr); | ||
| 109 | |||
| 110 | let mut rx_buffer = [0; 4096]; | ||
| 111 | let mut tx_buffer = [0; 4096]; | ||
| 112 | let mut buf = [0; 4096]; | ||
| 113 | loop { | ||
| 114 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 115 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 116 | |||
| 117 | led.set_low(); | ||
| 118 | info!("Listening on TCP:1234..."); | ||
| 119 | if let Err(e) = socket.accept(1234).await { | ||
| 120 | warn!("accept error: {:?}", e); | ||
| 121 | continue; | ||
| 122 | } | ||
| 123 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 124 | led.set_high(); | ||
| 125 | |||
| 126 | loop { | ||
| 127 | let n = match socket.read(&mut buf).await { | ||
| 128 | Ok(0) => { | ||
| 129 | warn!("read EOF"); | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | Ok(n) => n, | ||
| 133 | Err(e) => { | ||
| 134 | warn!("{:?}", e); | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | }; | ||
| 138 | info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); | ||
| 139 | |||
| 140 | if let Err(e) = socket.write_all(&buf[..n]).await { | ||
| 141 | warn!("write error: {:?}", e); | ||
| 142 | break; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { | ||
| 149 | loop { | ||
| 150 | if let Some(config) = stack.config_v4() { | ||
| 151 | return config.clone(); | ||
| 152 | } | ||
| 153 | yield_now().await; | ||
| 154 | } | ||
| 155 | } | ||
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs index 379e2b8f9..102f13c45 100644 --- a/examples/rp/src/bin/pio_onewire.rs +++ b/examples/rp/src/bin/pio_onewire.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | //! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. | 1 | //! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. |
| 2 | //! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example. | ||
| 2 | 3 | ||
| 3 | #![no_std] | 4 | #![no_std] |
| 4 | #![no_main] | 5 | #![no_main] |
diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs new file mode 100644 index 000000000..fd076dee0 --- /dev/null +++ b/examples/rp/src/bin/pio_onewire_parasite.rs | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | //! This example shows how you can use PIO to read one or more `DS18B20` | ||
| 2 | //! one-wire temperature sensors using parasite power. | ||
| 3 | //! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet. | ||
| 4 | //! For externally powered sensors, use the pio_onewire.rs example. | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::peripherals::PIO0; | ||
| 12 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 13 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; | ||
| 14 | use embassy_time::Duration; | ||
| 15 | use heapless::Vec; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_rp::init(Default::default()); | ||
| 25 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 26 | |||
| 27 | let prg = PioOneWireProgram::new(&mut pio.common); | ||
| 28 | let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); | ||
| 29 | |||
| 30 | info!("Starting onewire search"); | ||
| 31 | |||
| 32 | let mut devices = Vec::<u64, 10>::new(); | ||
| 33 | let mut search = PioOneWireSearch::new(); | ||
| 34 | for _ in 0..10 { | ||
| 35 | if !search.is_finished() { | ||
| 36 | if let Some(address) = search.next(&mut onewire).await { | ||
| 37 | if crc8(&address.to_le_bytes()) == 0 { | ||
| 38 | info!("Found address: {:x}", address); | ||
| 39 | let _ = devices.push(address); | ||
| 40 | } else { | ||
| 41 | warn!("Found invalid address: {:x}", address); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | info!("Search done, found {} devices", devices.len()); | ||
| 48 | |||
| 49 | loop { | ||
| 50 | // Read all devices one by one | ||
| 51 | for device in &devices { | ||
| 52 | onewire.reset().await; | ||
| 53 | onewire.write_bytes(&[0x55]).await; // Match rom | ||
| 54 | onewire.write_bytes(&device.to_le_bytes()).await; | ||
| 55 | // 750 ms delay required for default 12-bit resolution. | ||
| 56 | onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await; | ||
| 57 | |||
| 58 | onewire.reset().await; | ||
| 59 | onewire.write_bytes(&[0x55]).await; // Match rom | ||
| 60 | onewire.write_bytes(&device.to_le_bytes()).await; | ||
| 61 | onewire.write_bytes(&[0xBE]).await; // Read scratchpad | ||
| 62 | |||
| 63 | let mut data = [0; 9]; | ||
| 64 | onewire.read_bytes(&mut data).await; | ||
| 65 | if crc8(&data) == 0 { | ||
| 66 | let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; | ||
| 67 | info!("Read device {:x}: {} deg C", device, temp); | ||
| 68 | } else { | ||
| 69 | warn!("Reading device {:x} failed. {:02x}", device, data); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | fn crc8(data: &[u8]) -> u8 { | ||
| 76 | let mut crc = 0; | ||
| 77 | for b in data { | ||
| 78 | let mut data_byte = *b; | ||
| 79 | for _ in 0..8 { | ||
| 80 | let temp = (crc ^ data_byte) & 0x01; | ||
| 81 | crc >>= 1; | ||
| 82 | if temp != 0 { | ||
| 83 | crc ^= 0x8C; | ||
| 84 | } | ||
| 85 | data_byte >>= 1; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | crc | ||
| 89 | } | ||
diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs new file mode 100644 index 000000000..4218327ec --- /dev/null +++ b/examples/rp/src/bin/pio_spi.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | //! This example shows how to use a PIO state machine as an additional SPI | ||
| 2 | //! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is | ||
| 3 | //! specified in this example. | ||
| 4 | //! | ||
| 5 | //! If you connect pin 6 and 7 you should get the same data back. | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::peripherals::PIO0; | ||
| 13 | use embassy_rp::pio_programs::spi::Spi; | ||
| 14 | use embassy_rp::spi::Config; | ||
| 15 | use embassy_rp::{bind_interrupts, pio}; | ||
| 16 | use embassy_time::Timer; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | PIO0_IRQ_0 => pio::InterruptHandler<PIO0>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let p = embassy_rp::init(Default::default()); | ||
| 26 | info!("Hello World!"); | ||
| 27 | |||
| 28 | // These pins are routed to different hardware SPI peripherals, but we can | ||
| 29 | // use them together regardless | ||
| 30 | let mosi = p.PIN_6; // SPI0 SCLK | ||
| 31 | let miso = p.PIN_7; // SPI0 MOSI | ||
| 32 | let clk = p.PIN_8; // SPI1 MISO | ||
| 33 | |||
| 34 | let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); | ||
| 35 | |||
| 36 | // Construct an SPI driver backed by a PIO state machine | ||
| 37 | let mut spi = Spi::new_blocking(&mut common, sm0, clk, mosi, miso, Config::default()); | ||
| 38 | |||
| 39 | loop { | ||
| 40 | let tx_buf = [1_u8, 2, 3, 4, 5, 6]; | ||
| 41 | let mut rx_buf = [0_u8; 6]; | ||
| 42 | |||
| 43 | spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap(); | ||
| 44 | info!("{:?}", rx_buf); | ||
| 45 | |||
| 46 | Timer::after_secs(1).await; | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs new file mode 100644 index 000000000..18b57d26e --- /dev/null +++ b/examples/rp/src/bin/pio_spi_async.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | //! This example shows how to use a PIO state machine as an additional SPI | ||
| 2 | //! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is | ||
| 3 | //! specified in this example. | ||
| 4 | //! | ||
| 5 | //! If you connect pin 6 and 7 you should get the same data back. | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::peripherals::PIO0; | ||
| 13 | use embassy_rp::pio_programs::spi::Spi; | ||
| 14 | use embassy_rp::spi::Config; | ||
| 15 | use embassy_rp::{bind_interrupts, pio}; | ||
| 16 | use embassy_time::Timer; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | PIO0_IRQ_0 => pio::InterruptHandler<PIO0>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let p = embassy_rp::init(Default::default()); | ||
| 26 | info!("Hello World!"); | ||
| 27 | |||
| 28 | // These pins are routed to different hardware SPI peripherals, but we can | ||
| 29 | // use them together regardless | ||
| 30 | let mosi = p.PIN_6; // SPI0 SCLK | ||
| 31 | let miso = p.PIN_7; // SPI0 MOSI | ||
| 32 | let clk = p.PIN_8; // SPI1 MISO | ||
| 33 | |||
| 34 | let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); | ||
| 35 | |||
| 36 | // Construct an SPI driver backed by a PIO state machine | ||
| 37 | let mut spi = Spi::new( | ||
| 38 | &mut common, | ||
| 39 | sm0, | ||
| 40 | clk, | ||
| 41 | mosi, | ||
| 42 | miso, | ||
| 43 | p.DMA_CH0, | ||
| 44 | p.DMA_CH1, | ||
| 45 | Config::default(), | ||
| 46 | ); | ||
| 47 | |||
| 48 | loop { | ||
| 49 | let tx_buf = [1_u8, 2, 3, 4, 5, 6]; | ||
| 50 | let mut rx_buf = [0_u8; 6]; | ||
| 51 | |||
| 52 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 53 | info!("{:?}", rx_buf); | ||
| 54 | |||
| 55 | Timer::after_secs(1).await; | ||
| 56 | } | ||
| 57 | } | ||
diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index e9a5e43a8..1692bdf36 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs | |||
| @@ -5,16 +5,22 @@ | |||
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; | 9 | use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; |
| 9 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 12 | ||
| 13 | // Bind the RTC interrupt to the handler | ||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | RTC_IRQ => embassy_rp::rtc::InterruptHandler; | ||
| 16 | }); | ||
| 17 | |||
| 12 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 14 | let p = embassy_rp::init(Default::default()); | 20 | let p = embassy_rp::init(Default::default()); |
| 15 | info!("Wait for 20s"); | 21 | info!("Wait for 20s"); |
| 16 | 22 | ||
| 17 | let mut rtc = Rtc::new(p.RTC); | 23 | let mut rtc = Rtc::new(p.RTC, Irqs); |
| 18 | 24 | ||
| 19 | if !rtc.is_running() { | 25 | if !rtc.is_running() { |
| 20 | info!("Start RTC"); | 26 | info!("Start RTC"); |
diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs new file mode 100644 index 000000000..94b5fbd27 --- /dev/null +++ b/examples/rp/src/bin/rtc_alarm.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | //! This example shows how to use RTC (Real Time Clock) for scheduling alarms and reacting to them. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_futures::select::{select, Either}; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; | ||
| 11 | use embassy_time::Timer; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | // Bind the RTC interrupt to the handler | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | RTC_IRQ => embassy_rp::rtc::InterruptHandler; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_rp::init(Default::default()); | ||
| 22 | let mut rtc = Rtc::new(p.RTC, Irqs); | ||
| 23 | |||
| 24 | if !rtc.is_running() { | ||
| 25 | info!("Start RTC"); | ||
| 26 | let now = DateTime { | ||
| 27 | year: 2000, | ||
| 28 | month: 1, | ||
| 29 | day: 1, | ||
| 30 | day_of_week: DayOfWeek::Saturday, | ||
| 31 | hour: 0, | ||
| 32 | minute: 0, | ||
| 33 | second: 0, | ||
| 34 | }; | ||
| 35 | rtc.set_datetime(now).unwrap(); | ||
| 36 | } | ||
| 37 | |||
| 38 | loop { | ||
| 39 | // Wait for 5 seconds or until the alarm is triggered | ||
| 40 | match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { | ||
| 41 | // Timer expired | ||
| 42 | Either::First(_) => { | ||
| 43 | let dt = rtc.now().unwrap(); | ||
| 44 | info!( | ||
| 45 | "Now: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 46 | dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, | ||
| 47 | ); | ||
| 48 | |||
| 49 | // See if the alarm is already scheduled, if not, schedule it | ||
| 50 | if rtc.alarm_scheduled().is_none() { | ||
| 51 | info!("Scheduling alarm for 30 seconds from now"); | ||
| 52 | rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); | ||
| 53 | info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | // Alarm triggered | ||
| 57 | Either::Second(_) => { | ||
| 58 | let dt = rtc.now().unwrap(); | ||
| 59 | info!( | ||
| 60 | "ALARM TRIGGERED! Now: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 61 | dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, | ||
| 62 | ); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
diff --git a/examples/rp235x/src/bin/pio_i2s_rx.rs b/examples/rp235x/src/bin/pio_i2s_rx.rs new file mode 100644 index 000000000..c3f505b13 --- /dev/null +++ b/examples/rp235x/src/bin/pio_i2s_rx.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | //! This example shows receiving audio from a connected I2S microphone (or other audio source) | ||
| 2 | //! using the PIO module of the RP235x. | ||
| 3 | //! | ||
| 4 | //! | ||
| 5 | //! Connect the i2s microphone as follows: | ||
| 6 | //! bclk : GPIO 18 | ||
| 7 | //! lrc : GPIO 19 | ||
| 8 | //! din : GPIO 20 | ||
| 9 | //! Then hold down the boot select button to begin receiving audio. Received I2S words will be written to | ||
| 10 | //! buffers for the left and right channels for use in your application, whether that's storage or | ||
| 11 | //! further processing | ||
| 12 | //! | ||
| 13 | //! Note the const USE_ONBOARD_PULLDOWN is by default set to false, meaning an external | ||
| 14 | //! pull-down resistor is being used on the data pin if required by the mic being used. | ||
| 15 | |||
| 16 | #![no_std] | ||
| 17 | #![no_main] | ||
| 18 | use core::mem; | ||
| 19 | |||
| 20 | use defmt::*; | ||
| 21 | use embassy_executor::Spawner; | ||
| 22 | use embassy_rp::bind_interrupts; | ||
| 23 | use embassy_rp::peripherals::PIO0; | ||
| 24 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 25 | use embassy_rp::pio_programs::i2s::{PioI2sIn, PioI2sInProgram}; | ||
| 26 | use static_cell::StaticCell; | ||
| 27 | use {defmt_rtt as _, panic_probe as _}; | ||
| 28 | |||
| 29 | bind_interrupts!(struct Irqs { | ||
| 30 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 31 | }); | ||
| 32 | |||
| 33 | const SAMPLE_RATE: u32 = 48_000; | ||
| 34 | const BIT_DEPTH: u32 = 16; | ||
| 35 | const CHANNELS: u32 = 2; | ||
| 36 | const USE_ONBOARD_PULLDOWN: bool = false; // whether or not to use the onboard pull-down resistor, | ||
| 37 | // which has documented issues on many RP235x boards | ||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(_spawner: Spawner) { | ||
| 40 | let p = embassy_rp::init(Default::default()); | ||
| 41 | |||
| 42 | // Setup pio state machine for i2s input | ||
| 43 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 44 | |||
| 45 | let bit_clock_pin = p.PIN_18; | ||
| 46 | let left_right_clock_pin = p.PIN_19; | ||
| 47 | let data_pin = p.PIN_20; | ||
| 48 | |||
| 49 | let program = PioI2sInProgram::new(&mut common); | ||
| 50 | let mut i2s = PioI2sIn::new( | ||
| 51 | &mut common, | ||
| 52 | sm0, | ||
| 53 | p.DMA_CH0, | ||
| 54 | USE_ONBOARD_PULLDOWN, | ||
| 55 | data_pin, | ||
| 56 | bit_clock_pin, | ||
| 57 | left_right_clock_pin, | ||
| 58 | SAMPLE_RATE, | ||
| 59 | BIT_DEPTH, | ||
| 60 | CHANNELS, | ||
| 61 | &program, | ||
| 62 | ); | ||
| 63 | |||
| 64 | // create two audio buffers (back and front) which will take turns being | ||
| 65 | // filled with new audio data from the PIO fifo using DMA | ||
| 66 | const BUFFER_SIZE: usize = 960; | ||
| 67 | static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new(); | ||
| 68 | let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]); | ||
| 69 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); | ||
| 70 | |||
| 71 | loop { | ||
| 72 | // trigger transfer of front buffer data to the pio fifo | ||
| 73 | // but don't await the returned future, yet | ||
| 74 | let dma_future = i2s.read(front_buffer); | ||
| 75 | // now await the dma future. once the dma finishes, the next buffer needs to be queued | ||
| 76 | // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us | ||
| 77 | dma_future.await; | ||
| 78 | info!("Received I2S data word: {:?}", &front_buffer); | ||
| 79 | mem::swap(&mut back_buffer, &mut front_buffer); | ||
| 80 | } | ||
| 81 | } | ||
diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs index 991510851..102f13c45 100644 --- a/examples/rp235x/src/bin/pio_onewire.rs +++ b/examples/rp235x/src/bin/pio_onewire.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | //! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. | 1 | //! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. |
| 2 | //! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example. | ||
| 2 | 3 | ||
| 3 | #![no_std] | 4 | #![no_std] |
| 4 | #![no_main] | 5 | #![no_main] |
| @@ -6,9 +7,10 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 7 | use embassy_rp::bind_interrupts; | 8 | use embassy_rp::bind_interrupts; |
| 8 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 9 | use embassy_rp::pio::{self, InterruptHandler, Pio}; | 10 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 10 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; | 11 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; |
| 11 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 13 | use heapless::Vec; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 15 | ||
| 14 | bind_interrupts!(struct Irqs { | 16 | bind_interrupts!(struct Irqs { |
| @@ -21,63 +23,66 @@ async fn main(_spawner: Spawner) { | |||
| 21 | let mut pio = Pio::new(p.PIO0, Irqs); | 23 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 22 | 24 | ||
| 23 | let prg = PioOneWireProgram::new(&mut pio.common); | 25 | let prg = PioOneWireProgram::new(&mut pio.common); |
| 24 | let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); | 26 | let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); |
| 25 | 27 | ||
| 26 | let mut sensor = Ds18b20::new(onewire); | 28 | info!("Starting onewire search"); |
| 27 | 29 | ||
| 28 | loop { | 30 | let mut devices = Vec::<u64, 10>::new(); |
| 29 | sensor.start().await; // Start a new measurement | 31 | let mut search = PioOneWireSearch::new(); |
| 30 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish | 32 | for _ in 0..10 { |
| 31 | match sensor.temperature().await { | 33 | if !search.is_finished() { |
| 32 | Ok(temp) => info!("temp = {:?} deg C", temp), | 34 | if let Some(address) = search.next(&mut onewire).await { |
| 33 | _ => error!("sensor error"), | 35 | if crc8(&address.to_le_bytes()) == 0 { |
| 36 | info!("Found addres: {:x}", address); | ||
| 37 | let _ = devices.push(address); | ||
| 38 | } else { | ||
| 39 | warn!("Found invalid address: {:x}", address); | ||
| 40 | } | ||
| 41 | } | ||
| 34 | } | 42 | } |
| 35 | Timer::after_secs(1).await; | ||
| 36 | } | 43 | } |
| 37 | } | ||
| 38 | 44 | ||
| 39 | /// DS18B20 temperature sensor driver | 45 | info!("Search done, found {} devices", devices.len()); |
| 40 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { | ||
| 41 | wire: PioOneWire<'d, PIO, SM>, | ||
| 42 | } | ||
| 43 | 46 | ||
| 44 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | 47 | loop { |
| 45 | pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { | 48 | onewire.reset().await; |
| 46 | Self { wire } | 49 | // Skip rom and trigger conversion, we can trigger all devices on the bus immediately |
| 47 | } | 50 | onewire.write_bytes(&[0xCC, 0x44]).await; |
| 48 | 51 | ||
| 49 | /// Calculate CRC8 of the data | 52 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish |
| 50 | fn crc8(data: &[u8]) -> u8 { | 53 | |
| 51 | let mut temp; | 54 | // Read all devices one by one |
| 52 | let mut data_byte; | 55 | for device in &devices { |
| 53 | let mut crc = 0; | 56 | onewire.reset().await; |
| 54 | for b in data { | 57 | onewire.write_bytes(&[0x55]).await; // Match rom |
| 55 | data_byte = *b; | 58 | onewire.write_bytes(&device.to_le_bytes()).await; |
| 56 | for _ in 0..8 { | 59 | onewire.write_bytes(&[0xBE]).await; // Read scratchpad |
| 57 | temp = (crc ^ data_byte) & 0x01; | 60 | |
| 58 | crc >>= 1; | 61 | let mut data = [0; 9]; |
| 59 | if temp != 0 { | 62 | onewire.read_bytes(&mut data).await; |
| 60 | crc ^= 0x8C; | 63 | if crc8(&data) == 0 { |
| 61 | } | 64 | let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; |
| 62 | data_byte >>= 1; | 65 | info!("Read device {:x}: {} deg C", device, temp); |
| 66 | } else { | ||
| 67 | warn!("Reading device {:x} failed", device); | ||
| 63 | } | 68 | } |
| 64 | } | 69 | } |
| 65 | crc | 70 | Timer::after_secs(1).await; |
| 66 | } | ||
| 67 | |||
| 68 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 69 | pub async fn start(&mut self) { | ||
| 70 | self.wire.write_bytes(&[0xCC, 0x44]).await; | ||
| 71 | } | 71 | } |
| 72 | } | ||
| 72 | 73 | ||
| 73 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | 74 | fn crc8(data: &[u8]) -> u8 { |
| 74 | pub async fn temperature(&mut self) -> Result<f32, ()> { | 75 | let mut crc = 0; |
| 75 | self.wire.write_bytes(&[0xCC, 0xBE]).await; | 76 | for b in data { |
| 76 | let mut data = [0; 9]; | 77 | let mut data_byte = *b; |
| 77 | self.wire.read_bytes(&mut data).await; | 78 | for _ in 0..8 { |
| 78 | match Self::crc8(&data) == 0 { | 79 | let temp = (crc ^ data_byte) & 0x01; |
| 79 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), | 80 | crc >>= 1; |
| 80 | false => Err(()), | 81 | if temp != 0 { |
| 82 | crc ^= 0x8C; | ||
| 83 | } | ||
| 84 | data_byte >>= 1; | ||
| 81 | } | 85 | } |
| 82 | } | 86 | } |
| 87 | crc | ||
| 83 | } | 88 | } |
diff --git a/examples/rp235x/src/bin/pio_onewire_parasite.rs b/examples/rp235x/src/bin/pio_onewire_parasite.rs new file mode 100644 index 000000000..fd076dee0 --- /dev/null +++ b/examples/rp235x/src/bin/pio_onewire_parasite.rs | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | //! This example shows how you can use PIO to read one or more `DS18B20` | ||
| 2 | //! one-wire temperature sensors using parasite power. | ||
| 3 | //! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet. | ||
| 4 | //! For externally powered sensors, use the pio_onewire.rs example. | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::peripherals::PIO0; | ||
| 12 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 13 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; | ||
| 14 | use embassy_time::Duration; | ||
| 15 | use heapless::Vec; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_rp::init(Default::default()); | ||
| 25 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 26 | |||
| 27 | let prg = PioOneWireProgram::new(&mut pio.common); | ||
| 28 | let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); | ||
| 29 | |||
| 30 | info!("Starting onewire search"); | ||
| 31 | |||
| 32 | let mut devices = Vec::<u64, 10>::new(); | ||
| 33 | let mut search = PioOneWireSearch::new(); | ||
| 34 | for _ in 0..10 { | ||
| 35 | if !search.is_finished() { | ||
| 36 | if let Some(address) = search.next(&mut onewire).await { | ||
| 37 | if crc8(&address.to_le_bytes()) == 0 { | ||
| 38 | info!("Found address: {:x}", address); | ||
| 39 | let _ = devices.push(address); | ||
| 40 | } else { | ||
| 41 | warn!("Found invalid address: {:x}", address); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | info!("Search done, found {} devices", devices.len()); | ||
| 48 | |||
| 49 | loop { | ||
| 50 | // Read all devices one by one | ||
| 51 | for device in &devices { | ||
| 52 | onewire.reset().await; | ||
| 53 | onewire.write_bytes(&[0x55]).await; // Match rom | ||
| 54 | onewire.write_bytes(&device.to_le_bytes()).await; | ||
| 55 | // 750 ms delay required for default 12-bit resolution. | ||
| 56 | onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await; | ||
| 57 | |||
| 58 | onewire.reset().await; | ||
| 59 | onewire.write_bytes(&[0x55]).await; // Match rom | ||
| 60 | onewire.write_bytes(&device.to_le_bytes()).await; | ||
| 61 | onewire.write_bytes(&[0xBE]).await; // Read scratchpad | ||
| 62 | |||
| 63 | let mut data = [0; 9]; | ||
| 64 | onewire.read_bytes(&mut data).await; | ||
| 65 | if crc8(&data) == 0 { | ||
| 66 | let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; | ||
| 67 | info!("Read device {:x}: {} deg C", device, temp); | ||
| 68 | } else { | ||
| 69 | warn!("Reading device {:x} failed. {:02x}", device, data); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | fn crc8(data: &[u8]) -> u8 { | ||
| 76 | let mut crc = 0; | ||
| 77 | for b in data { | ||
| 78 | let mut data_byte = *b; | ||
| 79 | for _ in 0..8 { | ||
| 80 | let temp = (crc ^ data_byte) & 0x01; | ||
| 81 | crc >>= 1; | ||
| 82 | if temp != 0 { | ||
| 83 | crc ^= 0x8C; | ||
| 84 | } | ||
| 85 | data_byte >>= 1; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | crc | ||
| 89 | } | ||
diff --git a/examples/rp235x/src/bin/psram.rs b/examples/rp235x/src/bin/psram.rs new file mode 100644 index 000000000..716ac7695 --- /dev/null +++ b/examples/rp235x/src/bin/psram.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | //! This example tests an APS6404L PSRAM chip connected to the RP235x | ||
| 2 | //! It fills the PSRAM with alternating patterns and reads back a value | ||
| 3 | //! | ||
| 4 | //! In this example, the PSRAM CS is connected to Pin 0. | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | |||
| 9 | use core::slice; | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let config = embassy_rp::config::Config::default(); | ||
| 19 | let p = embassy_rp::init(config); | ||
| 20 | let psram_config = embassy_rp::psram::Config::aps6404l(); | ||
| 21 | let psram = embassy_rp::psram::Psram::new(embassy_rp::qmi_cs1::QmiCs1::new(p.QMI_CS1, p.PIN_0), psram_config); | ||
| 22 | |||
| 23 | let Ok(psram) = psram else { | ||
| 24 | error!("PSRAM not found"); | ||
| 25 | loop { | ||
| 26 | Timer::after_secs(1).await; | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | |||
| 30 | let psram_slice = unsafe { | ||
| 31 | let psram_ptr = psram.base_address(); | ||
| 32 | let slice: &'static mut [u8] = slice::from_raw_parts_mut(psram_ptr, psram.size() as usize); | ||
| 33 | slice | ||
| 34 | }; | ||
| 35 | |||
| 36 | loop { | ||
| 37 | psram_slice.fill(0x55); | ||
| 38 | info!("PSRAM filled with 0x55"); | ||
| 39 | let at_addr = psram_slice[0x100]; | ||
| 40 | info!("Read from PSRAM at address 0x100: 0x{:02x}", at_addr); | ||
| 41 | Timer::after_secs(1).await; | ||
| 42 | |||
| 43 | psram_slice.fill(0xAA); | ||
| 44 | info!("PSRAM filled with 0xAA"); | ||
| 45 | let at_addr = psram_slice[0x100]; | ||
| 46 | info!("Read from PSRAM at address 0x100: 0x{:02x}", at_addr); | ||
| 47 | Timer::after_secs(1).await; | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index d747a43c2..b5b26938d 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 6 | use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | 8 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; |
| 9 | use embassy_stm32::timer::{self, Channel}; | 9 | use embassy_stm32::timer::{self, Channel}; |
| @@ -40,7 +40,8 @@ async fn main(spawner: Spawner) { | |||
| 40 | spawner.spawn(unwrap!(blinky(p.PC13))); | 40 | spawner.spawn(unwrap!(blinky(p.PC13))); |
| 41 | 41 | ||
| 42 | let ch3 = CapturePin::new(p.PA2, Pull::None); | 42 | let ch3 = CapturePin::new(p.PA2, Pull::None); |
| 43 | let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); | 43 | let mut ic = |
| 44 | InputCapture::new::<AfioRemap<0>>(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); | ||
| 44 | 45 | ||
| 45 | loop { | 46 | loop { |
| 46 | info!("wait for rising edge"); | 47 | info!("wait for rising edge"); |
diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 63b899767..9ae747018 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 6 | use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::pwm_input::PwmInput; | 8 | use embassy_stm32::timer::pwm_input::PwmInput; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; |
| @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { | |||
| 38 | 38 | ||
| 39 | spawner.spawn(unwrap!(blinky(p.PC13))); | 39 | spawner.spawn(unwrap!(blinky(p.PC13))); |
| 40 | 40 | ||
| 41 | let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(10)); | 41 | let mut pwm_input = PwmInput::new_ch1::<AfioRemap<0>>(p.TIM2, p.PA0, Pull::None, khz(10)); |
| 42 | pwm_input.enable(); | 42 | pwm_input.enable(); |
| 43 | 43 | ||
| 44 | loop { | 44 | loop { |
diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index ab29ddeff..80652b865 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs | |||
| @@ -273,14 +273,14 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 273 | let p = embassy_stm32::init(config); | 273 | let p = embassy_stm32::init(config); |
| 274 | info!("Embassy initialized"); | 274 | info!("Embassy initialized"); |
| 275 | 275 | ||
| 276 | let config = QspiCfg { | 276 | let mut config = QspiCfg::default(); |
| 277 | memory_size: MemorySize::_8MiB, | 277 | config.memory_size = MemorySize::_8MiB; |
| 278 | address_size: AddressSize::_24bit, | 278 | config.address_size = AddressSize::_24bit; |
| 279 | prescaler: 16, | 279 | config.prescaler = 16; |
| 280 | cs_high_time: ChipSelectHighTime::_1Cycle, | 280 | config.cs_high_time = ChipSelectHighTime::_1Cycle; |
| 281 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | 281 | config.fifo_threshold = FIFOThresholdLevel::_16Bytes; |
| 282 | sample_shifting: SampleShifting::None, | 282 | config.sample_shifting = SampleShifting::None; |
| 283 | }; | 283 | |
| 284 | let driver = Qspi::new_bank1( | 284 | let driver = Qspi::new_bank1( |
| 285 | p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, | 285 | p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, |
| 286 | ); | 286 | ); |
diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs index 6c7f3b48a..7d8653ef2 100644 --- a/examples/stm32g0/src/bin/adc.rs +++ b/examples/stm32g0/src/bin/adc.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime}; |
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) { | |||
| 12 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 14 | 14 | ||
| 15 | let mut adc = Adc::new(p.ADC1); | 15 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 16 | adc.set_sample_time(SampleTime::CYCLES79_5); | 16 | adc.set_sample_time(SampleTime::CYCLES79_5); |
| 17 | let mut pin = p.PA1; | 17 | let mut pin = p.PA1; |
| 18 | 18 | ||
diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs index d7515933c..8266a6d83 100644 --- a/examples/stm32g0/src/bin/adc_dma.rs +++ b/examples/stm32g0/src/bin/adc_dma.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime}; |
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | 17 | ||
| 18 | info!("Hello World!"); | 18 | info!("Hello World!"); |
| 19 | 19 | ||
| 20 | let mut adc = Adc::new(p.ADC1); | 20 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 21 | 21 | ||
| 22 | let mut dma = p.DMA1_CH1; | 22 | let mut dma = p.DMA1_CH1; |
| 23 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | 23 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); |
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index 9c5dd872a..834d1cd4a 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::adc::{Adc, SampleTime}; | 10 | use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| @@ -16,23 +16,12 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let p = embassy_stm32::init(Default::default()); | 16 | let p = embassy_stm32::init(Default::default()); |
| 17 | info!("Adc oversample test"); | 17 | info!("Adc oversample test"); |
| 18 | 18 | ||
| 19 | let mut adc = Adc::new(p.ADC1); | 19 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); |
| 20 | adc.set_sample_time(SampleTime::CYCLES1_5); | 20 | adc.set_sample_time(SampleTime::CYCLES1_5); |
| 21 | let mut pin = p.PA1; | 21 | let mut pin = p.PA1; |
| 22 | 22 | ||
| 23 | // From https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | 23 | adc.set_oversampling_ratio(Ovsr::MUL16); |
| 24 | // page373 15.8 Oversampler | 24 | adc.set_oversampling_shift(Ovss::NO_SHIFT); |
| 25 | // Table 76. Maximum output results vs N and M. Grayed values indicates truncation | ||
| 26 | // 0x00 oversampling ratio X2 | ||
| 27 | // 0x01 oversampling ratio X4 | ||
| 28 | // 0x02 oversampling ratio X8 | ||
| 29 | // 0x03 oversampling ratio X16 | ||
| 30 | // 0x04 oversampling ratio X32 | ||
| 31 | // 0x05 oversampling ratio X64 | ||
| 32 | // 0x06 oversampling ratio X128 | ||
| 33 | // 0x07 oversampling ratio X256 | ||
| 34 | adc.set_oversampling_ratio(0x03); | ||
| 35 | adc.set_oversampling_shift(0b0000); | ||
| 36 | adc.oversampling_enable(true); | 25 | adc.oversampling_enable(true); |
| 37 | 26 | ||
| 38 | loop { | 27 | loop { |
diff --git a/examples/stm32h5/src/bin/sai.rs b/examples/stm32h5/src/bin/sai.rs new file mode 100644 index 000000000..0e182f9cf --- /dev/null +++ b/examples/stm32h5/src/bin/sai.rs | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::{sai, Config}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | info!("Hello world."); | ||
| 12 | |||
| 13 | let mut config = Config::default(); | ||
| 14 | { | ||
| 15 | use embassy_stm32::rcc::*; | ||
| 16 | |||
| 17 | config.rcc.pll2 = Some(Pll { | ||
| 18 | source: PllSource::HSI, | ||
| 19 | prediv: PllPreDiv::DIV16, | ||
| 20 | mul: PllMul::MUL32, | ||
| 21 | divp: Some(PllDiv::DIV16), // 8 MHz SAI clock | ||
| 22 | divq: None, | ||
| 23 | divr: None, | ||
| 24 | }); | ||
| 25 | |||
| 26 | config.rcc.mux.sai1sel = mux::Saisel::PLL2_P; | ||
| 27 | } | ||
| 28 | let p = embassy_stm32::init(config); | ||
| 29 | |||
| 30 | let mut write_buffer = [0u16; 1024]; | ||
| 31 | let (_, sai_b) = sai::split_subblocks(p.SAI1); | ||
| 32 | |||
| 33 | let mut sai_b = sai::Sai::new_asynchronous( | ||
| 34 | sai_b, | ||
| 35 | p.PF8, | ||
| 36 | p.PE3, | ||
| 37 | p.PF9, | ||
| 38 | p.GPDMA1_CH0, | ||
| 39 | &mut write_buffer, | ||
| 40 | Default::default(), | ||
| 41 | ); | ||
| 42 | |||
| 43 | // Populate arbitrary data. | ||
| 44 | let mut data = [0u16; 256]; | ||
| 45 | for (index, sample) in data.iter_mut().enumerate() { | ||
| 46 | *sample = index as u16; | ||
| 47 | } | ||
| 48 | |||
| 49 | loop { | ||
| 50 | sai_b.write(&data).await.unwrap(); | ||
| 51 | } | ||
| 52 | } | ||
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 01937593a..847b70c85 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs | |||
| @@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) { | |||
| 63 | tx_config.tx_rx = TxRx::Transmitter; | 63 | tx_config.tx_rx = TxRx::Transmitter; |
| 64 | tx_config.sync_output = true; | 64 | tx_config.sync_output = true; |
| 65 | tx_config.clock_strobe = ClockStrobe::Falling; | 65 | tx_config.clock_strobe = ClockStrobe::Falling; |
| 66 | tx_config.master_clock_divider = mclk_div; | 66 | tx_config.master_clock_divider = Some(mclk_div); |
| 67 | tx_config.stereo_mono = StereoMono::Stereo; | 67 | tx_config.stereo_mono = StereoMono::Stereo; |
| 68 | tx_config.data_size = DataSize::Data24; | 68 | tx_config.data_size = DataSize::Data24; |
| 69 | tx_config.bit_order = BitOrder::MsbFirst; | 69 | tx_config.bit_order = BitOrder::MsbFirst; |
| @@ -119,71 +119,7 @@ async fn main(_spawner: Spawner) { | |||
| 119 | } | 119 | } |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | const fn mclk_div_from_u8(v: u8) -> MasterClockDivider { | 122 | fn mclk_div_from_u8(v: u8) -> MasterClockDivider { |
| 123 | match v { | 123 | assert!((1..=63).contains(&v)); |
| 124 | 1 => MasterClockDivider::Div1, | 124 | MasterClockDivider::from_bits(v) |
| 125 | 2 => MasterClockDivider::Div2, | ||
| 126 | 3 => MasterClockDivider::Div3, | ||
| 127 | 4 => MasterClockDivider::Div4, | ||
| 128 | 5 => MasterClockDivider::Div5, | ||
| 129 | 6 => MasterClockDivider::Div6, | ||
| 130 | 7 => MasterClockDivider::Div7, | ||
| 131 | 8 => MasterClockDivider::Div8, | ||
| 132 | 9 => MasterClockDivider::Div9, | ||
| 133 | 10 => MasterClockDivider::Div10, | ||
| 134 | 11 => MasterClockDivider::Div11, | ||
| 135 | 12 => MasterClockDivider::Div12, | ||
| 136 | 13 => MasterClockDivider::Div13, | ||
| 137 | 14 => MasterClockDivider::Div14, | ||
| 138 | 15 => MasterClockDivider::Div15, | ||
| 139 | 16 => MasterClockDivider::Div16, | ||
| 140 | 17 => MasterClockDivider::Div17, | ||
| 141 | 18 => MasterClockDivider::Div18, | ||
| 142 | 19 => MasterClockDivider::Div19, | ||
| 143 | 20 => MasterClockDivider::Div20, | ||
| 144 | 21 => MasterClockDivider::Div21, | ||
| 145 | 22 => MasterClockDivider::Div22, | ||
| 146 | 23 => MasterClockDivider::Div23, | ||
| 147 | 24 => MasterClockDivider::Div24, | ||
| 148 | 25 => MasterClockDivider::Div25, | ||
| 149 | 26 => MasterClockDivider::Div26, | ||
| 150 | 27 => MasterClockDivider::Div27, | ||
| 151 | 28 => MasterClockDivider::Div28, | ||
| 152 | 29 => MasterClockDivider::Div29, | ||
| 153 | 30 => MasterClockDivider::Div30, | ||
| 154 | 31 => MasterClockDivider::Div31, | ||
| 155 | 32 => MasterClockDivider::Div32, | ||
| 156 | 33 => MasterClockDivider::Div33, | ||
| 157 | 34 => MasterClockDivider::Div34, | ||
| 158 | 35 => MasterClockDivider::Div35, | ||
| 159 | 36 => MasterClockDivider::Div36, | ||
| 160 | 37 => MasterClockDivider::Div37, | ||
| 161 | 38 => MasterClockDivider::Div38, | ||
| 162 | 39 => MasterClockDivider::Div39, | ||
| 163 | 40 => MasterClockDivider::Div40, | ||
| 164 | 41 => MasterClockDivider::Div41, | ||
| 165 | 42 => MasterClockDivider::Div42, | ||
| 166 | 43 => MasterClockDivider::Div43, | ||
| 167 | 44 => MasterClockDivider::Div44, | ||
| 168 | 45 => MasterClockDivider::Div45, | ||
| 169 | 46 => MasterClockDivider::Div46, | ||
| 170 | 47 => MasterClockDivider::Div47, | ||
| 171 | 48 => MasterClockDivider::Div48, | ||
| 172 | 49 => MasterClockDivider::Div49, | ||
| 173 | 50 => MasterClockDivider::Div50, | ||
| 174 | 51 => MasterClockDivider::Div51, | ||
| 175 | 52 => MasterClockDivider::Div52, | ||
| 176 | 53 => MasterClockDivider::Div53, | ||
| 177 | 54 => MasterClockDivider::Div54, | ||
| 178 | 55 => MasterClockDivider::Div55, | ||
| 179 | 56 => MasterClockDivider::Div56, | ||
| 180 | 57 => MasterClockDivider::Div57, | ||
| 181 | 58 => MasterClockDivider::Div58, | ||
| 182 | 59 => MasterClockDivider::Div59, | ||
| 183 | 60 => MasterClockDivider::Div60, | ||
| 184 | 61 => MasterClockDivider::Div61, | ||
| 185 | 62 => MasterClockDivider::Div62, | ||
| 186 | 63 => MasterClockDivider::Div63, | ||
| 187 | _ => panic!(), | ||
| 188 | } | ||
| 189 | } | 125 | } |
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index 6d29e8a4d..b75a03ae8 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs | |||
| @@ -168,7 +168,7 @@ fn new_sai_transmitter<'d>( | |||
| 168 | sai_config.slot_enable = 0xFFFF; // All slots | 168 | sai_config.slot_enable = 0xFFFF; // All slots |
| 169 | sai_config.data_size = sai::DataSize::Data32; | 169 | sai_config.data_size = sai::DataSize::Data32; |
| 170 | sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; | 170 | sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; |
| 171 | sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; | 171 | sai_config.master_clock_divider = None; |
| 172 | 172 | ||
| 173 | let (sub_block_tx, _) = hal::sai::split_subblocks(sai); | 173 | let (sub_block_tx, _) = hal::sai::split_subblocks(sai); |
| 174 | Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) | 174 | Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) |
diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs index 50e37ec52..9e79d7089 100644 --- a/examples/stm32h742/src/bin/qspi.rs +++ b/examples/stm32h742/src/bin/qspi.rs | |||
| @@ -266,14 +266,14 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 266 | let p = embassy_stm32::init(config); | 266 | let p = embassy_stm32::init(config); |
| 267 | info!("Embassy initialized"); | 267 | info!("Embassy initialized"); |
| 268 | 268 | ||
| 269 | let config = QspiCfg { | 269 | let mut config = QspiCfg::default(); |
| 270 | memory_size: MemorySize::_8MiB, | 270 | config.memory_size = MemorySize::_8MiB; |
| 271 | address_size: AddressSize::_24bit, | 271 | config.address_size = AddressSize::_24bit; |
| 272 | prescaler: 16, | 272 | config.prescaler = 16; |
| 273 | cs_high_time: ChipSelectHighTime::_1Cycle, | 273 | config.cs_high_time = ChipSelectHighTime::_1Cycle; |
| 274 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | 274 | config.fifo_threshold = FIFOThresholdLevel::_16Bytes; |
| 275 | sample_shifting: SampleShifting::None, | 275 | config.sample_shifting = SampleShifting::None; |
| 276 | }; | 276 | |
| 277 | let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); | 277 | let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); |
| 278 | let mut flash = FlashMemory::new(driver); | 278 | let mut flash = FlashMemory::new(driver); |
| 279 | let flash_id = flash.read_id(); | 279 | let flash_id = flash.read_id(); |
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 90f57a2d8..d42cdac15 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml | |||
| @@ -11,6 +11,8 @@ embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [" | |||
| 11 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 12 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } | ||
| 14 | 16 | ||
| 15 | defmt = "1.0.1" | 17 | defmt = "1.0.1" |
| 16 | defmt-rtt = "1.0.0" | 18 | defmt-rtt = "1.0.0" |
diff --git a/examples/stm32l0/src/bin/usb_serial.rs b/examples/stm32l0/src/bin/usb_serial.rs new file mode 100644 index 000000000..fdb1aeb59 --- /dev/null +++ b/examples/stm32l0/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{panic, *}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::usb::{self, Driver, Instance}; | ||
| 8 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 9 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 10 | use embassy_usb::driver::EndpointError; | ||
| 11 | use embassy_usb::Builder; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | USB => usb::InterruptHandler<peripherals::USB>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let mut config = embassy_stm32::Config::default(); | ||
| 21 | { | ||
| 22 | use embassy_stm32::rcc::*; | ||
| 23 | config.rcc.hsi = true; | ||
| 24 | config.rcc.pll = Some(Pll { | ||
| 25 | source: PllSource::HSI, | ||
| 26 | mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz | ||
| 27 | div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3) | ||
| 28 | }); | ||
| 29 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 30 | } | ||
| 31 | |||
| 32 | let p = embassy_stm32::init(config); | ||
| 33 | |||
| 34 | info!("Hello World!"); | ||
| 35 | |||
| 36 | let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); | ||
| 37 | |||
| 38 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 39 | config.manufacturer = Some("Embassy"); | ||
| 40 | config.product = Some("USB-Serial Example"); | ||
| 41 | config.serial_number = Some("123456"); | ||
| 42 | |||
| 43 | let mut config_descriptor = [0; 256]; | ||
| 44 | let mut bos_descriptor = [0; 256]; | ||
| 45 | let mut control_buf = [0; 64]; | ||
| 46 | |||
| 47 | let mut state = State::new(); | ||
| 48 | |||
| 49 | let mut builder = Builder::new( | ||
| 50 | driver, | ||
| 51 | config, | ||
| 52 | &mut config_descriptor, | ||
| 53 | &mut bos_descriptor, | ||
| 54 | &mut [], // no msos descriptors | ||
| 55 | &mut control_buf, | ||
| 56 | ); | ||
| 57 | |||
| 58 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 59 | |||
| 60 | let mut usb = builder.build(); | ||
| 61 | |||
| 62 | let usb_fut = usb.run(); | ||
| 63 | |||
| 64 | let echo_fut = async { | ||
| 65 | loop { | ||
| 66 | class.wait_connection().await; | ||
| 67 | info!("Connected"); | ||
| 68 | let _ = echo(&mut class).await; | ||
| 69 | info!("Disconnected"); | ||
| 70 | } | ||
| 71 | }; | ||
| 72 | |||
| 73 | join(usb_fut, echo_fut).await; | ||
| 74 | } | ||
| 75 | |||
| 76 | struct Disconnected {} | ||
| 77 | |||
| 78 | impl From<EndpointError> for Disconnected { | ||
| 79 | fn from(val: EndpointError) -> Self { | ||
| 80 | match val { | ||
| 81 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 82 | EndpointError::Disabled => Disconnected {}, | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 88 | let mut buf = [0; 64]; | ||
| 89 | loop { | ||
| 90 | let n = class.read_packet(&mut buf).await?; | ||
| 91 | let data = &buf[..n]; | ||
| 92 | info!("data: {:x}", data); | ||
| 93 | class.write_packet(data).await?; | ||
| 94 | } | ||
| 95 | } | ||
diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs index 075458fe5..feabdd532 100644 --- a/examples/stm32l432/src/bin/qspi_mmap.rs +++ b/examples/stm32l432/src/bin/qspi_mmap.rs | |||
| @@ -246,14 +246,14 @@ const MEMORY_ADDR: u32 = 0x00000000 as u32; | |||
| 246 | async fn main(_spawner: Spawner) { | 246 | async fn main(_spawner: Spawner) { |
| 247 | let p = embassy_stm32::init(Default::default()); | 247 | let p = embassy_stm32::init(Default::default()); |
| 248 | 248 | ||
| 249 | let config = qspi::Config { | 249 | let mut config = qspi::Config::default(); |
| 250 | memory_size: MemorySize::_16MiB, | 250 | config.memory_size = MemorySize::_16MiB; |
| 251 | address_size: AddressSize::_24bit, | 251 | config.address_size = AddressSize::_24bit; |
| 252 | prescaler: 200, | 252 | config.prescaler = 200; |
| 253 | cs_high_time: ChipSelectHighTime::_1Cycle, | 253 | config.cs_high_time = ChipSelectHighTime::_1Cycle; |
| 254 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | 254 | config.fifo_threshold = FIFOThresholdLevel::_16Bytes; |
| 255 | sample_shifting: SampleShifting::None, | 255 | config.sample_shifting = SampleShifting::None; |
| 256 | }; | 256 | |
| 257 | let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); | 257 | let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); |
| 258 | let mut flash = FlashMemory::new(driver); | 258 | let mut flash = FlashMemory::new(driver); |
| 259 | let mut wr_buf = [0u8; 256]; | 259 | let mut wr_buf = [0u8; 256]; |
diff --git a/examples/stm32wl/src/bin/adc.rs b/examples/stm32wl/src/bin/adc.rs new file mode 100644 index 000000000..118f02ae1 --- /dev/null +++ b/examples/stm32wl/src/bin/adc.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime}; | ||
| 9 | use embassy_stm32::SharedData; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); | ||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 }); | ||
| 21 | adc.set_sample_time(SampleTime::CYCLES79_5); | ||
| 22 | let mut pin = p.PB2; | ||
| 23 | |||
| 24 | let mut vrefint = adc.enable_vrefint(); | ||
| 25 | let vrefint_sample = adc.blocking_read(&mut vrefint); | ||
| 26 | let convert_to_millivolts = |sample| { | ||
| 27 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf | ||
| 28 | // 6.3.3 Embedded internal reference voltage | ||
| 29 | const VREFINT_MV: u32 = 1212; // mV | ||
| 30 | |||
| 31 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 32 | }; | ||
| 33 | |||
| 34 | loop { | ||
| 35 | let v = adc.blocking_read(&mut pin); | ||
| 36 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | ||
| 37 | Timer::after_millis(100).await; | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 809346bed..19461520a 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml | |||
| @@ -64,6 +64,12 @@ name = "float" | |||
| 64 | path = "src/bin/float.rs" | 64 | path = "src/bin/float.rs" |
| 65 | required-features = [ "rp2040",] | 65 | required-features = [ "rp2040",] |
| 66 | 66 | ||
| 67 | # RTC is only available on RP2040 | ||
| 68 | [[bin]] | ||
| 69 | name = "rtc" | ||
| 70 | path = "src/bin/rtc.rs" | ||
| 71 | required-features = [ "rp2040",] | ||
| 72 | |||
| 67 | [profile.dev] | 73 | [profile.dev] |
| 68 | debug = 2 | 74 | debug = 2 |
| 69 | debug-assertions = true | 75 | debug-assertions = true |
diff --git a/tests/rp/src/bin/rtc.rs b/tests/rp/src/bin/rtc.rs new file mode 100644 index 000000000..c66981d95 --- /dev/null +++ b/tests/rp/src/bin/rtc.rs | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #[cfg(feature = "rp2040")] | ||
| 4 | teleprobe_meta::target!(b"rpi-pico"); | ||
| 5 | |||
| 6 | use defmt::{assert, *}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_futures::select::{select, Either}; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; | ||
| 11 | use embassy_time::{Duration, Instant, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | // Bind the RTC interrupt to the handler | ||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | RTC_IRQ => embassy_rp::rtc::InterruptHandler; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_rp::init(Default::default()); | ||
| 22 | let mut rtc = Rtc::new(p.RTC, Irqs); | ||
| 23 | |||
| 24 | info!("RTC test started"); | ||
| 25 | |||
| 26 | // Initialize RTC if not running | ||
| 27 | if !rtc.is_running() { | ||
| 28 | info!("Starting RTC"); | ||
| 29 | let now = DateTime { | ||
| 30 | year: 2000, | ||
| 31 | month: 1, | ||
| 32 | day: 1, | ||
| 33 | day_of_week: DayOfWeek::Saturday, | ||
| 34 | hour: 0, | ||
| 35 | minute: 0, | ||
| 36 | second: 0, | ||
| 37 | }; | ||
| 38 | rtc.set_datetime(now).unwrap(); | ||
| 39 | Timer::after_millis(100).await; | ||
| 40 | } | ||
| 41 | |||
| 42 | // Test 1: Basic RTC functionality - read current time | ||
| 43 | let initial_time = rtc.now().unwrap(); | ||
| 44 | info!( | ||
| 45 | "Initial time: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 46 | initial_time.year, | ||
| 47 | initial_time.month, | ||
| 48 | initial_time.day, | ||
| 49 | initial_time.hour, | ||
| 50 | initial_time.minute, | ||
| 51 | initial_time.second | ||
| 52 | ); | ||
| 53 | |||
| 54 | // Test 2: Schedule and wait for alarm | ||
| 55 | info!("Testing alarm scheduling"); | ||
| 56 | |||
| 57 | // Wait until we're at a predictable second, then schedule for a future second | ||
| 58 | loop { | ||
| 59 | let current = rtc.now().unwrap(); | ||
| 60 | if current.second <= 55 { | ||
| 61 | break; | ||
| 62 | } | ||
| 63 | Timer::after_millis(100).await; | ||
| 64 | } | ||
| 65 | |||
| 66 | // Now schedule alarm for 3 seconds from current time | ||
| 67 | let current_time = rtc.now().unwrap(); | ||
| 68 | let alarm_second = (current_time.second + 3) % 60; | ||
| 69 | let alarm_filter = DateTimeFilter::default().second(alarm_second); | ||
| 70 | |||
| 71 | info!("Scheduling alarm for second: {}", alarm_second); | ||
| 72 | rtc.schedule_alarm(alarm_filter); | ||
| 73 | |||
| 74 | // Verify alarm is scheduled | ||
| 75 | let scheduled = rtc.alarm_scheduled(); | ||
| 76 | assert!(scheduled.is_some(), "Alarm should be scheduled"); | ||
| 77 | info!("Alarm scheduled successfully: {}", scheduled.unwrap()); | ||
| 78 | |||
| 79 | // Wait for alarm with timeout | ||
| 80 | let alarm_start = Instant::now(); | ||
| 81 | match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { | ||
| 82 | Either::First(_) => { | ||
| 83 | core::panic!("Alarm timeout - alarm should have triggered by now"); | ||
| 84 | } | ||
| 85 | Either::Second(_) => { | ||
| 86 | let alarm_duration = Instant::now() - alarm_start; | ||
| 87 | info!("ALARM TRIGGERED after {:?}", alarm_duration); | ||
| 88 | |||
| 89 | // Verify timing is reasonable (should be around 3 seconds) | ||
| 90 | assert!( | ||
| 91 | alarm_duration >= Duration::from_secs(2) && alarm_duration <= Duration::from_secs(4), | ||
| 92 | "Alarm timing incorrect: {:?}", | ||
| 93 | alarm_duration | ||
| 94 | ); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | // Test 3: Verify RTC is still running and time has advanced | ||
| 99 | let final_time = rtc.now().unwrap(); | ||
| 100 | info!( | ||
| 101 | "Final time: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 102 | final_time.year, final_time.month, final_time.day, final_time.hour, final_time.minute, final_time.second | ||
| 103 | ); | ||
| 104 | |||
| 105 | // Verify time has advanced (allowing for minute/hour rollover) | ||
| 106 | let time_diff = if final_time.second >= initial_time.second { | ||
| 107 | final_time.second - initial_time.second | ||
| 108 | } else { | ||
| 109 | 60 - initial_time.second + final_time.second | ||
| 110 | }; | ||
| 111 | |||
| 112 | assert!(time_diff >= 3, "RTC should have advanced by at least 3 seconds"); | ||
| 113 | info!("Time advanced by {} seconds", time_diff); | ||
| 114 | |||
| 115 | // Test 4: Verify alarm is no longer scheduled after triggering | ||
| 116 | let post_alarm_scheduled = rtc.alarm_scheduled(); | ||
| 117 | assert!( | ||
| 118 | post_alarm_scheduled.is_none(), | ||
| 119 | "Alarm should not be scheduled after triggering" | ||
| 120 | ); | ||
| 121 | info!("Alarm correctly cleared after triggering"); | ||
| 122 | |||
| 123 | info!("Test OK"); | ||
| 124 | cortex_m::asm::bkpt(); | ||
| 125 | } | ||
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index aeca67659..891ec93fd 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -9,7 +9,9 @@ publish = false | |||
| 9 | [features] | 9 | [features] |
| 10 | stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] | 10 | stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] |
| 11 | stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"] | 11 | stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"] |
| 12 | stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] | 12 | stm32f100rd = ["embassy-stm32/stm32f100rd", "spi-v1", "not-gpdma", "afio", "afio-value-line"] |
| 13 | stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma", "afio"] | ||
| 14 | stm32f107vc = ["embassy-stm32/stm32f107vc", "spi-v1", "not-gpdma", "afio", "afio-connectivity-line"] | ||
| 13 | stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] | 15 | stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] |
| 14 | stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] | 16 | stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] |
| 15 | stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] | 17 | stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] |
| @@ -59,6 +61,9 @@ cordic = ["dep:num-traits"] | |||
| 59 | dual-bank = ["embassy-stm32/dual-bank"] | 61 | dual-bank = ["embassy-stm32/dual-bank"] |
| 60 | single-bank = ["embassy-stm32/single-bank"] | 62 | single-bank = ["embassy-stm32/single-bank"] |
| 61 | eeprom = [] | 63 | eeprom = [] |
| 64 | afio = [] | ||
| 65 | afio-connectivity-line = [] | ||
| 66 | afio-value-line = [] | ||
| 62 | 67 | ||
| 63 | cm0 = ["portable-atomic/unsafe-assume-single-core"] | 68 | cm0 = ["portable-atomic/unsafe-assume-single-core"] |
| 64 | 69 | ||
| @@ -99,6 +104,11 @@ num-traits = { version="0.2", default-features = false,features = ["libm"], opti | |||
| 99 | # BEGIN TESTS | 104 | # BEGIN TESTS |
| 100 | # Generated by gen_test.py. DO NOT EDIT. | 105 | # Generated by gen_test.py. DO NOT EDIT. |
| 101 | [[bin]] | 106 | [[bin]] |
| 107 | name = "afio" | ||
| 108 | path = "src/bin/afio.rs" | ||
| 109 | required-features = [ "afio",] | ||
| 110 | |||
| 111 | [[bin]] | ||
| 102 | name = "can" | 112 | name = "can" |
| 103 | path = "src/bin/can.rs" | 113 | path = "src/bin/can.rs" |
| 104 | required-features = [ "can",] | 114 | required-features = [ "can",] |
diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs new file mode 100644 index 000000000..cc44dc59c --- /dev/null +++ b/tests/stm32/src/bin/afio.rs | |||
| @@ -0,0 +1,1156 @@ | |||
| 1 | // required-features: afio | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | #[path = "../common.rs"] | ||
| 5 | mod common; | ||
| 6 | |||
| 7 | use common::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::gpio::{AfioRemap, OutputType, Pull}; | ||
| 10 | use embassy_stm32::pac::AFIO; | ||
| 11 | use embassy_stm32::time::khz; | ||
| 12 | use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; | ||
| 13 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | ||
| 14 | use embassy_stm32::timer::pwm_input::PwmInput; | ||
| 15 | use embassy_stm32::timer::qei::{Qei, QeiPin}; | ||
| 16 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 17 | use embassy_stm32::usart::{Uart, UartRx, UartTx}; | ||
| 18 | use embassy_stm32::{bind_interrupts, Peripherals}; | ||
| 19 | |||
| 20 | #[cfg(not(feature = "afio-connectivity-line"))] | ||
| 21 | bind_interrupts!(struct Irqs { | ||
| 22 | USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>; | ||
| 23 | TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler<embassy_stm32::peripherals::TIM1>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[cfg(feature = "afio-connectivity-line")] | ||
| 27 | bind_interrupts!(struct Irqs { | ||
| 28 | CAN1_RX0 => embassy_stm32::can::Rx0InterruptHandler<embassy_stm32::peripherals::CAN1>; | ||
| 29 | CAN1_RX1 => embassy_stm32::can::Rx1InterruptHandler<embassy_stm32::peripherals::CAN1>; | ||
| 30 | CAN1_SCE => embassy_stm32::can::SceInterruptHandler<embassy_stm32::peripherals::CAN1>; | ||
| 31 | CAN1_TX => embassy_stm32::can::TxInterruptHandler<embassy_stm32::peripherals::CAN1>; | ||
| 32 | |||
| 33 | CAN2_RX0 => embassy_stm32::can::Rx0InterruptHandler<embassy_stm32::peripherals::CAN2>; | ||
| 34 | CAN2_RX1 => embassy_stm32::can::Rx1InterruptHandler<embassy_stm32::peripherals::CAN2>; | ||
| 35 | CAN2_SCE => embassy_stm32::can::SceInterruptHandler<embassy_stm32::peripherals::CAN2>; | ||
| 36 | CAN2_TX => embassy_stm32::can::TxInterruptHandler<embassy_stm32::peripherals::CAN2>; | ||
| 37 | |||
| 38 | ETH => embassy_stm32::eth::InterruptHandler; | ||
| 39 | USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>; | ||
| 40 | TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler<embassy_stm32::peripherals::TIM1>; | ||
| 41 | }); | ||
| 42 | |||
| 43 | #[embassy_executor::main] | ||
| 44 | async fn main(_spawner: Spawner) { | ||
| 45 | let mut p = init(); | ||
| 46 | info!("Hello World!"); | ||
| 47 | |||
| 48 | // USART3 | ||
| 49 | { | ||
| 50 | // no remap RX/TX/RTS/CTS | ||
| 51 | afio_registers_set_remap(); | ||
| 52 | Uart::new_blocking_with_rtscts( | ||
| 53 | p.USART3.reborrow(), | ||
| 54 | p.PB11.reborrow(), | ||
| 55 | p.PB10.reborrow(), | ||
| 56 | p.PB14.reborrow(), | ||
| 57 | p.PB13.reborrow(), | ||
| 58 | Default::default(), | ||
| 59 | ) | ||
| 60 | .unwrap(); | ||
| 61 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 62 | } | ||
| 63 | { | ||
| 64 | // no remap RX/TX | ||
| 65 | afio_registers_set_remap(); | ||
| 66 | Uart::new_blocking( | ||
| 67 | p.USART3.reborrow(), | ||
| 68 | p.PB11.reborrow(), | ||
| 69 | p.PB10.reborrow(), | ||
| 70 | Default::default(), | ||
| 71 | ) | ||
| 72 | .unwrap(); | ||
| 73 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 74 | } | ||
| 75 | { | ||
| 76 | // no remap TX | ||
| 77 | afio_registers_set_remap(); | ||
| 78 | Uart::new_blocking_half_duplex( | ||
| 79 | p.USART3.reborrow(), | ||
| 80 | p.PB10.reborrow(), | ||
| 81 | Default::default(), | ||
| 82 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 83 | ) | ||
| 84 | .unwrap(); | ||
| 85 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 86 | } | ||
| 87 | { | ||
| 88 | // no remap TX async | ||
| 89 | afio_registers_set_remap(); | ||
| 90 | UartTx::new( | ||
| 91 | p.USART3.reborrow(), | ||
| 92 | p.PB10.reborrow(), | ||
| 93 | p.DMA1_CH2.reborrow(), | ||
| 94 | Default::default(), | ||
| 95 | ) | ||
| 96 | .unwrap(); | ||
| 97 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 98 | } | ||
| 99 | { | ||
| 100 | // no remap TX/CTS async | ||
| 101 | afio_registers_set_remap(); | ||
| 102 | UartTx::new_with_cts( | ||
| 103 | p.USART3.reborrow(), | ||
| 104 | p.PB10.reborrow(), | ||
| 105 | p.PB13.reborrow(), | ||
| 106 | p.DMA1_CH2.reborrow(), | ||
| 107 | Default::default(), | ||
| 108 | ) | ||
| 109 | .unwrap(); | ||
| 110 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 111 | } | ||
| 112 | { | ||
| 113 | // no remap RX async | ||
| 114 | afio_registers_set_remap(); | ||
| 115 | UartRx::new( | ||
| 116 | p.USART3.reborrow(), | ||
| 117 | Irqs, | ||
| 118 | p.PB11.reborrow(), | ||
| 119 | p.DMA1_CH3.reborrow(), | ||
| 120 | Default::default(), | ||
| 121 | ) | ||
| 122 | .unwrap(); | ||
| 123 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 124 | } | ||
| 125 | { | ||
| 126 | // no remap RX async | ||
| 127 | afio_registers_set_remap(); | ||
| 128 | UartRx::new_with_rts( | ||
| 129 | p.USART3.reborrow(), | ||
| 130 | Irqs, | ||
| 131 | p.PB11.reborrow(), | ||
| 132 | p.PB14.reborrow(), | ||
| 133 | p.DMA1_CH3.reborrow(), | ||
| 134 | Default::default(), | ||
| 135 | ) | ||
| 136 | .unwrap(); | ||
| 137 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 138 | } | ||
| 139 | { | ||
| 140 | // no remap RX/TX async | ||
| 141 | afio_registers_set_remap(); | ||
| 142 | Uart::new( | ||
| 143 | p.USART3.reborrow(), | ||
| 144 | p.PB11.reborrow(), | ||
| 145 | p.PB10.reborrow(), | ||
| 146 | Irqs, | ||
| 147 | p.DMA1_CH2.reborrow(), | ||
| 148 | p.DMA1_CH3.reborrow(), | ||
| 149 | Default::default(), | ||
| 150 | ) | ||
| 151 | .unwrap(); | ||
| 152 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 153 | } | ||
| 154 | { | ||
| 155 | // no remap RX/TX/RTS/CTS async | ||
| 156 | afio_registers_set_remap(); | ||
| 157 | Uart::new_with_rtscts( | ||
| 158 | p.USART3.reborrow(), | ||
| 159 | p.PB11.reborrow(), | ||
| 160 | p.PB10.reborrow(), | ||
| 161 | Irqs, | ||
| 162 | p.PB14.reborrow(), | ||
| 163 | p.PB13.reborrow(), | ||
| 164 | p.DMA1_CH2.reborrow(), | ||
| 165 | p.DMA1_CH3.reborrow(), | ||
| 166 | Default::default(), | ||
| 167 | ) | ||
| 168 | .unwrap(); | ||
| 169 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); | ||
| 170 | } | ||
| 171 | |||
| 172 | // TIM1 | ||
| 173 | { | ||
| 174 | // no remap | ||
| 175 | afio_registers_set_remap(); | ||
| 176 | SimplePwm::new::<AfioRemap<0>>( | ||
| 177 | p.TIM1.reborrow(), | ||
| 178 | Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), | ||
| 179 | Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)), | ||
| 180 | Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)), | ||
| 181 | Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)), | ||
| 182 | khz(10), | ||
| 183 | Default::default(), | ||
| 184 | ); | ||
| 185 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0); | ||
| 186 | } | ||
| 187 | { | ||
| 188 | // no remap | ||
| 189 | afio_registers_set_remap(); | ||
| 190 | SimplePwm::new::<AfioRemap<0>>( | ||
| 191 | p.TIM1.reborrow(), | ||
| 192 | Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), | ||
| 193 | None, | ||
| 194 | None, | ||
| 195 | None, | ||
| 196 | khz(10), | ||
| 197 | Default::default(), | ||
| 198 | ); | ||
| 199 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0); | ||
| 200 | } | ||
| 201 | { | ||
| 202 | // partial remap | ||
| 203 | reset_afio_registers(); | ||
| 204 | ComplementaryPwm::new::<AfioRemap<1>>( | ||
| 205 | p.TIM1.reborrow(), | ||
| 206 | Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), | ||
| 207 | None, | ||
| 208 | None, | ||
| 209 | None, | ||
| 210 | None, | ||
| 211 | None, | ||
| 212 | None, | ||
| 213 | None, | ||
| 214 | khz(10), | ||
| 215 | Default::default(), | ||
| 216 | ); | ||
| 217 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); | ||
| 218 | } | ||
| 219 | { | ||
| 220 | // partial remap | ||
| 221 | reset_afio_registers(); | ||
| 222 | ComplementaryPwm::new::<AfioRemap<1>>( | ||
| 223 | p.TIM1.reborrow(), | ||
| 224 | Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), | ||
| 225 | Some(ComplementaryPwmPin::new(p.PA7.reborrow(), OutputType::PushPull)), | ||
| 226 | Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)), | ||
| 227 | Some(ComplementaryPwmPin::new(p.PB0.reborrow(), OutputType::PushPull)), | ||
| 228 | Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)), | ||
| 229 | None, // pin does not exist on medium-density devices | ||
| 230 | Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)), | ||
| 231 | None, // signal does not exist | ||
| 232 | khz(10), | ||
| 233 | Default::default(), | ||
| 234 | ); | ||
| 235 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); | ||
| 236 | } | ||
| 237 | { | ||
| 238 | // partial remap | ||
| 239 | reset_afio_registers(); | ||
| 240 | InputCapture::new::<AfioRemap<1>>( | ||
| 241 | p.TIM1.reborrow(), | ||
| 242 | Some(CapturePin::new(p.PA8.reborrow(), Pull::Down)), | ||
| 243 | None, | ||
| 244 | None, | ||
| 245 | None, | ||
| 246 | Irqs, | ||
| 247 | khz(10), | ||
| 248 | Default::default(), | ||
| 249 | ); | ||
| 250 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); | ||
| 251 | } | ||
| 252 | { | ||
| 253 | // partial remap | ||
| 254 | reset_afio_registers(); | ||
| 255 | PwmInput::new_ch1::<AfioRemap<1>>(p.TIM1.reborrow(), p.PA8.reborrow(), Pull::Down, khz(10)); | ||
| 256 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); | ||
| 257 | } | ||
| 258 | { | ||
| 259 | // partial remap | ||
| 260 | reset_afio_registers(); | ||
| 261 | Qei::new::<AfioRemap<1>>( | ||
| 262 | p.TIM1.reborrow(), | ||
| 263 | QeiPin::new(p.PA8.reborrow()), | ||
| 264 | QeiPin::new(p.PA9.reborrow()), | ||
| 265 | ); | ||
| 266 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); | ||
| 267 | } | ||
| 268 | |||
| 269 | // TIM2 | ||
| 270 | { | ||
| 271 | // no remap | ||
| 272 | afio_registers_set_remap(); | ||
| 273 | SimplePwm::new::<AfioRemap<0>>( | ||
| 274 | p.TIM2.reborrow(), | ||
| 275 | Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)), | ||
| 276 | Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)), | ||
| 277 | Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)), | ||
| 278 | Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)), | ||
| 279 | khz(10), | ||
| 280 | Default::default(), | ||
| 281 | ); | ||
| 282 | defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 0); | ||
| 283 | } | ||
| 284 | { | ||
| 285 | // partial remap 1 | ||
| 286 | reset_afio_registers(); | ||
| 287 | SimplePwm::new::<AfioRemap<1>>( | ||
| 288 | p.TIM2.reborrow(), | ||
| 289 | Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)), | ||
| 290 | Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)), | ||
| 291 | Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)), | ||
| 292 | Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)), | ||
| 293 | khz(10), | ||
| 294 | Default::default(), | ||
| 295 | ); | ||
| 296 | defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 1); | ||
| 297 | } | ||
| 298 | { | ||
| 299 | // partial remap 2 | ||
| 300 | reset_afio_registers(); | ||
| 301 | SimplePwm::new::<AfioRemap<2>>( | ||
| 302 | p.TIM2.reborrow(), | ||
| 303 | Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)), | ||
| 304 | Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)), | ||
| 305 | Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)), | ||
| 306 | Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)), | ||
| 307 | khz(10), | ||
| 308 | Default::default(), | ||
| 309 | ); | ||
| 310 | defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 2); | ||
| 311 | } | ||
| 312 | { | ||
| 313 | // full remap | ||
| 314 | reset_afio_registers(); | ||
| 315 | SimplePwm::new::<AfioRemap<3>>( | ||
| 316 | p.TIM2.reborrow(), | ||
| 317 | Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)), | ||
| 318 | Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)), | ||
| 319 | Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)), | ||
| 320 | Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)), | ||
| 321 | khz(10), | ||
| 322 | Default::default(), | ||
| 323 | ); | ||
| 324 | defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 3); | ||
| 325 | } | ||
| 326 | |||
| 327 | connectivity_line::run(&mut p); | ||
| 328 | value_line::run(&mut p); | ||
| 329 | |||
| 330 | info!("Test OK"); | ||
| 331 | cortex_m::asm::bkpt(); | ||
| 332 | } | ||
| 333 | |||
| 334 | #[cfg(feature = "afio-connectivity-line")] | ||
| 335 | mod connectivity_line { | ||
| 336 | use embassy_stm32::can::Can; | ||
| 337 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | ||
| 338 | use embassy_stm32::i2s::I2S; | ||
| 339 | use embassy_stm32::spi::Spi; | ||
| 340 | |||
| 341 | use super::*; | ||
| 342 | |||
| 343 | pub fn run(p: &mut Peripherals) { | ||
| 344 | // USART3 | ||
| 345 | { | ||
| 346 | // partial remap RX/TX/RTS/CTS | ||
| 347 | reset_afio_registers(); | ||
| 348 | Uart::new_blocking_with_rtscts( | ||
| 349 | p.USART3.reborrow(), | ||
| 350 | p.PC11.reborrow(), | ||
| 351 | p.PC10.reborrow(), | ||
| 352 | p.PB14.reborrow(), | ||
| 353 | p.PB13.reborrow(), | ||
| 354 | Default::default(), | ||
| 355 | ) | ||
| 356 | .unwrap(); | ||
| 357 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 358 | } | ||
| 359 | { | ||
| 360 | // partial remap RX/TX | ||
| 361 | reset_afio_registers(); | ||
| 362 | Uart::new_blocking( | ||
| 363 | p.USART3.reborrow(), | ||
| 364 | p.PC11.reborrow(), | ||
| 365 | p.PC10.reborrow(), | ||
| 366 | Default::default(), | ||
| 367 | ) | ||
| 368 | .unwrap(); | ||
| 369 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 370 | } | ||
| 371 | { | ||
| 372 | // partial remap TX | ||
| 373 | reset_afio_registers(); | ||
| 374 | Uart::new_blocking_half_duplex( | ||
| 375 | p.USART3.reborrow(), | ||
| 376 | p.PC10.reborrow(), | ||
| 377 | Default::default(), | ||
| 378 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 379 | ) | ||
| 380 | .unwrap(); | ||
| 381 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 382 | } | ||
| 383 | { | ||
| 384 | // partial remap TX async | ||
| 385 | reset_afio_registers(); | ||
| 386 | UartTx::new( | ||
| 387 | p.USART3.reborrow(), | ||
| 388 | p.PC10.reborrow(), | ||
| 389 | p.DMA1_CH2.reborrow(), | ||
| 390 | Default::default(), | ||
| 391 | ) | ||
| 392 | .unwrap(); | ||
| 393 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 394 | } | ||
| 395 | { | ||
| 396 | // partial remap TX/CTS async | ||
| 397 | reset_afio_registers(); | ||
| 398 | UartTx::new_with_cts( | ||
| 399 | p.USART3.reborrow(), | ||
| 400 | p.PC10.reborrow(), | ||
| 401 | p.PB13.reborrow(), | ||
| 402 | p.DMA1_CH2.reborrow(), | ||
| 403 | Default::default(), | ||
| 404 | ) | ||
| 405 | .unwrap(); | ||
| 406 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 407 | } | ||
| 408 | { | ||
| 409 | // partial remap RX async | ||
| 410 | reset_afio_registers(); | ||
| 411 | UartRx::new( | ||
| 412 | p.USART3.reborrow(), | ||
| 413 | Irqs, | ||
| 414 | p.PC11.reborrow(), | ||
| 415 | p.DMA1_CH3.reborrow(), | ||
| 416 | Default::default(), | ||
| 417 | ) | ||
| 418 | .unwrap(); | ||
| 419 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 420 | } | ||
| 421 | { | ||
| 422 | // partial remap RX async | ||
| 423 | reset_afio_registers(); | ||
| 424 | UartRx::new_with_rts( | ||
| 425 | p.USART3.reborrow(), | ||
| 426 | Irqs, | ||
| 427 | p.PC11.reborrow(), | ||
| 428 | p.PB14.reborrow(), | ||
| 429 | p.DMA1_CH3.reborrow(), | ||
| 430 | Default::default(), | ||
| 431 | ) | ||
| 432 | .unwrap(); | ||
| 433 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 434 | } | ||
| 435 | { | ||
| 436 | // partial remap RX/TX async | ||
| 437 | reset_afio_registers(); | ||
| 438 | Uart::new( | ||
| 439 | p.USART3.reborrow(), | ||
| 440 | p.PC11.reborrow(), | ||
| 441 | p.PC10.reborrow(), | ||
| 442 | Irqs, | ||
| 443 | p.DMA1_CH2.reborrow(), | ||
| 444 | p.DMA1_CH3.reborrow(), | ||
| 445 | Default::default(), | ||
| 446 | ) | ||
| 447 | .unwrap(); | ||
| 448 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 449 | } | ||
| 450 | { | ||
| 451 | // partial remap RX/TX/RTS/CTS async | ||
| 452 | reset_afio_registers(); | ||
| 453 | Uart::new_with_rtscts( | ||
| 454 | p.USART3.reborrow(), | ||
| 455 | p.PC11.reborrow(), | ||
| 456 | p.PC10.reborrow(), | ||
| 457 | Irqs, | ||
| 458 | p.PB14.reborrow(), | ||
| 459 | p.PB13.reborrow(), | ||
| 460 | p.DMA1_CH2.reborrow(), | ||
| 461 | p.DMA1_CH3.reborrow(), | ||
| 462 | Default::default(), | ||
| 463 | ) | ||
| 464 | .unwrap(); | ||
| 465 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); | ||
| 466 | } | ||
| 467 | { | ||
| 468 | // full remap RX/TX/RTS/CTS | ||
| 469 | reset_afio_registers(); | ||
| 470 | Uart::new_blocking_with_rtscts( | ||
| 471 | p.USART3.reborrow(), | ||
| 472 | p.PD9.reborrow(), | ||
| 473 | p.PD8.reborrow(), | ||
| 474 | p.PD12.reborrow(), | ||
| 475 | p.PD11.reborrow(), | ||
| 476 | Default::default(), | ||
| 477 | ) | ||
| 478 | .unwrap(); | ||
| 479 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 480 | } | ||
| 481 | { | ||
| 482 | // full remap RX/TX | ||
| 483 | reset_afio_registers(); | ||
| 484 | Uart::new_blocking( | ||
| 485 | p.USART3.reborrow(), | ||
| 486 | p.PD9.reborrow(), | ||
| 487 | p.PD8.reborrow(), | ||
| 488 | Default::default(), | ||
| 489 | ) | ||
| 490 | .unwrap(); | ||
| 491 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 492 | } | ||
| 493 | { | ||
| 494 | // full remap TX | ||
| 495 | reset_afio_registers(); | ||
| 496 | Uart::new_blocking_half_duplex( | ||
| 497 | p.USART3.reborrow(), | ||
| 498 | p.PD8.reborrow(), | ||
| 499 | Default::default(), | ||
| 500 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 501 | ) | ||
| 502 | .unwrap(); | ||
| 503 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 504 | } | ||
| 505 | { | ||
| 506 | // full remap TX async | ||
| 507 | reset_afio_registers(); | ||
| 508 | UartTx::new( | ||
| 509 | p.USART3.reborrow(), | ||
| 510 | p.PD8.reborrow(), | ||
| 511 | p.DMA1_CH2.reborrow(), | ||
| 512 | Default::default(), | ||
| 513 | ) | ||
| 514 | .unwrap(); | ||
| 515 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 516 | } | ||
| 517 | { | ||
| 518 | // full remap TX/CTS async | ||
| 519 | reset_afio_registers(); | ||
| 520 | UartTx::new_with_cts( | ||
| 521 | p.USART3.reborrow(), | ||
| 522 | p.PD8.reborrow(), | ||
| 523 | p.PD11.reborrow(), | ||
| 524 | p.DMA1_CH2.reborrow(), | ||
| 525 | Default::default(), | ||
| 526 | ) | ||
| 527 | .unwrap(); | ||
| 528 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 529 | } | ||
| 530 | { | ||
| 531 | // full remap RX async | ||
| 532 | reset_afio_registers(); | ||
| 533 | UartRx::new( | ||
| 534 | p.USART3.reborrow(), | ||
| 535 | Irqs, | ||
| 536 | p.PD9.reborrow(), | ||
| 537 | p.DMA1_CH3.reborrow(), | ||
| 538 | Default::default(), | ||
| 539 | ) | ||
| 540 | .unwrap(); | ||
| 541 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 542 | } | ||
| 543 | { | ||
| 544 | // full remap RX async | ||
| 545 | reset_afio_registers(); | ||
| 546 | UartRx::new_with_rts( | ||
| 547 | p.USART3.reborrow(), | ||
| 548 | Irqs, | ||
| 549 | p.PD9.reborrow(), | ||
| 550 | p.PD12.reborrow(), | ||
| 551 | p.DMA1_CH3.reborrow(), | ||
| 552 | Default::default(), | ||
| 553 | ) | ||
| 554 | .unwrap(); | ||
| 555 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 556 | } | ||
| 557 | { | ||
| 558 | // full remap RX/TX async | ||
| 559 | reset_afio_registers(); | ||
| 560 | Uart::new( | ||
| 561 | p.USART3.reborrow(), | ||
| 562 | p.PD9.reborrow(), | ||
| 563 | p.PD8.reborrow(), | ||
| 564 | Irqs, | ||
| 565 | p.DMA1_CH2.reborrow(), | ||
| 566 | p.DMA1_CH3.reborrow(), | ||
| 567 | Default::default(), | ||
| 568 | ) | ||
| 569 | .unwrap(); | ||
| 570 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 571 | } | ||
| 572 | { | ||
| 573 | // full remap RX/TX/RTS/CTS async | ||
| 574 | reset_afio_registers(); | ||
| 575 | Uart::new_with_rtscts( | ||
| 576 | p.USART3.reborrow(), | ||
| 577 | p.PD9.reborrow(), | ||
| 578 | p.PD8.reborrow(), | ||
| 579 | Irqs, | ||
| 580 | p.PD12.reborrow(), | ||
| 581 | p.PD11.reborrow(), | ||
| 582 | p.DMA1_CH2.reborrow(), | ||
| 583 | p.DMA1_CH3.reborrow(), | ||
| 584 | Default::default(), | ||
| 585 | ) | ||
| 586 | .unwrap(); | ||
| 587 | defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); | ||
| 588 | } | ||
| 589 | |||
| 590 | // SPI3 | ||
| 591 | { | ||
| 592 | // no remap SCK/MISO/MOSI | ||
| 593 | afio_registers_set_remap(); | ||
| 594 | Spi::new_blocking( | ||
| 595 | p.SPI3.reborrow(), | ||
| 596 | p.PB3.reborrow(), | ||
| 597 | p.PB5.reborrow(), | ||
| 598 | p.PB4.reborrow(), | ||
| 599 | Default::default(), | ||
| 600 | ); | ||
| 601 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); | ||
| 602 | } | ||
| 603 | { | ||
| 604 | // no remap SCK/MOSI | ||
| 605 | afio_registers_set_remap(); | ||
| 606 | Spi::new_blocking_txonly( | ||
| 607 | p.SPI3.reborrow(), | ||
| 608 | p.PB3.reborrow(), | ||
| 609 | p.PB5.reborrow(), | ||
| 610 | Default::default(), | ||
| 611 | ); | ||
| 612 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); | ||
| 613 | } | ||
| 614 | { | ||
| 615 | // no remap MOSI | ||
| 616 | afio_registers_set_remap(); | ||
| 617 | Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default()); | ||
| 618 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); | ||
| 619 | } | ||
| 620 | { | ||
| 621 | // no remap SCK/MISO | ||
| 622 | afio_registers_set_remap(); | ||
| 623 | Spi::new_blocking_rxonly( | ||
| 624 | p.SPI3.reborrow(), | ||
| 625 | p.PB3.reborrow(), | ||
| 626 | p.PB4.reborrow(), | ||
| 627 | Default::default(), | ||
| 628 | ); | ||
| 629 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); | ||
| 630 | } | ||
| 631 | { | ||
| 632 | // remap SCK/MISO/MOSI | ||
| 633 | reset_afio_registers(); | ||
| 634 | Spi::new_blocking( | ||
| 635 | p.SPI3.reborrow(), | ||
| 636 | p.PC10.reborrow(), | ||
| 637 | p.PC12.reborrow(), | ||
| 638 | p.PC11.reborrow(), | ||
| 639 | Default::default(), | ||
| 640 | ); | ||
| 641 | |||
| 642 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 643 | } | ||
| 644 | { | ||
| 645 | // remap SCK/MOSI | ||
| 646 | reset_afio_registers(); | ||
| 647 | Spi::new_blocking_txonly( | ||
| 648 | p.SPI3.reborrow(), | ||
| 649 | p.PC10.reborrow(), | ||
| 650 | p.PC12.reborrow(), | ||
| 651 | Default::default(), | ||
| 652 | ); | ||
| 653 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 654 | } | ||
| 655 | { | ||
| 656 | // remap MOSI | ||
| 657 | reset_afio_registers(); | ||
| 658 | Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default()); | ||
| 659 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 660 | } | ||
| 661 | { | ||
| 662 | // remap SCK/MISO | ||
| 663 | reset_afio_registers(); | ||
| 664 | Spi::new_blocking_rxonly( | ||
| 665 | p.SPI3.reborrow(), | ||
| 666 | p.PC10.reborrow(), | ||
| 667 | p.PC11.reborrow(), | ||
| 668 | Default::default(), | ||
| 669 | ); | ||
| 670 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 671 | } | ||
| 672 | |||
| 673 | // I2S3 | ||
| 674 | { | ||
| 675 | // no remap SD/WS/CK/MCK | ||
| 676 | afio_registers_set_remap(); | ||
| 677 | I2S::new_txonly( | ||
| 678 | p.SPI3.reborrow(), | ||
| 679 | p.PB5.reborrow(), | ||
| 680 | p.PA15.reborrow(), | ||
| 681 | p.PB3.reborrow(), | ||
| 682 | p.PC7.reborrow(), | ||
| 683 | p.DMA2_CH2.reborrow(), | ||
| 684 | &mut [0u16; 0], | ||
| 685 | Default::default(), | ||
| 686 | ); | ||
| 687 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); | ||
| 688 | } | ||
| 689 | { | ||
| 690 | // no remap SD/WS/CK | ||
| 691 | afio_registers_set_remap(); | ||
| 692 | I2S::new_txonly_nomck( | ||
| 693 | p.SPI3.reborrow(), | ||
| 694 | p.PB5.reborrow(), | ||
| 695 | p.PA15.reborrow(), | ||
| 696 | p.PB3.reborrow(), | ||
| 697 | p.DMA2_CH2.reborrow(), | ||
| 698 | &mut [0u16; 0], | ||
| 699 | Default::default(), | ||
| 700 | ); | ||
| 701 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); | ||
| 702 | } | ||
| 703 | { | ||
| 704 | // no remap SD/WS/CK/MCK | ||
| 705 | afio_registers_set_remap(); | ||
| 706 | I2S::new_rxonly( | ||
| 707 | p.SPI3.reborrow(), | ||
| 708 | p.PB4.reborrow(), | ||
| 709 | p.PA15.reborrow(), | ||
| 710 | p.PB3.reborrow(), | ||
| 711 | p.PC7.reborrow(), | ||
| 712 | p.DMA2_CH1.reborrow(), | ||
| 713 | &mut [0u16; 0], | ||
| 714 | Default::default(), | ||
| 715 | ); | ||
| 716 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 717 | } | ||
| 718 | { | ||
| 719 | // remap SD/WS/CK/MCK | ||
| 720 | reset_afio_registers(); | ||
| 721 | I2S::new_txonly( | ||
| 722 | p.SPI3.reborrow(), | ||
| 723 | p.PC12.reborrow(), | ||
| 724 | p.PA4.reborrow(), | ||
| 725 | p.PC10.reborrow(), | ||
| 726 | p.PC7.reborrow(), | ||
| 727 | p.DMA2_CH2.reborrow(), | ||
| 728 | &mut [0u16; 0], | ||
| 729 | Default::default(), | ||
| 730 | ); | ||
| 731 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 732 | } | ||
| 733 | { | ||
| 734 | // remap SD/WS/CK | ||
| 735 | reset_afio_registers(); | ||
| 736 | I2S::new_txonly_nomck( | ||
| 737 | p.SPI3.reborrow(), | ||
| 738 | p.PC12.reborrow(), | ||
| 739 | p.PA4.reborrow(), | ||
| 740 | p.PC10.reborrow(), | ||
| 741 | p.DMA2_CH2.reborrow(), | ||
| 742 | &mut [0u16; 0], | ||
| 743 | Default::default(), | ||
| 744 | ); | ||
| 745 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 746 | } | ||
| 747 | { | ||
| 748 | // remap SD/WS/CK/MCK | ||
| 749 | reset_afio_registers(); | ||
| 750 | I2S::new_rxonly( | ||
| 751 | p.SPI3.reborrow(), | ||
| 752 | p.PC11.reborrow(), | ||
| 753 | p.PA4.reborrow(), | ||
| 754 | p.PC10.reborrow(), | ||
| 755 | p.PC7.reborrow(), | ||
| 756 | p.DMA2_CH1.reborrow(), | ||
| 757 | &mut [0u16; 0], | ||
| 758 | Default::default(), | ||
| 759 | ); | ||
| 760 | defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); | ||
| 761 | } | ||
| 762 | |||
| 763 | // CAN2 | ||
| 764 | { | ||
| 765 | // no remap | ||
| 766 | afio_registers_set_remap(); | ||
| 767 | Can::new(p.CAN2.reborrow(), p.PB12.reborrow(), p.PB13.reborrow(), Irqs); | ||
| 768 | defmt::assert_eq!(AFIO.mapr().read().can2_remap(), false); | ||
| 769 | } | ||
| 770 | { | ||
| 771 | // remap | ||
| 772 | reset_afio_registers(); | ||
| 773 | Can::new(p.CAN2.reborrow(), p.PB5.reborrow(), p.PB6.reborrow(), Irqs); | ||
| 774 | defmt::assert_eq!(AFIO.mapr().read().can2_remap(), true); | ||
| 775 | } | ||
| 776 | |||
| 777 | // Ethernet | ||
| 778 | { | ||
| 779 | // no remap RMII | ||
| 780 | afio_registers_set_remap(); | ||
| 781 | Ethernet::new( | ||
| 782 | &mut PacketQueue::<1, 1>::new(), | ||
| 783 | p.ETH.reborrow(), | ||
| 784 | Irqs, | ||
| 785 | p.PA1.reborrow(), | ||
| 786 | p.PA2.reborrow(), | ||
| 787 | p.PC1.reborrow(), | ||
| 788 | p.PA7.reborrow(), | ||
| 789 | p.PC4.reborrow(), | ||
| 790 | p.PC5.reborrow(), | ||
| 791 | p.PB12.reborrow(), | ||
| 792 | p.PB13.reborrow(), | ||
| 793 | p.PB11.reborrow(), | ||
| 794 | GenericPhy::new_auto(), | ||
| 795 | Default::default(), | ||
| 796 | ); | ||
| 797 | defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false); | ||
| 798 | } | ||
| 799 | { | ||
| 800 | // no remap MII | ||
| 801 | afio_registers_set_remap(); | ||
| 802 | Ethernet::new_mii( | ||
| 803 | &mut PacketQueue::<1, 1>::new(), | ||
| 804 | p.ETH.reborrow(), | ||
| 805 | Irqs, | ||
| 806 | p.PA1.reborrow(), | ||
| 807 | p.PC3.reborrow(), | ||
| 808 | p.PA2.reborrow(), | ||
| 809 | p.PC1.reborrow(), | ||
| 810 | p.PA7.reborrow(), | ||
| 811 | p.PC4.reborrow(), | ||
| 812 | p.PC5.reborrow(), | ||
| 813 | p.PB0.reborrow(), | ||
| 814 | p.PB1.reborrow(), | ||
| 815 | p.PB12.reborrow(), | ||
| 816 | p.PB13.reborrow(), | ||
| 817 | p.PC2.reborrow(), | ||
| 818 | p.PB8.reborrow(), | ||
| 819 | p.PB11.reborrow(), | ||
| 820 | GenericPhy::new_auto(), | ||
| 821 | Default::default(), | ||
| 822 | ); | ||
| 823 | defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false); | ||
| 824 | } | ||
| 825 | { | ||
| 826 | // remap RMII | ||
| 827 | reset_afio_registers(); | ||
| 828 | Ethernet::new( | ||
| 829 | &mut PacketQueue::<1, 1>::new(), | ||
| 830 | p.ETH.reborrow(), | ||
| 831 | Irqs, | ||
| 832 | p.PA1.reborrow(), | ||
| 833 | p.PA2.reborrow(), | ||
| 834 | p.PC1.reborrow(), | ||
| 835 | p.PD8.reborrow(), | ||
| 836 | p.PD9.reborrow(), | ||
| 837 | p.PD10.reborrow(), | ||
| 838 | p.PB12.reborrow(), | ||
| 839 | p.PB13.reborrow(), | ||
| 840 | p.PB11.reborrow(), | ||
| 841 | GenericPhy::new_auto(), | ||
| 842 | Default::default(), | ||
| 843 | ); | ||
| 844 | defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true); | ||
| 845 | } | ||
| 846 | { | ||
| 847 | // remap MII | ||
| 848 | reset_afio_registers(); | ||
| 849 | Ethernet::new_mii( | ||
| 850 | &mut PacketQueue::<1, 1>::new(), | ||
| 851 | p.ETH.reborrow(), | ||
| 852 | Irqs, | ||
| 853 | p.PA1.reborrow(), | ||
| 854 | p.PC3.reborrow(), | ||
| 855 | p.PA2.reborrow(), | ||
| 856 | p.PC1.reborrow(), | ||
| 857 | p.PD8.reborrow(), | ||
| 858 | p.PD9.reborrow(), | ||
| 859 | p.PD10.reborrow(), | ||
| 860 | p.PD11.reborrow(), | ||
| 861 | p.PD12.reborrow(), | ||
| 862 | p.PB12.reborrow(), | ||
| 863 | p.PB13.reborrow(), | ||
| 864 | p.PC2.reborrow(), | ||
| 865 | p.PB8.reborrow(), | ||
| 866 | p.PB11.reborrow(), | ||
| 867 | GenericPhy::new_auto(), | ||
| 868 | Default::default(), | ||
| 869 | ); | ||
| 870 | defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true); | ||
| 871 | } | ||
| 872 | |||
| 873 | // CAN1 | ||
| 874 | { | ||
| 875 | // no remap | ||
| 876 | afio_registers_set_remap(); | ||
| 877 | Can::new(p.CAN1.reborrow(), p.PA11.reborrow(), p.PA12.reborrow(), Irqs); | ||
| 878 | defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 0); | ||
| 879 | } | ||
| 880 | { | ||
| 881 | // partial remap | ||
| 882 | reset_afio_registers(); | ||
| 883 | Can::new(p.CAN1.reborrow(), p.PB8.reborrow(), p.PB9.reborrow(), Irqs); | ||
| 884 | defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 2); | ||
| 885 | } | ||
| 886 | { | ||
| 887 | // full remap | ||
| 888 | reset_afio_registers(); | ||
| 889 | Can::new(p.CAN1.reborrow(), p.PD0.reborrow(), p.PD1.reborrow(), Irqs); | ||
| 890 | defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 3); | ||
| 891 | } | ||
| 892 | |||
| 893 | // USART2 | ||
| 894 | { | ||
| 895 | // no remap RX/TX/RTS/CTS | ||
| 896 | afio_registers_set_remap(); | ||
| 897 | Uart::new_blocking_with_rtscts( | ||
| 898 | p.USART2.reborrow(), | ||
| 899 | p.PA3.reborrow(), | ||
| 900 | p.PA2.reborrow(), | ||
| 901 | p.PA1.reborrow(), | ||
| 902 | p.PA0.reborrow(), | ||
| 903 | Default::default(), | ||
| 904 | ) | ||
| 905 | .unwrap(); | ||
| 906 | defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); | ||
| 907 | } | ||
| 908 | { | ||
| 909 | // no remap RX/TX | ||
| 910 | afio_registers_set_remap(); | ||
| 911 | Uart::new_blocking( | ||
| 912 | p.USART2.reborrow(), | ||
| 913 | p.PA3.reborrow(), | ||
| 914 | p.PA2.reborrow(), | ||
| 915 | Default::default(), | ||
| 916 | ) | ||
| 917 | .unwrap(); | ||
| 918 | defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); | ||
| 919 | } | ||
| 920 | { | ||
| 921 | // no remap TX | ||
| 922 | afio_registers_set_remap(); | ||
| 923 | Uart::new_blocking_half_duplex( | ||
| 924 | p.USART2.reborrow(), | ||
| 925 | p.PA2.reborrow(), | ||
| 926 | Default::default(), | ||
| 927 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 928 | ) | ||
| 929 | .unwrap(); | ||
| 930 | defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); | ||
| 931 | } | ||
| 932 | { | ||
| 933 | // full remap RX/TX/RTS/CTS | ||
| 934 | reset_afio_registers(); | ||
| 935 | Uart::new_blocking_with_rtscts( | ||
| 936 | p.USART2.reborrow(), | ||
| 937 | p.PD6.reborrow(), | ||
| 938 | p.PD5.reborrow(), | ||
| 939 | p.PD4.reborrow(), | ||
| 940 | p.PD3.reborrow(), | ||
| 941 | Default::default(), | ||
| 942 | ) | ||
| 943 | .unwrap(); | ||
| 944 | defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); | ||
| 945 | } | ||
| 946 | { | ||
| 947 | // full remap RX/TX | ||
| 948 | reset_afio_registers(); | ||
| 949 | Uart::new_blocking( | ||
| 950 | p.USART2.reborrow(), | ||
| 951 | p.PD6.reborrow(), | ||
| 952 | p.PD5.reborrow(), | ||
| 953 | Default::default(), | ||
| 954 | ) | ||
| 955 | .unwrap(); | ||
| 956 | defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); | ||
| 957 | } | ||
| 958 | { | ||
| 959 | // full remap TX | ||
| 960 | reset_afio_registers(); | ||
| 961 | Uart::new_blocking_half_duplex( | ||
| 962 | p.USART2.reborrow(), | ||
| 963 | p.PD5.reborrow(), | ||
| 964 | Default::default(), | ||
| 965 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 966 | ) | ||
| 967 | .unwrap(); | ||
| 968 | defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), true); | ||
| 969 | } | ||
| 970 | |||
| 971 | // USART1 | ||
| 972 | { | ||
| 973 | // no remap RX/TX/RTS/CTS | ||
| 974 | afio_registers_set_remap(); | ||
| 975 | Uart::new_blocking_with_rtscts( | ||
| 976 | p.USART1.reborrow(), | ||
| 977 | p.PA10.reborrow(), | ||
| 978 | p.PA9.reborrow(), | ||
| 979 | p.PA12.reborrow(), | ||
| 980 | p.PA11.reborrow(), | ||
| 981 | Default::default(), | ||
| 982 | ) | ||
| 983 | .unwrap(); | ||
| 984 | defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false); | ||
| 985 | } | ||
| 986 | { | ||
| 987 | // no remap RX/TX | ||
| 988 | afio_registers_set_remap(); | ||
| 989 | Uart::new_blocking( | ||
| 990 | p.USART1.reborrow(), | ||
| 991 | p.PA10.reborrow(), | ||
| 992 | p.PA9.reborrow(), | ||
| 993 | Default::default(), | ||
| 994 | ) | ||
| 995 | .unwrap(); | ||
| 996 | defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false); | ||
| 997 | } | ||
| 998 | { | ||
| 999 | // no remap TX | ||
| 1000 | afio_registers_set_remap(); | ||
| 1001 | Uart::new_blocking_half_duplex( | ||
| 1002 | p.USART1.reborrow(), | ||
| 1003 | p.PA9.reborrow(), | ||
| 1004 | Default::default(), | ||
| 1005 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 1006 | ) | ||
| 1007 | .unwrap(); | ||
| 1008 | defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false); | ||
| 1009 | } | ||
| 1010 | { | ||
| 1011 | // remap RX/TX/RTS/CTS | ||
| 1012 | reset_afio_registers(); | ||
| 1013 | Uart::new_blocking_with_rtscts( | ||
| 1014 | p.USART1.reborrow(), | ||
| 1015 | p.PB7.reborrow(), | ||
| 1016 | p.PB6.reborrow(), | ||
| 1017 | p.PA12.reborrow(), | ||
| 1018 | p.PA11.reborrow(), | ||
| 1019 | Default::default(), | ||
| 1020 | ) | ||
| 1021 | .unwrap(); | ||
| 1022 | defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true); | ||
| 1023 | } | ||
| 1024 | { | ||
| 1025 | // remap RX/TX | ||
| 1026 | reset_afio_registers(); | ||
| 1027 | Uart::new_blocking( | ||
| 1028 | p.USART1.reborrow(), | ||
| 1029 | p.PB7.reborrow(), | ||
| 1030 | p.PB6.reborrow(), | ||
| 1031 | Default::default(), | ||
| 1032 | ) | ||
| 1033 | .unwrap(); | ||
| 1034 | defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true); | ||
| 1035 | } | ||
| 1036 | { | ||
| 1037 | // remap TX | ||
| 1038 | reset_afio_registers(); | ||
| 1039 | Uart::new_blocking_half_duplex( | ||
| 1040 | p.USART1.reborrow(), | ||
| 1041 | p.PB6.reborrow(), | ||
| 1042 | Default::default(), | ||
| 1043 | embassy_stm32::usart::HalfDuplexReadback::NoReadback, | ||
| 1044 | ) | ||
| 1045 | .unwrap(); | ||
| 1046 | defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true); | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | // TIM1 | ||
| 1050 | { | ||
| 1051 | // full remap | ||
| 1052 | reset_afio_registers(); | ||
| 1053 | SimplePwm::new( | ||
| 1054 | p.TIM1.reborrow(), | ||
| 1055 | Some(PwmPin::new(p.PE9.reborrow(), OutputType::PushPull)), | ||
| 1056 | Some(PwmPin::new(p.PE11.reborrow(), OutputType::PushPull)), | ||
| 1057 | None, | ||
| 1058 | None, | ||
| 1059 | khz(10), | ||
| 1060 | Default::default(), | ||
| 1061 | ); | ||
| 1062 | defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 3); | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | #[cfg(feature = "afio-value-line")] | ||
| 1068 | mod value_line { | ||
| 1069 | use super::*; | ||
| 1070 | |||
| 1071 | pub fn run(p: &mut Peripherals) { | ||
| 1072 | // TIM13 | ||
| 1073 | { | ||
| 1074 | // no remap | ||
| 1075 | reset_afio_registers(); | ||
| 1076 | SimplePwm::new( | ||
| 1077 | p.TIM13.reborrow(), | ||
| 1078 | Some(PwmPin::new(p.PC8.reborrow(), OutputType::PushPull)), | ||
| 1079 | None, | ||
| 1080 | None, | ||
| 1081 | None, | ||
| 1082 | khz(10), | ||
| 1083 | Default::default(), | ||
| 1084 | ); | ||
| 1085 | defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), false); | ||
| 1086 | } | ||
| 1087 | { | ||
| 1088 | // remap | ||
| 1089 | reset_afio_registers(); | ||
| 1090 | SimplePwm::new( | ||
| 1091 | p.TIM13.reborrow(), | ||
| 1092 | Some(PwmPin::new(p.PB0.reborrow(), OutputType::PushPull)), | ||
| 1093 | None, | ||
| 1094 | None, | ||
| 1095 | None, | ||
| 1096 | khz(10), | ||
| 1097 | Default::default(), | ||
| 1098 | ); | ||
| 1099 | defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), true); | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | #[cfg(not(feature = "afio-connectivity-line"))] | ||
| 1105 | mod connectivity_line { | ||
| 1106 | use super::*; | ||
| 1107 | |||
| 1108 | pub fn run(_: &mut Peripherals) {} | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | #[cfg(not(feature = "afio-value-line"))] | ||
| 1112 | mod value_line { | ||
| 1113 | use super::*; | ||
| 1114 | |||
| 1115 | pub fn run(_: &mut Peripherals) {} | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | fn reset_afio_registers() { | ||
| 1119 | set_afio_registers(false, 0); | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | fn afio_registers_set_remap() { | ||
| 1123 | set_afio_registers(true, 1); | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | fn set_afio_registers(bool_val: bool, num_val: u8) { | ||
| 1127 | AFIO.mapr().modify(|w| { | ||
| 1128 | w.set_swj_cfg(embassy_stm32::pac::afio::vals::SwjCfg::NO_OP); | ||
| 1129 | w.set_can1_remap(num_val); | ||
| 1130 | w.set_can2_remap(bool_val); | ||
| 1131 | w.set_eth_remap(bool_val); | ||
| 1132 | w.set_i2c1_remap(bool_val); | ||
| 1133 | w.set_spi1_remap(bool_val); | ||
| 1134 | w.set_spi3_remap(bool_val); | ||
| 1135 | w.set_tim1_remap(num_val); | ||
| 1136 | w.set_tim2_remap(num_val); | ||
| 1137 | w.set_tim3_remap(num_val); | ||
| 1138 | w.set_tim4_remap(bool_val); | ||
| 1139 | w.set_usart1_remap(bool_val); | ||
| 1140 | w.set_usart2_remap(bool_val); | ||
| 1141 | w.set_usart3_remap(num_val); | ||
| 1142 | }); | ||
| 1143 | |||
| 1144 | AFIO.mapr2().modify(|w| { | ||
| 1145 | w.set_cec_remap(bool_val); | ||
| 1146 | w.set_tim9_remap(bool_val); | ||
| 1147 | w.set_tim10_remap(bool_val); | ||
| 1148 | w.set_tim11_remap(bool_val); | ||
| 1149 | w.set_tim12_remap(bool_val); | ||
| 1150 | w.set_tim13_remap(bool_val); | ||
| 1151 | w.set_tim14_remap(bool_val); | ||
| 1152 | w.set_tim15_remap(bool_val); | ||
| 1153 | w.set_tim16_remap(bool_val); | ||
| 1154 | w.set_tim17_remap(bool_val); | ||
| 1155 | }); | ||
| 1156 | } | ||
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index cb63b3374..f800769ab 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs | |||
| @@ -103,7 +103,7 @@ define_peris!( | |||
| 103 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, | 103 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, |
| 104 | @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, | 104 | @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;}, |
| 105 | ); | 105 | ); |
| 106 | #[cfg(feature = "stm32f103c8")] | 106 | #[cfg(any(feature = "stm32f100rd", feature = "stm32f103c8", feature = "stm32f107vc"))] |
| 107 | define_peris!( | 107 | define_peris!( |
| 108 | UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, | 108 | UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, |
| 109 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, | 109 | SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, |
